Merge branch 'master' of https://github.com/pulumi/pulumi into evan/auto
This commit is contained in:
commit
c360e4d3f1
12
.github/workflows/codegen-test.yml
vendored
12
.github/workflows/codegen-test.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
|||
- name: Install Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.6.10
|
||||
python-version: 3.6.11
|
||||
- name: Install Pulumi CLI
|
||||
uses: pulumi/action-install-pulumi-cli@releases/v1
|
||||
|
||||
|
@ -54,7 +54,7 @@ jobs:
|
|||
- name: Install Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.6.10
|
||||
python-version: 3.6.11
|
||||
- name: Install Pulumi CLI
|
||||
uses: pulumi/action-install-pulumi-cli@releases/v1
|
||||
|
||||
|
@ -87,7 +87,7 @@ jobs:
|
|||
- name: Install Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.6.10
|
||||
python-version: 3.6.11
|
||||
- name: Install Pulumi CLI
|
||||
uses: pulumi/action-install-pulumi-cli@releases/v1
|
||||
|
||||
|
@ -120,7 +120,7 @@ jobs:
|
|||
- name: Install Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.6.10
|
||||
python-version: 3.6.11
|
||||
- name: Install Pulumi CLI
|
||||
uses: pulumi/action-install-pulumi-cli@releases/v1
|
||||
|
||||
|
@ -153,7 +153,7 @@ jobs:
|
|||
- name: Install Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.6.10
|
||||
python-version: 3.6.11
|
||||
- name: Install Pulumi CLI
|
||||
uses: pulumi/action-install-pulumi-cli@releases/v1
|
||||
|
||||
|
@ -186,7 +186,7 @@ jobs:
|
|||
- name: Install Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.6.10
|
||||
python-version: 3.6.11
|
||||
- name: Install Pulumi CLI
|
||||
uses: pulumi/action-install-pulumi-cli@releases/v1
|
||||
|
||||
|
|
2
.github/workflows/windows-build.yml
vendored
2
.github/workflows/windows-build.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
go-version: [1.14.x]
|
||||
node-version: [10.x]
|
||||
python-version: [3.7]
|
||||
dotnet: ['3.1.100']
|
||||
dotnet: ['3.1.301']
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}
|
||||
|
|
2
.github/workflows/windows-pr.yml
vendored
2
.github/workflows/windows-pr.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
go-version: [1.14.x]
|
||||
node-version: [10.x]
|
||||
python-version: [3.7]
|
||||
dotnet: ['3.1.100']
|
||||
dotnet: ['3.1.301']
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}
|
||||
|
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -3,12 +3,39 @@ CHANGELOG
|
|||
|
||||
## HEAD (Unreleased)
|
||||
|
||||
- Add pluginDownloadURL field to package definition
|
||||
[#4947](https://github.com/pulumi/pulumi/pull/4947)
|
||||
- Add support for streamInvoke during update
|
||||
[#4990](https://github.com/pulumi/pulumi/pull/4990)
|
||||
|
||||
- Add ability to copy configuration values between stacks
|
||||
[#4971](https://github.com/pulumi/pulumi/pull/4971)
|
||||
|
||||
- Add logic to parse pulumi venv on github action
|
||||
[#4994](https://github.com/pulumi/pulumi/pull/4994)
|
||||
|
||||
- Better performance for stacks with many resources using the .NET SDK
|
||||
[#5015](https://github.com/pulumi/pulumi/pull/5015)
|
||||
|
||||
- Output PDB files and enable SourceLink integration for .NET assemblies
|
||||
[#4967](https://github.com/pulumi/pulumi/pull/4967)
|
||||
|
||||
## 2.6.1 (2020-07-09)
|
||||
|
||||
- Fix a panic in the display during CLI operations
|
||||
[#4987](https://github.com/pulumi/pulumi/pull/4987)
|
||||
|
||||
## 2.6.0 (2020-07-08)
|
||||
|
||||
- Go program gen: Improved handling for pulumi.Map types
|
||||
[#491](https://github.com/pulumi/pulumi/pull/4914)
|
||||
[#4914](https://github.com/pulumi/pulumi/pull/4914)
|
||||
|
||||
- Go SDK: Input type interfaces should declare pointer type impls where appropriate
|
||||
[#4911](https://github.com/pulumi/pulumi/pull/4911)
|
||||
|
||||
- Fixes issue where base64-encoded GOOGLE_CREDENTIALS causes problems with other commands
|
||||
[#4972](https://github.com/pulumi/pulumi/pull/4972)
|
||||
|
||||
## 2.5.0 (2020-06-25)
|
||||
|
||||
- Go program gen: prompt array conversion, unused range vars, id handling
|
||||
|
@ -379,7 +406,7 @@ CHANGELOG
|
|||
[3938](https://github.com/pulumi/pulumi/pull/3938)
|
||||
|
||||
- Add support for transformations in the Go SDK.
|
||||
[3978](https://github.com/pulumi/pulumi/pull/3938)
|
||||
[3978](https://github.com/pulumi/pulumi/pull/3938)
|
||||
|
||||
## 1.11.0 (2020-02-19)
|
||||
- Allow oversize protocol buffers for Python SDK.
|
||||
|
|
15
dist/actions/entrypoint.sh
vendored
15
dist/actions/entrypoint.sh
vendored
|
@ -79,9 +79,11 @@ fi
|
|||
# For Google, we need to authenticate with a service principal for certain authentication operations.
|
||||
if [ ! -z "$GOOGLE_CREDENTIALS" ]; then
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="$(mktemp).json"
|
||||
# Check if GOOGLE_CREDENTIALS is base64 encoded
|
||||
# Check if GOOGLE_CREDENTIALS is base64 encoded
|
||||
if [[ $GOOGLE_CREDENTIALS =~ ^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$ ]]; then
|
||||
echo "$GOOGLE_CREDENTIALS"|base64 -d > $GOOGLE_APPLICATION_CREDENTIALS
|
||||
# unset for other gcloud commands using this variable.
|
||||
unset GOOGLE_CREDENTIALS
|
||||
else
|
||||
echo "$GOOGLE_CREDENTIALS" > $GOOGLE_APPLICATION_CREDENTIALS
|
||||
fi
|
||||
|
@ -116,7 +118,16 @@ fi
|
|||
|
||||
# If the user is running the Python SDK, we will need to install their requirements as well.
|
||||
if [ -e requirements.txt ]; then
|
||||
pip3 install -r requirements.txt
|
||||
# Check if should use venv
|
||||
PULUMI_VENV=$(cat Pulumi.yaml | grep "virtualenv:" | cut -d':' -f2)
|
||||
if [ -z $PULUMI_VENV ]; then
|
||||
python3 -m venv $PULUMI_VENV
|
||||
source $PULUMI_VENV/bin/activate
|
||||
pip3 install -r requirements.txt
|
||||
deactivate
|
||||
else
|
||||
pip3 install -r requirements.txt
|
||||
fi
|
||||
fi
|
||||
|
||||
# Now just pass along all arguments to the Pulumi CLI, sending the output to a file for
|
||||
|
|
|
@ -68,7 +68,7 @@ func ShowDiffEvents(op string, action apitype.UpdateKind,
|
|||
|
||||
out := os.Stdout
|
||||
if event.Type == engine.DiagEvent {
|
||||
payload := event.Payload.(engine.DiagEventPayload)
|
||||
payload := event.Payload().(engine.DiagEventPayload)
|
||||
if payload.Severity == diag.Error || payload.Severity == diag.Warning {
|
||||
out = os.Stderr
|
||||
}
|
||||
|
@ -96,25 +96,26 @@ func RenderDiffEvent(action apitype.UpdateKind, event engine.Event,
|
|||
// Currently, prelude, summary, and stdout events are printed the same for both the diff and
|
||||
// progress displays.
|
||||
case engine.PreludeEvent:
|
||||
return renderPreludeEvent(event.Payload.(engine.PreludeEventPayload), opts)
|
||||
return renderPreludeEvent(event.Payload().(engine.PreludeEventPayload), opts)
|
||||
case engine.SummaryEvent:
|
||||
return renderSummaryEvent(action, event.Payload.(engine.SummaryEventPayload), false /* wroteDiagnosticHeader */, opts)
|
||||
const wroteDiagnosticHeader = false
|
||||
return renderSummaryEvent(action, event.Payload().(engine.SummaryEventPayload), wroteDiagnosticHeader, opts)
|
||||
case engine.StdoutColorEvent:
|
||||
return renderStdoutColorEvent(event.Payload.(engine.StdoutEventPayload), opts)
|
||||
return renderStdoutColorEvent(event.Payload().(engine.StdoutEventPayload), opts)
|
||||
|
||||
// Resource operations have very specific displays for either diff or progress displays.
|
||||
// These functions should not be directly used by the progress display without validating
|
||||
// that the display is appropriate for both.
|
||||
case engine.ResourceOperationFailed:
|
||||
return renderDiffResourceOperationFailedEvent(event.Payload.(engine.ResourceOperationFailedPayload), opts)
|
||||
return renderDiffResourceOperationFailedEvent(event.Payload().(engine.ResourceOperationFailedPayload), opts)
|
||||
case engine.ResourceOutputsEvent:
|
||||
return renderDiffResourceOutputsEvent(event.Payload.(engine.ResourceOutputsEventPayload), seen, opts)
|
||||
return renderDiffResourceOutputsEvent(event.Payload().(engine.ResourceOutputsEventPayload), seen, opts)
|
||||
case engine.ResourcePreEvent:
|
||||
return renderDiffResourcePreEvent(event.Payload.(engine.ResourcePreEventPayload), seen, opts)
|
||||
return renderDiffResourcePreEvent(event.Payload().(engine.ResourcePreEventPayload), seen, opts)
|
||||
case engine.DiagEvent:
|
||||
return renderDiffDiagEvent(event.Payload.(engine.DiagEventPayload), opts)
|
||||
return renderDiffDiagEvent(event.Payload().(engine.DiagEventPayload), opts)
|
||||
case engine.PolicyViolationEvent:
|
||||
return renderDiffPolicyViolationEvent(event.Payload.(engine.PolicyViolationEventPayload), opts)
|
||||
return renderDiffPolicyViolationEvent(event.Payload().(engine.PolicyViolationEventPayload), opts)
|
||||
|
||||
default:
|
||||
contract.Failf("unknown event type '%s'", event.Type)
|
||||
|
@ -332,7 +333,7 @@ func renderDiffResourcePreEvent(
|
|||
seen map[resource.URN]engine.StepEventMetadata,
|
||||
opts Options) string {
|
||||
|
||||
seen[payload.Metadata.Res.URN] = payload.Metadata
|
||||
seen[payload.Metadata.URN] = payload.Metadata
|
||||
if payload.Metadata.Op == deploy.OpRefresh || payload.Metadata.Op == deploy.OpImport {
|
||||
return ""
|
||||
}
|
||||
|
@ -360,7 +361,7 @@ func renderDiffResourceOutputsEvent(
|
|||
indent := engine.GetIndent(payload.Metadata, seen)
|
||||
|
||||
refresh := false // are these outputs from a refresh?
|
||||
if m, has := seen[payload.Metadata.Res.URN]; has && m.Op == deploy.OpRefresh {
|
||||
if m, has := seen[payload.Metadata.URN]; has && m.Op == deploy.OpRefresh {
|
||||
refresh = true
|
||||
summary := engine.GetResourcePropertiesSummary(payload.Metadata, indent)
|
||||
fprintIgnoreError(out, opts.Color.Colorize(summary))
|
||||
|
|
|
@ -119,7 +119,7 @@ func (s *nopSpinner) Reset() {
|
|||
|
||||
// isRootStack returns true if the step pertains to the rootmost stack component.
|
||||
func isRootStack(step engine.StepEventMetadata) bool {
|
||||
return isRootURN(step.Res.URN)
|
||||
return isRootURN(step.URN)
|
||||
}
|
||||
|
||||
func isRootURN(urn resource.URN) bool {
|
||||
|
|
|
@ -28,7 +28,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
apiEvent.CancelEvent = &apitype.CancelEvent{}
|
||||
|
||||
case engine.StdoutColorEvent:
|
||||
p, ok := e.Payload.(engine.StdoutEventPayload)
|
||||
p, ok := e.Payload().(engine.StdoutEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.DiagEvent:
|
||||
p, ok := e.Payload.(engine.DiagEventPayload)
|
||||
p, ok := e.Payload().(engine.DiagEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.PolicyViolationEvent:
|
||||
p, ok := e.Payload.(engine.PolicyViolationEventPayload)
|
||||
p, ok := e.Payload().(engine.PolicyViolationEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.PreludeEvent:
|
||||
p, ok := e.Payload.(engine.PreludeEventPayload)
|
||||
p, ok := e.Payload().(engine.PreludeEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.SummaryEvent:
|
||||
p, ok := e.Payload.(engine.SummaryEventPayload)
|
||||
p, ok := e.Payload().(engine.SummaryEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.ResourcePreEvent:
|
||||
p, ok := e.Payload.(engine.ResourcePreEventPayload)
|
||||
p, ok := e.Payload().(engine.ResourcePreEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.ResourceOutputsEvent:
|
||||
p, ok := e.Payload.(engine.ResourceOutputsEventPayload)
|
||||
p, ok := e.Payload().(engine.ResourceOutputsEventPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func ConvertEngineEvent(e engine.Event) (apitype.EngineEvent, error) {
|
|||
}
|
||||
|
||||
case engine.ResourceOperationFailed:
|
||||
p, ok := e.Payload.(engine.ResourceOperationFailedPayload)
|
||||
p, ok := e.Payload().(engine.ResourceOperationFailedPayload)
|
||||
if !ok {
|
||||
return apiEvent, eventTypePayloadMismatch
|
||||
}
|
||||
|
@ -175,8 +175,8 @@ func convertStepEventMetadata(md engine.StepEventMetadata) apitype.StepEventMeta
|
|||
|
||||
return apitype.StepEventMetadata{
|
||||
Op: string(md.Op),
|
||||
URN: string(md.Res.URN),
|
||||
Type: string(md.Res.URN.Type()),
|
||||
URN: string(md.URN),
|
||||
Type: string(md.Type),
|
||||
|
||||
Old: convertStepEventStateMetadata(md.Old),
|
||||
New: convertStepEventStateMetadata(md.New),
|
||||
|
|
|
@ -110,12 +110,12 @@ func ShowJSONEvents(op string, action apitype.UpdateKind, events <-chan engine.E
|
|||
// Events ocurring early:
|
||||
case engine.PreludeEvent:
|
||||
// Capture the config map from the prelude. Note that all secrets will remain blinded for safety.
|
||||
digest.Config = e.Payload.(engine.PreludeEventPayload).Config
|
||||
digest.Config = e.Payload().(engine.PreludeEventPayload).Config
|
||||
|
||||
// Events throughout the execution:
|
||||
case engine.DiagEvent:
|
||||
// Skip any ephemeral or debug messages, and elide all colorization.
|
||||
p := e.Payload.(engine.DiagEventPayload)
|
||||
p := e.Payload().(engine.DiagEventPayload)
|
||||
if !p.Ephemeral && p.Severity != diag.Debug {
|
||||
digest.Diagnostics = append(digest.Diagnostics, previewDiagnostic{
|
||||
URN: p.URN,
|
||||
|
@ -125,7 +125,7 @@ func ShowJSONEvents(op string, action apitype.UpdateKind, events <-chan engine.E
|
|||
}
|
||||
case engine.StdoutColorEvent:
|
||||
// Append stdout events as informational messages, and elide all colorization.
|
||||
p := e.Payload.(engine.StdoutEventPayload)
|
||||
p := e.Payload().(engine.StdoutEventPayload)
|
||||
digest.Diagnostics = append(digest.Diagnostics, previewDiagnostic{
|
||||
Message: colors.Never.Colorize(p.Message),
|
||||
Severity: diag.Info,
|
||||
|
@ -133,7 +133,7 @@ func ShowJSONEvents(op string, action apitype.UpdateKind, events <-chan engine.E
|
|||
case engine.ResourcePreEvent:
|
||||
// Create the detailed metadata for this step and the initial state of its resource. Later,
|
||||
// if new outputs arrive, we'll search for and swap in those new values.
|
||||
if m := e.Payload.(engine.ResourcePreEventPayload).Metadata; shouldShow(m, opts) || isRootStack(m) {
|
||||
if m := e.Payload().(engine.ResourcePreEventPayload).Metadata; shouldShow(m, opts) || isRootStack(m) {
|
||||
var detailedDiff map[string]propertyDiff
|
||||
if m.DetailedDiff != nil {
|
||||
detailedDiff = make(map[string]propertyDiff)
|
||||
|
@ -147,7 +147,7 @@ func ShowJSONEvents(op string, action apitype.UpdateKind, events <-chan engine.E
|
|||
|
||||
step := &previewStep{
|
||||
Op: m.Op,
|
||||
URN: m.Res.URN,
|
||||
URN: m.URN,
|
||||
Provider: m.Provider,
|
||||
DiffReasons: m.Diffs,
|
||||
ReplaceReasons: m.Keys,
|
||||
|
@ -183,7 +183,7 @@ func ShowJSONEvents(op string, action apitype.UpdateKind, events <-chan engine.E
|
|||
// Events ocurring late:
|
||||
case engine.SummaryEvent:
|
||||
// At the end of the preview, a summary event indicates the final conclusions.
|
||||
p := e.Payload.(engine.SummaryEventPayload)
|
||||
p := e.Payload().(engine.SummaryEventPayload)
|
||||
digest.Duration = p.Duration
|
||||
digest.ChangeSummary = p.ResourceChanges
|
||||
digest.MaybeCorrupt = p.MaybeCorrupt
|
||||
|
|
|
@ -183,22 +183,23 @@ func simplifyTypeName(typ tokens.Type) string {
|
|||
// event that has a URN. If this is also a 'step' event, then this will return the step metadata as
|
||||
// well.
|
||||
func getEventUrnAndMetadata(event engine.Event) (resource.URN, *engine.StepEventMetadata) {
|
||||
if event.Type == engine.ResourcePreEvent {
|
||||
payload := event.Payload.(engine.ResourcePreEventPayload)
|
||||
return payload.Metadata.Res.URN, &payload.Metadata
|
||||
} else if event.Type == engine.ResourceOutputsEvent {
|
||||
payload := event.Payload.(engine.ResourceOutputsEventPayload)
|
||||
return payload.Metadata.Res.URN, &payload.Metadata
|
||||
} else if event.Type == engine.ResourceOperationFailed {
|
||||
payload := event.Payload.(engine.ResourceOperationFailedPayload)
|
||||
return payload.Metadata.Res.URN, &payload.Metadata
|
||||
} else if event.Type == engine.DiagEvent {
|
||||
return event.Payload.(engine.DiagEventPayload).URN, nil
|
||||
} else if event.Type == engine.PolicyViolationEvent {
|
||||
return event.Payload.(engine.PolicyViolationEventPayload).ResourceURN, nil
|
||||
switch event.Type {
|
||||
case engine.ResourcePreEvent:
|
||||
payload := event.Payload().(engine.ResourcePreEventPayload)
|
||||
return payload.Metadata.URN, &payload.Metadata
|
||||
case engine.ResourceOutputsEvent:
|
||||
payload := event.Payload().(engine.ResourceOutputsEventPayload)
|
||||
return payload.Metadata.URN, &payload.Metadata
|
||||
case engine.ResourceOperationFailed:
|
||||
payload := event.Payload().(engine.ResourceOperationFailedPayload)
|
||||
return payload.Metadata.URN, &payload.Metadata
|
||||
case engine.DiagEvent:
|
||||
return event.Payload().(engine.DiagEventPayload).URN, nil
|
||||
case engine.PolicyViolationEvent:
|
||||
return event.Payload().(engine.PolicyViolationEventPayload).ResourceURN, nil
|
||||
default:
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Converts the colorization tags in a progress message and then actually writes the progress
|
||||
|
@ -922,7 +923,7 @@ func (display *ProgressDisplay) getRowForURN(urn resource.URN, metadata *engine.
|
|||
}
|
||||
|
||||
// First time we're hearing about this resource. Create an initial nearly-empty status for it.
|
||||
step := engine.StepEventMetadata{Res: &engine.StepEventStateMetadata{URN: urn}, Op: deploy.OpSame}
|
||||
step := engine.StepEventMetadata{URN: urn, Op: deploy.OpSame}
|
||||
if metadata != nil {
|
||||
step = *metadata
|
||||
}
|
||||
|
@ -962,18 +963,15 @@ func (display *ProgressDisplay) processNormalEvent(event engine.Event) {
|
|||
// A prelude event can just be printed out directly to the console.
|
||||
// Note: we should probably make sure we don't get any prelude events
|
||||
// once we start hearing about actual resource events.
|
||||
payload := event.Payload.(engine.PreludeEventPayload)
|
||||
payload := event.Payload().(engine.PreludeEventPayload)
|
||||
preludeEventString := renderPreludeEvent(payload, display.opts)
|
||||
if display.isTerminal {
|
||||
display.processNormalEvent(engine.Event{
|
||||
Type: engine.DiagEvent,
|
||||
Payload: engine.DiagEventPayload{
|
||||
Ephemeral: false,
|
||||
Severity: diag.Info,
|
||||
Color: cmdutil.GetGlobalColorization(),
|
||||
Message: preludeEventString,
|
||||
},
|
||||
})
|
||||
display.processNormalEvent(engine.NewEvent(engine.DiagEvent, engine.DiagEventPayload{
|
||||
Ephemeral: false,
|
||||
Severity: diag.Info,
|
||||
Color: cmdutil.GetGlobalColorization(),
|
||||
Message: preludeEventString,
|
||||
}))
|
||||
} else {
|
||||
display.writeSimpleMessage(preludeEventString)
|
||||
}
|
||||
|
@ -981,16 +979,16 @@ func (display *ProgressDisplay) processNormalEvent(event engine.Event) {
|
|||
case engine.SummaryEvent:
|
||||
// keep track of the summary event so that we can display it after all other
|
||||
// resource-related events we receive.
|
||||
payload := event.Payload.(engine.SummaryEventPayload)
|
||||
payload := event.Payload().(engine.SummaryEventPayload)
|
||||
display.summaryEventPayload = &payload
|
||||
return
|
||||
case engine.DiagEvent:
|
||||
msg := display.renderProgressDiagEvent(event.Payload.(engine.DiagEventPayload), true /*includePrefix:*/)
|
||||
msg := display.renderProgressDiagEvent(event.Payload().(engine.DiagEventPayload), true /*includePrefix:*/)
|
||||
if msg == "" {
|
||||
return
|
||||
}
|
||||
case engine.StdoutColorEvent:
|
||||
display.handleSystemEvent(event.Payload.(engine.StdoutEventPayload))
|
||||
display.handleSystemEvent(event.Payload().(engine.StdoutEventPayload))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1012,15 +1010,12 @@ func (display *ProgressDisplay) processNormalEvent(event engine.Event) {
|
|||
// what's going on, we can show them as ephemeral diagnostic messages that are
|
||||
// associated at the top level with the stack. That way if things are taking a while,
|
||||
// there's insight in the display as to what's going on.
|
||||
display.processNormalEvent(engine.Event{
|
||||
Type: engine.DiagEvent,
|
||||
Payload: engine.DiagEventPayload{
|
||||
Ephemeral: true,
|
||||
Severity: diag.Info,
|
||||
Color: cmdutil.GetGlobalColorization(),
|
||||
Message: fmt.Sprintf("read %v %v", simplifyTypeName(eventUrn.Type()), eventUrn.Name()),
|
||||
},
|
||||
})
|
||||
display.processNormalEvent(engine.NewEvent(engine.DiagEvent, engine.DiagEventPayload{
|
||||
Ephemeral: true,
|
||||
Severity: diag.Info,
|
||||
Color: cmdutil.GetGlobalColorization(),
|
||||
Message: fmt.Sprintf("read %v %v", simplifyTypeName(eventUrn.Type()), eventUrn.Name()),
|
||||
}))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1047,14 +1042,14 @@ func (display *ProgressDisplay) processNormalEvent(event engine.Event) {
|
|||
}
|
||||
|
||||
if event.Type == engine.ResourcePreEvent {
|
||||
step := event.Payload.(engine.ResourcePreEventPayload).Metadata
|
||||
step := event.Payload().(engine.ResourcePreEventPayload).Metadata
|
||||
row.SetStep(step)
|
||||
} else if event.Type == engine.ResourceOutputsEvent {
|
||||
isRefresh := display.getStepOp(row.Step()) == deploy.OpRefresh
|
||||
step := event.Payload.(engine.ResourceOutputsEventPayload).Metadata
|
||||
step := event.Payload().(engine.ResourceOutputsEventPayload).Metadata
|
||||
|
||||
// Is this the stack outputs event? If so, we'll need to print it out at the end of the plan.
|
||||
if step.Res.URN == display.stackUrn {
|
||||
if step.URN == display.stackUrn {
|
||||
display.seenStackOutputs = true
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ func ShowQueryEvents(op string, events <-chan engine.Event,
|
|||
|
||||
out := os.Stdout
|
||||
if event.Type == engine.DiagEvent {
|
||||
payload := event.Payload.(engine.DiagEventPayload)
|
||||
payload := event.Payload().(engine.DiagEventPayload)
|
||||
if payload.Severity == diag.Error || payload.Severity == diag.Warning {
|
||||
out = os.Stderr
|
||||
}
|
||||
|
@ -81,11 +81,11 @@ func renderQueryEvent(event engine.Event, opts Options) string {
|
|||
return ""
|
||||
|
||||
case engine.StdoutColorEvent:
|
||||
return renderStdoutColorEvent(event.Payload.(engine.StdoutEventPayload), opts)
|
||||
return renderStdoutColorEvent(event.Payload().(engine.StdoutEventPayload), opts)
|
||||
|
||||
// Includes stdout of the query process.
|
||||
case engine.DiagEvent:
|
||||
return renderQueryDiagEvent(event.Payload.(engine.DiagEventPayload), opts)
|
||||
return renderQueryDiagEvent(event.Payload().(engine.DiagEventPayload), opts)
|
||||
|
||||
case engine.PreludeEvent, engine.SummaryEvent, engine.ResourceOperationFailed,
|
||||
engine.ResourceOutputsEvent, engine.ResourcePreEvent:
|
||||
|
|
|
@ -180,7 +180,7 @@ func (data *resourceRowData) DiagInfo() *DiagInfo {
|
|||
}
|
||||
|
||||
func (data *resourceRowData) RecordDiagEvent(event engine.Event) {
|
||||
payload := event.Payload.(engine.DiagEventPayload)
|
||||
payload := event.Payload().(engine.DiagEventPayload)
|
||||
data.recordDiagEventPayload(payload)
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ func (data *resourceRowData) PolicyPayloads() []engine.PolicyViolationEventPaylo
|
|||
|
||||
// RecordPolicyViolationEvent records a policy event with the resourceRowData.
|
||||
func (data *resourceRowData) RecordPolicyViolationEvent(event engine.Event) {
|
||||
pePayload := event.Payload.(engine.PolicyViolationEventPayload)
|
||||
pePayload := event.Payload().(engine.PolicyViolationEventPayload)
|
||||
data.policyPayloads = append(data.policyPayloads, pePayload)
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ func (data *resourceRowData) ContainsOutputsStep(op deploy.StepOp) bool {
|
|||
func (data *resourceRowData) ColorizedSuffix() string {
|
||||
if !data.IsDone() && data.display.isTerminal {
|
||||
op := data.display.getStepOp(data.step)
|
||||
if op != deploy.OpSame || isRootURN(data.step.Res.URN) {
|
||||
if op != deploy.OpSame || isRootURN(data.step.URN) {
|
||||
suffixes := data.display.suffixesArray
|
||||
ellipses := suffixes[(data.tick+data.display.currentTick)%len(suffixes)]
|
||||
|
||||
|
@ -286,7 +286,7 @@ func (data *resourceRowData) ColorizedSuffix() string {
|
|||
func (data *resourceRowData) ColorizedColumns() []string {
|
||||
step := data.step
|
||||
|
||||
urn := data.step.Res.URN
|
||||
urn := data.step.URN
|
||||
if urn == "" {
|
||||
// If we don't have a URN yet, mock parent it to the global stack.
|
||||
urn = resource.DefaultRootStackURN(data.display.stack, data.display.proj)
|
||||
|
|
|
@ -52,7 +52,7 @@ func ShowWatchEvents(op string, action apitype.UpdateKind, events <-chan engine.
|
|||
continue
|
||||
case engine.DiagEvent:
|
||||
// Skip any ephemeral or debug messages, and elide all colorization.
|
||||
p := e.Payload.(engine.DiagEventPayload)
|
||||
p := e.Payload().(engine.DiagEventPayload)
|
||||
resourceName := ""
|
||||
if p.URN != "" {
|
||||
resourceName = string(p.URN.Name())
|
||||
|
@ -60,22 +60,22 @@ func ShowWatchEvents(op string, action apitype.UpdateKind, events <-chan engine.
|
|||
PrintfWithWatchPrefix(time.Now(), resourceName,
|
||||
"%s", renderDiffDiagEvent(p, opts))
|
||||
case engine.ResourcePreEvent:
|
||||
p := e.Payload.(engine.ResourcePreEventPayload)
|
||||
p := e.Payload().(engine.ResourcePreEventPayload)
|
||||
if shouldShow(p.Metadata, opts) {
|
||||
PrintfWithWatchPrefix(time.Now(), string(p.Metadata.Res.URN.Name()),
|
||||
"%s %s\n", p.Metadata.Op, p.Metadata.Res.URN.Type())
|
||||
PrintfWithWatchPrefix(time.Now(), string(p.Metadata.URN.Name()),
|
||||
"%s %s\n", p.Metadata.Op, p.Metadata.URN.Type())
|
||||
}
|
||||
case engine.ResourceOutputsEvent:
|
||||
p := e.Payload.(engine.ResourceOutputsEventPayload)
|
||||
p := e.Payload().(engine.ResourceOutputsEventPayload)
|
||||
if shouldShow(p.Metadata, opts) {
|
||||
PrintfWithWatchPrefix(time.Now(), string(p.Metadata.Res.URN.Name()),
|
||||
"done %s %s\n", p.Metadata.Op, p.Metadata.Res.URN.Type())
|
||||
PrintfWithWatchPrefix(time.Now(), string(p.Metadata.URN.Name()),
|
||||
"done %s %s\n", p.Metadata.Op, p.Metadata.URN.Type())
|
||||
}
|
||||
case engine.ResourceOperationFailed:
|
||||
p := e.Payload.(engine.ResourceOperationFailedPayload)
|
||||
p := e.Payload().(engine.ResourceOperationFailedPayload)
|
||||
if shouldShow(p.Metadata, opts) {
|
||||
PrintfWithWatchPrefix(time.Now(), string(p.Metadata.Res.URN.Name()),
|
||||
"failed %s %s\n", p.Metadata.Op, p.Metadata.Res.URN.Type())
|
||||
PrintfWithWatchPrefix(time.Now(), string(p.Metadata.URN.Name()),
|
||||
"failed %s %s\n", p.Metadata.Op, p.Metadata.URN.Type())
|
||||
}
|
||||
default:
|
||||
contract.Failf("unknown event type '%s'", e.Type)
|
||||
|
|
|
@ -301,7 +301,7 @@ func (b *cloudBackend) getTarget(ctx context.Context, stackRef backend.StackRefe
|
|||
}
|
||||
|
||||
func isDebugDiagEvent(e engine.Event) bool {
|
||||
return e.Type == engine.DiagEvent && (e.Payload.(engine.DiagEventPayload)).Severity == diag.Debug
|
||||
return e.Type == engine.DiagEvent && (e.Payload().(engine.DiagEventPayload)).Severity == diag.Debug
|
||||
}
|
||||
|
||||
type engineEventBatch struct {
|
||||
|
|
|
@ -127,7 +127,7 @@ func (sm *SnapshotManager) RegisterResourceOutputs(step deploy.Step) error {
|
|||
// intent to mutate before the mutation occurs.
|
||||
func (sm *SnapshotManager) BeginMutation(step deploy.Step) (engine.SnapshotMutation, error) {
|
||||
contract.Require(step != nil, "step != nil")
|
||||
logging.V(9).Infof("SnapshotManager: Beginning mutation for step `%s` on resource `%s`", step.Op(), step.Res().URN)
|
||||
logging.V(9).Infof("SnapshotManager: Beginning mutation for step `%s` on resource `%s`", step.Op(), step.URN())
|
||||
|
||||
switch step.Op() {
|
||||
case deploy.OpSame:
|
||||
|
@ -280,7 +280,7 @@ func (ssm *sameSnapshotMutation) End(step deploy.Step, successful bool) error {
|
|||
}
|
||||
|
||||
func (sm *SnapshotManager) doCreate(step deploy.Step) (engine.SnapshotMutation, error) {
|
||||
logging.V(9).Infof("SnapshotManager.doCreate(%s)", step.Res().URN)
|
||||
logging.V(9).Infof("SnapshotManager.doCreate(%s)", step.URN())
|
||||
err := sm.mutate(func() bool {
|
||||
sm.markOperationPending(step.New(), resource.OperationTypeCreating)
|
||||
return true
|
||||
|
@ -324,7 +324,7 @@ func (csm *createSnapshotMutation) End(step deploy.Step, successful bool) error
|
|||
}
|
||||
|
||||
func (sm *SnapshotManager) doUpdate(step deploy.Step) (engine.SnapshotMutation, error) {
|
||||
logging.V(9).Infof("SnapshotManager.doUpdate(%s)", step.Res().URN)
|
||||
logging.V(9).Infof("SnapshotManager.doUpdate(%s)", step.URN())
|
||||
err := sm.mutate(func() bool {
|
||||
sm.markOperationPending(step.New(), resource.OperationTypeUpdating)
|
||||
return true
|
||||
|
@ -354,7 +354,7 @@ func (usm *updateSnapshotMutation) End(step deploy.Step, successful bool) error
|
|||
}
|
||||
|
||||
func (sm *SnapshotManager) doDelete(step deploy.Step) (engine.SnapshotMutation, error) {
|
||||
logging.V(9).Infof("SnapshotManager.doDelete(%s)", step.Res().URN)
|
||||
logging.V(9).Infof("SnapshotManager.doDelete(%s)", step.URN())
|
||||
err := sm.mutate(func() bool {
|
||||
sm.markOperationPending(step.Old(), resource.OperationTypeDeleting)
|
||||
return true
|
||||
|
@ -395,7 +395,7 @@ func (rsm *replaceSnapshotMutation) End(step deploy.Step, successful bool) error
|
|||
}
|
||||
|
||||
func (sm *SnapshotManager) doRead(step deploy.Step) (engine.SnapshotMutation, error) {
|
||||
logging.V(9).Infof("SnapshotManager.doRead(%s)", step.Res().URN)
|
||||
logging.V(9).Infof("SnapshotManager.doRead(%s)", step.URN())
|
||||
err := sm.mutate(func() bool {
|
||||
sm.markOperationPending(step.New(), resource.OperationTypeReading)
|
||||
return true
|
||||
|
@ -460,7 +460,7 @@ func (rsm *removePendingReplaceSnapshotMutation) End(step deploy.Step, successfu
|
|||
}
|
||||
|
||||
func (sm *SnapshotManager) doImport(step deploy.Step) (engine.SnapshotMutation, error) {
|
||||
logging.V(9).Infof("SnapshotManager.doImport(%s)", step.Res().URN)
|
||||
logging.V(9).Infof("SnapshotManager.doImport(%s)", step.URN())
|
||||
err := sm.mutate(func() bool {
|
||||
sm.markOperationPending(step.New(), resource.OperationTypeImporting)
|
||||
return true
|
||||
|
|
|
@ -80,10 +80,162 @@ func newConfigCmd() *cobra.Command {
|
|||
cmd.AddCommand(newConfigRmCmd(&stack))
|
||||
cmd.AddCommand(newConfigSetCmd(&stack))
|
||||
cmd.AddCommand(newConfigRefreshCmd(&stack))
|
||||
cmd.AddCommand(newConfigCopyCmd(&stack))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newConfigCopyCmd(stack *string) *cobra.Command {
|
||||
var path bool
|
||||
var destinationStackName string
|
||||
|
||||
cpCommand := &cobra.Command{
|
||||
Use: "cp [key]",
|
||||
Short: "Copy config to another stack",
|
||||
Long: "Copies the config from the current stack to the destination stack. If `key` is omitted,\n" +
|
||||
"then all of the config from the current stack will be copied to the destination stack.",
|
||||
Args: cmdutil.MaximumNArgs(1),
|
||||
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
||||
opts := display.Options{
|
||||
Color: cmdutil.GetGlobalColorization(),
|
||||
}
|
||||
|
||||
// Get current stack and ensure that it is a different stack to the destination stack
|
||||
currentStack, err := requireStack(*stack, false, opts, true /*setCurrent*/)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentStack.Ref().Name().String() == destinationStackName {
|
||||
return errors.New("current stack and destination stack are the same")
|
||||
}
|
||||
currentProjectStack, err := loadProjectStack(currentStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the destination stack
|
||||
destinationStack, err := requireStack(destinationStackName, false, opts, false /*setCurrent*/)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destinationProjectStack, err := loadProjectStack(destinationStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do we need to copy a single value or the entire map
|
||||
if len(args) > 0 {
|
||||
// A single key was specified so we only need to copy that specific value
|
||||
return copySingleConfigKey(args[0], path, currentStack, currentProjectStack, destinationStack,
|
||||
destinationProjectStack)
|
||||
}
|
||||
|
||||
return copyEntireConfigMap(currentStack, currentProjectStack, destinationStack, destinationProjectStack)
|
||||
}),
|
||||
}
|
||||
|
||||
cpCommand.PersistentFlags().BoolVar(
|
||||
&path, "path", false,
|
||||
"The key contains a path to a property in a map or list to set")
|
||||
cpCommand.PersistentFlags().StringVarP(
|
||||
&destinationStackName, "dest", "d", "",
|
||||
"The name of the new stack to copy the config to")
|
||||
|
||||
return cpCommand
|
||||
}
|
||||
|
||||
func copySingleConfigKey(configKey string, path bool, currentStack backend.Stack,
|
||||
currentProjectStack *workspace.ProjectStack, destinationStack backend.Stack,
|
||||
destinationProjectStack *workspace.ProjectStack) error {
|
||||
var decrypter config.Decrypter
|
||||
key, err := parseConfigKey(configKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid configuration key")
|
||||
}
|
||||
|
||||
v, ok, err := currentProjectStack.Config.Get(key, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if v.Secure() {
|
||||
var err error
|
||||
if decrypter, err = getStackDecrypter(currentStack); err != nil {
|
||||
return errors.Wrap(err, "could not create a decrypter")
|
||||
}
|
||||
} else {
|
||||
decrypter = config.NewPanicCrypter()
|
||||
}
|
||||
|
||||
encrypter, cerr := getStackEncrypter(destinationStack)
|
||||
if cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
|
||||
val, err := v.Copy(decrypter, encrypter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = destinationProjectStack.Config.Set(key, val, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return saveProjectStack(destinationStack, destinationProjectStack)
|
||||
}
|
||||
|
||||
return errors.Errorf(
|
||||
"configuration key '%s' not found for stack '%s'", prettyKey(key), currentStack.Ref())
|
||||
}
|
||||
|
||||
func copyEntireConfigMap(currentStack backend.Stack,
|
||||
currentProjectStack *workspace.ProjectStack, destinationStack backend.Stack,
|
||||
destinationProjectStack *workspace.ProjectStack) error {
|
||||
|
||||
var decrypter config.Decrypter
|
||||
currentConfig := currentProjectStack.Config
|
||||
if currentConfig.HasSecureValue() {
|
||||
dec, decerr := getStackDecrypter(currentStack)
|
||||
if decerr != nil {
|
||||
return decerr
|
||||
}
|
||||
decrypter = dec
|
||||
} else {
|
||||
decrypter = config.NewPanicCrypter()
|
||||
}
|
||||
|
||||
encrypter, cerr := getStackEncrypter(destinationStack)
|
||||
if cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
|
||||
newProjectConfig, err := currentConfig.Copy(decrypter, encrypter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var requiresSaving bool
|
||||
for key, val := range newProjectConfig {
|
||||
err = destinationProjectStack.Config.Set(key, val, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requiresSaving = true
|
||||
}
|
||||
|
||||
// The use of `requiresSaving` here ensures that there was actually some config
|
||||
// that needed saved, otherwise it's an unnecessary save call
|
||||
if requiresSaving {
|
||||
err := saveProjectStack(destinationStack, destinationProjectStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newConfigGetCmd(stack *string) *cobra.Command {
|
||||
var jsonOut bool
|
||||
var path bool
|
||||
|
@ -431,7 +583,7 @@ func listConfig(stack backend.Stack, showSecrets bool, jsonOut bool) error {
|
|||
// By default, we will use a blinding decrypter to show "[secret]". If requested, display secrets in plaintext.
|
||||
decrypter := config.NewBlindingDecrypter()
|
||||
if cfg.HasSecureValue() && showSecrets {
|
||||
dec, decerr := getStackDencrypter(stack)
|
||||
dec, decerr := getStackDecrypter(stack)
|
||||
if decerr != nil {
|
||||
return decerr
|
||||
}
|
||||
|
@ -518,7 +670,7 @@ func getConfig(stack backend.Stack, key config.Key, path, jsonOut bool) error {
|
|||
var d config.Decrypter
|
||||
if v.Secure() {
|
||||
var err error
|
||||
if d, err = getStackDencrypter(stack); err != nil {
|
||||
if d, err = getStackDecrypter(stack); err != nil {
|
||||
return errors.Wrap(err, "could not create a decrypter")
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -37,7 +37,7 @@ func getStackEncrypter(s backend.Stack) (config.Encrypter, error) {
|
|||
return sm.Encrypter()
|
||||
}
|
||||
|
||||
func getStackDencrypter(s backend.Stack) (config.Decrypter, error) {
|
||||
func getStackDecrypter(s backend.Stack) (config.Decrypter, error) {
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -61,7 +61,7 @@ This command lists data about previous updates for a stack.`,
|
|||
}
|
||||
var decrypter config.Decrypter
|
||||
if showSecrets {
|
||||
crypter, err := getStackDencrypter(s)
|
||||
crypter, err := getStackDecrypter(s)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decrypting secrets")
|
||||
}
|
||||
|
|
|
@ -661,24 +661,18 @@ func (cancellationScopeSource) NewScope(events chan<- engine.Event, isPreview bo
|
|||
message += colors.BrightRed + "Note that terminating immediately may lead to orphaned resources " +
|
||||
"and other inconsistencies.\n" + colors.Reset
|
||||
}
|
||||
events <- engine.Event{
|
||||
Type: engine.StdoutColorEvent,
|
||||
Payload: engine.StdoutEventPayload{
|
||||
Message: message,
|
||||
Color: colors.Always,
|
||||
},
|
||||
}
|
||||
events <- engine.NewEvent(engine.StdoutColorEvent, engine.StdoutEventPayload{
|
||||
Message: message,
|
||||
Color: colors.Always,
|
||||
})
|
||||
|
||||
cancelSource.Cancel()
|
||||
} else {
|
||||
message := colors.BrightRed + "^C received; terminating" + colors.Reset
|
||||
events <- engine.Event{
|
||||
Type: engine.StdoutColorEvent,
|
||||
Payload: engine.StdoutEventPayload{
|
||||
Message: message,
|
||||
Color: colors.Always,
|
||||
},
|
||||
}
|
||||
events <- engine.NewEvent(engine.StdoutColorEvent, engine.StdoutEventPayload{
|
||||
Message: message,
|
||||
Color: colors.Always,
|
||||
})
|
||||
|
||||
cancelSource.Terminate()
|
||||
}
|
||||
|
|
|
@ -895,12 +895,41 @@ func (mod *modContext) getProperties(properties []*schema.Property, lang string,
|
|||
return docProperties
|
||||
}
|
||||
|
||||
func getDockerImagePythonFormalParams() []formalParam {
|
||||
return []formalParam{
|
||||
{
|
||||
Name: "image_name",
|
||||
},
|
||||
{
|
||||
Name: "build",
|
||||
},
|
||||
{
|
||||
Name: "local_image_name",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
{
|
||||
Name: "registry",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
{
|
||||
Name: "skip_push",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
{
|
||||
Name: "opts",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the rendered HTML for the resource's constructor, as well as the specific arguments.
|
||||
func (mod *modContext) genConstructors(r *schema.Resource, allOptionalInputs bool) (map[string]string, map[string][]formalParam) {
|
||||
renderedParams := make(map[string]string)
|
||||
formalParams := make(map[string][]formalParam)
|
||||
isK8sOverlayMod := mod.isKubernetesOverlayModule()
|
||||
isK8sPackage := isKubernetesPackage(mod.pkg)
|
||||
isDockerImageResource := mod.pkg.Name == "docker" && resourceName(r) == "Image"
|
||||
|
||||
for _, lang := range supportedLanguages {
|
||||
var (
|
||||
paramTemplate string
|
||||
|
@ -927,6 +956,9 @@ func (mod *modContext) genConstructors(r *schema.Resource, allOptionalInputs boo
|
|||
if isK8sOverlayMod {
|
||||
params = getKubernetesOverlayPythonFormalParams(mod.mod)
|
||||
break
|
||||
} else if isDockerImageResource {
|
||||
params = getDockerImagePythonFormalParams()
|
||||
break
|
||||
}
|
||||
|
||||
params = make([]formalParam, 0, len(r.InputProperties)+1)
|
||||
|
|
|
@ -33,15 +33,17 @@ func isKubernetesPackage(pkg *schema.Package) bool {
|
|||
|
||||
func (mod *modContext) isKubernetesOverlayModule() bool {
|
||||
// The CustomResource overlay resource is directly under the apiextensions module
|
||||
// and not under a version, so we include that. The resources under helm and yaml are
|
||||
// always under a version.
|
||||
return mod.mod == "apiextensions" ||
|
||||
// and not under a version, so we include that. The Directory overlay resource is directly under the
|
||||
// kustomize module. The resources under helm and yaml are always under a version.
|
||||
return mod.mod == "apiextensions" || mod.mod == "kustomize" ||
|
||||
strings.HasPrefix(mod.mod, "helm") || strings.HasPrefix(mod.mod, "yaml")
|
||||
}
|
||||
|
||||
func (mod *modContext) isComponentResource() bool {
|
||||
// TODO: Support this more generally. For now, only the Helm and YAML overlays use ComponentResources.
|
||||
return strings.HasPrefix(mod.mod, "helm") || strings.HasPrefix(mod.mod, "yaml")
|
||||
// TODO: Support this more generally. For now, only the Helm, Kustomize, and YAML overlays use ComponentResources.
|
||||
return strings.HasPrefix(mod.mod, "helm") ||
|
||||
strings.HasPrefix(mod.mod, "kustomize") ||
|
||||
strings.HasPrefix(mod.mod, "yaml")
|
||||
}
|
||||
|
||||
// getKubernetesOverlayPythonFormalParams returns the formal params to render
|
||||
|
@ -60,6 +62,24 @@ func getKubernetesOverlayPythonFormalParams(modName string) []formalParam {
|
|||
DefaultValue: "=None",
|
||||
},
|
||||
}
|
||||
case "kustomize":
|
||||
params = []formalParam{
|
||||
{
|
||||
Name: "directory",
|
||||
},
|
||||
{
|
||||
Name: "opts",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
{
|
||||
Name: "transformations",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
{
|
||||
Name: "resource_prefix",
|
||||
DefaultValue: "=None",
|
||||
},
|
||||
}
|
||||
case "yaml":
|
||||
params = []formalParam{
|
||||
{
|
||||
|
|
4
pkg/codegen/docs/templates/resource.tmpl
vendored
4
pkg/codegen/docs/templates/resource.tmpl
vendored
|
@ -31,7 +31,7 @@
|
|||
{{ htmlSafe "{{% /choosable %}}" }}
|
||||
|
||||
{{ htmlSafe "{{% choosable language python %}}" }}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="k">def </span>{{ template "linkify_param" .ConstructorResource.python }}<span class="p">(resource_name, </span>{{ htmlSafe .ConstructorParams.python }}<span class="p">);</span></code></pre></div>
|
||||
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="k">def </span>{{ template "linkify_param" .ConstructorResource.python }}<span class="p">(resource_name, </span>{{ htmlSafe .ConstructorParams.python }}<span class="p">)</span></code></pre></div>
|
||||
{{ htmlSafe "{{% /choosable %}}" }}
|
||||
|
||||
{{ htmlSafe "{{% choosable language go %}}" }}
|
||||
|
@ -111,7 +111,7 @@ Get an existing {{.Header.Title}} resource's state with the given name, ID, and
|
|||
{{ htmlSafe "{{% /choosable %}}" }}
|
||||
|
||||
{{ htmlSafe "{{% choosable language python %}}" }}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="k">static </span><span class="nf">get</span><span class="p">(resource_name, id, opts=None, </span>{{ htmlSafe .LookupParams.python }}<span class="p">, __props__=None);</span></code></pre></div>
|
||||
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="k">static </span><span class="nf">get</span><span class="p">(resource_name, id, opts=None, </span>{{ htmlSafe .LookupParams.python }}<span class="p">, __props__=None)</span></code></pre></div>
|
||||
{{ htmlSafe "{{% /choosable %}}" }}
|
||||
|
||||
{{ htmlSafe "{{% choosable language go %}}" }}
|
||||
|
|
|
@ -129,6 +129,16 @@ const csharpProjectFileTemplateText = `<Project Sdk="Microsoft.NET.Sdk">
|
|||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="version.txt" />
|
||||
<Content Include="version.txt" />
|
||||
|
|
|
@ -92,6 +92,14 @@ func camel(s string) string {
|
|||
return string(res)
|
||||
}
|
||||
|
||||
func tokenToPackage(pkg *schema.Package, overrides map[string]string, tok string) string {
|
||||
mod := pkg.TokenToModule(tok)
|
||||
if override, ok := overrides[mod]; ok {
|
||||
mod = override
|
||||
}
|
||||
return strings.ToLower(mod)
|
||||
}
|
||||
|
||||
type pkgContext struct {
|
||||
pkg *schema.Package
|
||||
mod string
|
||||
|
@ -120,6 +128,10 @@ func (pkg *pkgContext) details(t *schema.ObjectType) *typeDetails {
|
|||
return details
|
||||
}
|
||||
|
||||
func (pkg *pkgContext) tokenToPackage(tok string) string {
|
||||
return tokenToPackage(pkg.pkg, pkg.modToPkg, tok)
|
||||
}
|
||||
|
||||
func (pkg *pkgContext) tokenToType(tok string) string {
|
||||
// token := pkg : module : member
|
||||
// module := path/to/module
|
||||
|
@ -133,14 +145,13 @@ func (pkg *pkgContext) tokenToType(tok string) string {
|
|||
panic(fmt.Errorf("pkg.pkg is nil. token %s", tok))
|
||||
}
|
||||
|
||||
mod, name := pkg.pkg.TokenToModule(tok), components[2]
|
||||
if override, ok := pkg.modToPkg[mod]; ok {
|
||||
mod = override
|
||||
}
|
||||
mod, name := pkg.tokenToPackage(tok), components[2]
|
||||
|
||||
// If the package containing the type's token already has a resource with the
|
||||
// same name, add a `Type` suffix.
|
||||
modPkg := pkg.getPkg(mod)
|
||||
modPkg, ok := pkg.packages[mod]
|
||||
contract.Assert(ok)
|
||||
|
||||
name = Title(name)
|
||||
if modPkg.names.has(name) {
|
||||
name += "Type"
|
||||
|
@ -149,6 +160,9 @@ func (pkg *pkgContext) tokenToType(tok string) string {
|
|||
if mod == pkg.mod {
|
||||
return name
|
||||
}
|
||||
if mod == "" {
|
||||
mod = components[0]
|
||||
}
|
||||
return strings.Replace(mod, "/", "", -1) + "." + name
|
||||
}
|
||||
|
||||
|
@ -921,10 +935,7 @@ func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, imports strin
|
|||
case *schema.MapType:
|
||||
pkg.getTypeImports(t.ElementType, recurse, imports, seen)
|
||||
case *schema.ObjectType:
|
||||
mod := pkg.pkg.TokenToModule(t.Token)
|
||||
if override, ok := pkg.modToPkg[mod]; ok {
|
||||
mod = override
|
||||
}
|
||||
mod := pkg.tokenToPackage(t.Token)
|
||||
if mod != pkg.mod {
|
||||
imports.add(path.Join(pkg.importBasePath, mod))
|
||||
}
|
||||
|
@ -1071,25 +1082,10 @@ func (pkg *pkgContext) genConfig(w io.Writer, variables []*schema.Property) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func (pkg *pkgContext) getPkg(mod string) *pkgContext {
|
||||
if override, ok := pkg.modToPkg[mod]; ok {
|
||||
mod = override
|
||||
}
|
||||
pack, ok := pkg.packages[mod]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return pack
|
||||
}
|
||||
|
||||
// generatePackageContextMap groups resources, types, and functions into Go packages.
|
||||
func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackageInfo) map[string]*pkgContext {
|
||||
packages := map[string]*pkgContext{}
|
||||
getPkg := func(mod string) *pkgContext {
|
||||
if override, ok := goInfo.ModuleToPackage[mod]; ok {
|
||||
mod = override
|
||||
}
|
||||
|
||||
pack, ok := packages[mod]
|
||||
if !ok {
|
||||
pack = &pkgContext{
|
||||
|
@ -1110,7 +1106,7 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag
|
|||
}
|
||||
|
||||
getPkgFromToken := func(token string) *pkgContext {
|
||||
return getPkg(pkg.TokenToModule(token))
|
||||
return getPkg(tokenToPackage(pkg, goInfo.ModuleToPackage, token))
|
||||
}
|
||||
|
||||
if len(pkg.Config) > 0 {
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
|
||||
type bindOptions struct {
|
||||
allowMissingVariables bool
|
||||
host plugin.Host
|
||||
loader schema.Loader
|
||||
packageCache *PackageCache
|
||||
}
|
||||
|
||||
|
@ -59,8 +59,12 @@ func AllowMissingVariables(options *bindOptions) {
|
|||
}
|
||||
|
||||
func PluginHost(host plugin.Host) BindOption {
|
||||
return Loader(schema.NewPluginLoader(host))
|
||||
}
|
||||
|
||||
func Loader(loader schema.Loader) BindOption {
|
||||
return func(options *bindOptions) {
|
||||
options.host = host
|
||||
options.loader = loader
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +82,7 @@ func BindProgram(files []*syntax.File, opts ...BindOption) (*Program, hcl.Diagno
|
|||
o(&options)
|
||||
}
|
||||
|
||||
if options.host == nil {
|
||||
if options.loader == nil {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -87,7 +91,7 @@ func BindProgram(files []*syntax.File, opts ...BindOption) (*Program, hcl.Diagno
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
options.host = ctx.Host
|
||||
options.loader = schema.NewPluginLoader(ctx.Host)
|
||||
|
||||
defer contract.IgnoreClose(ctx)
|
||||
}
|
||||
|
|
|
@ -21,12 +21,9 @@ import (
|
|||
"github.com/blang/semver"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/model"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/plugin"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/tokens"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
)
|
||||
|
||||
|
@ -60,29 +57,13 @@ func (c *PackageCache) getPackageSchema(name string) (*packageSchema, bool) {
|
|||
// GetSchema method.
|
||||
//
|
||||
// TODO: schema and provider versions
|
||||
func (c *PackageCache) loadPackageSchema(host plugin.Host, name string) (*packageSchema, error) {
|
||||
func (c *PackageCache) loadPackageSchema(loader schema.Loader, name string) (*packageSchema, error) {
|
||||
if s, ok := c.getPackageSchema(name); ok {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
providerVersion := (*semver.Version)(nil)
|
||||
provider, err := host.Provider(tokens.Package(name), providerVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schemaFormatVersion := 0
|
||||
schemaBytes, err := provider.GetSchema(schemaFormatVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spec schema.PackageSpec
|
||||
if err := jsoniter.Unmarshal(schemaBytes, &spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkg, err := schema.ImportSpec(spec, nil)
|
||||
version := (*semver.Version)(nil)
|
||||
pkg, err := loader.LoadPackage(name, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -153,7 +134,7 @@ func (b *binder) loadReferencedPackageSchemas(n Node) error {
|
|||
if _, ok := b.referencedPackages[name]; ok {
|
||||
continue
|
||||
}
|
||||
pkg, err := b.options.packageCache.loadPackageSchema(b.options.host, name)
|
||||
pkg, err := b.options.packageCache.loadPackageSchema(b.options.loader, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/internal/test"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
)
|
||||
|
||||
func BenchmarkLoadPackage(b *testing.B) {
|
||||
host := test.NewHost(testdataPath)
|
||||
loader := schema.NewPluginLoader(test.NewHost(testdataPath))
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := NewPackageCache().loadPackageSchema(host, "aws")
|
||||
_, err := NewPackageCache().loadPackageSchema(loader, "aws")
|
||||
contract.AssertNoError(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,14 @@ func (a *Attribute) HasTrailingTrivia() bool {
|
|||
return a.Value.HasTrailingTrivia()
|
||||
}
|
||||
|
||||
func (a *Attribute) GetLeadingTrivia() syntax.TriviaList {
|
||||
return a.Tokens.GetName(a.Name).LeadingTrivia
|
||||
}
|
||||
|
||||
func (a *Attribute) GetTrailingTrivia() syntax.TriviaList {
|
||||
return a.Value.GetTrailingTrivia()
|
||||
}
|
||||
|
||||
func (a *Attribute) Format(f fmt.State, c rune) {
|
||||
a.print(f, &printer{})
|
||||
}
|
||||
|
|
|
@ -52,6 +52,14 @@ func (b *Block) HasTrailingTrivia() bool {
|
|||
return b.Tokens != nil
|
||||
}
|
||||
|
||||
func (b *Block) GetLeadingTrivia() syntax.TriviaList {
|
||||
return b.Tokens.GetType(b.Type).LeadingTrivia
|
||||
}
|
||||
|
||||
func (b *Block) GetTrailingTrivia() syntax.TriviaList {
|
||||
return b.Tokens.GetCloseBrace().TrailingTrivia
|
||||
}
|
||||
|
||||
func (b *Block) Format(f fmt.State, c rune) {
|
||||
b.print(f, &printer{})
|
||||
}
|
||||
|
@ -94,9 +102,6 @@ func (b *Block) print(w io.Writer, p *printer) {
|
|||
p.indented(func() {
|
||||
b.Body.print(w, p)
|
||||
})
|
||||
if !b.Body.HasTrailingTrivia() {
|
||||
p.fprintf(w, "\n")
|
||||
}
|
||||
|
||||
if b.Tokens != nil {
|
||||
p.fprintf(w, "%v", b.Tokens.GetCloseBrace())
|
||||
|
|
|
@ -61,6 +61,23 @@ func (b *Body) HasTrailingTrivia() bool {
|
|||
return len(b.Items) > 0 && b.Items[len(b.Items)-1].HasTrailingTrivia()
|
||||
}
|
||||
|
||||
func (b *Body) GetLeadingTrivia() syntax.TriviaList {
|
||||
if len(b.Items) == 0 {
|
||||
return nil
|
||||
}
|
||||
return b.Items[0].GetLeadingTrivia()
|
||||
}
|
||||
|
||||
func (b *Body) GetTrailingTrivia() syntax.TriviaList {
|
||||
if eof := b.Tokens.GetEndOfFile(); eof != nil {
|
||||
return eof.TrailingTrivia
|
||||
}
|
||||
if len(b.Items) == 0 {
|
||||
return nil
|
||||
}
|
||||
return b.Items[len(b.Items)-1].GetTrailingTrivia()
|
||||
}
|
||||
|
||||
func (b *Body) Format(f fmt.State, c rune) {
|
||||
b.print(f, &printer{})
|
||||
}
|
||||
|
@ -69,6 +86,9 @@ func (b *Body) print(w io.Writer, p *printer) {
|
|||
// Print the items, separated by newlines.
|
||||
for _, item := range b.Items {
|
||||
p.fprintf(w, "% v", item)
|
||||
if !item.GetTrailingTrivia().EndsOnNewLine() {
|
||||
p.fprintf(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
// If the body has an end-of-file token, print it.
|
||||
|
|
|
@ -37,12 +37,8 @@ type Expression interface {
|
|||
// NodeTokens returns the syntax.Tokens associated with the expression.
|
||||
NodeTokens() syntax.NodeTokens
|
||||
|
||||
// GetLeadingTrivia returns the leading trivia associated with the expression.
|
||||
GetLeadingTrivia() syntax.TriviaList
|
||||
// SetLeadingTrivia sets the leading trivia associated with the expression.
|
||||
SetLeadingTrivia(syntax.TriviaList)
|
||||
// GetTrailingTrivia returns the trailing trivia associated with the expression.
|
||||
GetTrailingTrivia() syntax.TriviaList
|
||||
// SetTrailingTrivia sets the trailing trivia associated with the expression.
|
||||
SetTrailingTrivia(syntax.TriviaList)
|
||||
|
||||
|
@ -1556,6 +1552,7 @@ func (x *ObjectConsExpression) print(w io.Writer, p *printer) {
|
|||
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpenBrace(len(x.Items)))
|
||||
|
||||
// Print the items.
|
||||
isMultiLine, trailingNewline := false, false
|
||||
p.indented(func() {
|
||||
items := x.Tokens.GetItems(len(x.Items))
|
||||
for i, item := range x.Items {
|
||||
|
@ -1564,7 +1561,12 @@ func (x *ObjectConsExpression) print(w io.Writer, p *printer) {
|
|||
tokens = items[i]
|
||||
}
|
||||
|
||||
if !item.Key.HasLeadingTrivia() {
|
||||
if item.Key.HasLeadingTrivia() {
|
||||
if _, i := item.Key.GetLeadingTrivia().Index("\n"); i != -1 {
|
||||
isMultiLine = true
|
||||
}
|
||||
} else if len(items) > 1 {
|
||||
isMultiLine = true
|
||||
p.fprintf(w, "\n%s", p.indent)
|
||||
}
|
||||
p.fprintf(w, "%v% v% v", item.Key, tokens.Equals, item.Value)
|
||||
|
@ -1572,6 +1574,14 @@ func (x *ObjectConsExpression) print(w io.Writer, p *printer) {
|
|||
if tokens.Comma != nil {
|
||||
p.fprintf(w, "%v", tokens.Comma)
|
||||
}
|
||||
|
||||
if isMultiLine && i == len(items)-1 {
|
||||
trailingTrivia := item.Value.GetTrailingTrivia()
|
||||
if tokens.Comma != nil {
|
||||
trailingTrivia = tokens.Comma.TrailingTrivia
|
||||
}
|
||||
trailingNewline = trailingTrivia.EndsOnNewLine()
|
||||
}
|
||||
}
|
||||
|
||||
if len(x.Items) < len(items) {
|
||||
|
@ -1585,7 +1595,11 @@ func (x *ObjectConsExpression) print(w io.Writer, p *printer) {
|
|||
})
|
||||
|
||||
if x.Tokens != nil {
|
||||
p.fprintf(w, "%v%)", x.Tokens.CloseBrace, x.Tokens.Parentheses)
|
||||
pre := ""
|
||||
if isMultiLine && !trailingNewline {
|
||||
pre = "\n" + p.indent
|
||||
}
|
||||
p.fprintf(w, "%s%v%)", pre, x.Tokens.CloseBrace, x.Tokens.Parentheses)
|
||||
} else {
|
||||
p.fprintf(w, "\n%s}", p.indent)
|
||||
}
|
||||
|
|
26
pkg/codegen/hcl2/model/print_test.go
Normal file
26
pkg/codegen/hcl2/model/print_test.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestPrintNoTokens(t *testing.T) {
|
||||
b := &Block{
|
||||
Type: "block", Body: &Body{
|
||||
Items: []BodyItem{
|
||||
&Attribute{
|
||||
Name: "attribute",
|
||||
Value: &LiteralValueExpression{
|
||||
Value: cty.True,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expected := "block {\n attribute = true\n}"
|
||||
assert.Equal(t, expected, fmt.Sprintf("%v", b))
|
||||
}
|
|
@ -19,6 +19,8 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/syntax"
|
||||
)
|
||||
|
||||
type printable interface {
|
||||
|
@ -28,6 +30,10 @@ type printable interface {
|
|||
HasLeadingTrivia() bool
|
||||
// HasTrailingTrivia returns true if the value has associated trailing trivia.
|
||||
HasTrailingTrivia() bool
|
||||
// GetLeadingTrivia returns the leading trivia for this value, if any.
|
||||
GetLeadingTrivia() syntax.TriviaList
|
||||
// GetTrailingTrivia returns the trailing trivia for this value, if any.
|
||||
GetTrailingTrivia() syntax.TriviaList
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
|
@ -50,7 +56,7 @@ func (p *printer) format(f fmt.State, c rune, pp printable) {
|
|||
if f.Flag(' ') && !pp.HasLeadingTrivia() {
|
||||
switch pp.(type) {
|
||||
case BodyItem:
|
||||
p.fprintf(f, "\n%s", p.indent)
|
||||
p.fprintf(f, "%s", p.indent)
|
||||
case Expression:
|
||||
p.fprintf(f, " ")
|
||||
}
|
||||
|
|
|
@ -150,6 +150,10 @@ func (r *applyRewriter) inspectsEventualValues(x model.Expression) bool {
|
|||
case *model.ForExpression:
|
||||
return r.hasEventualElements(x.Collection)
|
||||
case *model.FunctionCallExpression:
|
||||
_, isEventual := r.isEventualType(x.Signature.ReturnType)
|
||||
if isEventual {
|
||||
return true
|
||||
}
|
||||
for i, arg := range x.Args {
|
||||
if r.hasEventualValues(arg) && r.isPromptArg(x.Signature.Parameters[i].Type, arg) {
|
||||
return true
|
||||
|
@ -385,8 +389,18 @@ func (ctx *observeContext) rewriteApplyArg(applyArg model.Expression, paramType
|
|||
func (ctx *observeContext) rewriteRelativeTraversalExpression(expr *model.RelativeTraversalExpression,
|
||||
isRoot bool) model.Expression {
|
||||
|
||||
// If the access is not an output() or a promise(), return the node as-is.
|
||||
paramType, isEventual := ctx.isEventualType(expr.Type())
|
||||
if !isEventual {
|
||||
return expr
|
||||
}
|
||||
|
||||
// If the receiver is an eventual type, we're done.
|
||||
if receiverResolvedType, isEventual := ctx.isEventualType(model.GetTraversableType(expr.Parts[0])); isEventual {
|
||||
return ctx.rewriteApplyArg(expr.Source, receiverResolvedType, expr.Traversal, expr.Parts[1:], isRoot)
|
||||
}
|
||||
|
||||
// Compute the type of the apply and callback arguments.
|
||||
paramType := model.ResolveOutputs(expr.Type())
|
||||
parts, traversal := expr.Parts, expr.Traversal
|
||||
for i := range expr.Traversal {
|
||||
partResolvedType, isEventual := paramType, true
|
||||
|
|
|
@ -20,6 +20,7 @@ func (nameInfo) Format(name string) string {
|
|||
func TestApplyRewriter(t *testing.T) {
|
||||
cases := []struct {
|
||||
input, output string
|
||||
skipPromises bool
|
||||
}{
|
||||
{
|
||||
input: `"v: ${resource.foo.bar}"`,
|
||||
|
@ -79,23 +80,45 @@ func TestApplyRewriter(t *testing.T) {
|
|||
},
|
||||
{
|
||||
input: `toJSON({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Principal = "*"
|
||||
Action = [ "s3:GetObject" ]
|
||||
Resource = [ "arn:aws:s3:::${resource.id}/*" ]
|
||||
}]
|
||||
})`,
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Principal = "*"
|
||||
Action = [ "s3:GetObject" ]
|
||||
Resource = [ "arn:aws:s3:::${resource.id}/*" ]
|
||||
}]
|
||||
})`,
|
||||
output: `__apply(resource.id,eval(id, toJSON({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Principal = "*"
|
||||
Action = [ "s3:GetObject" ]
|
||||
Resource = [ "arn:aws:s3:::${id}/*" ]
|
||||
}]
|
||||
})))`,
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Principal = "*"
|
||||
Action = [ "s3:GetObject" ]
|
||||
Resource = [ "arn:aws:s3:::${id}/*" ]
|
||||
}]
|
||||
})))`,
|
||||
},
|
||||
{
|
||||
input: `getPromise().property`,
|
||||
output: `__apply(getPromise(), eval(getPromise, getPromise.property))`,
|
||||
},
|
||||
{
|
||||
input: `getPromise().object.foo`,
|
||||
output: `__apply(getPromise(), eval(getPromise, getPromise.object.foo))`,
|
||||
},
|
||||
{
|
||||
input: `getPromise().property`,
|
||||
output: `getPromise().property`,
|
||||
skipPromises: true,
|
||||
},
|
||||
{
|
||||
input: `getPromise().object.foo`,
|
||||
output: `getPromise().object.foo`,
|
||||
skipPromises: true,
|
||||
},
|
||||
{
|
||||
input: `getPromise(resource.id).property`,
|
||||
output: `__apply(__apply(resource.id,eval(id, getPromise(id))), eval(getPromise, getPromise.property))`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -130,14 +153,28 @@ func TestApplyRewriter(t *testing.T) {
|
|||
})
|
||||
scope.DefineFunction("element", pulumiBuiltins["element"])
|
||||
scope.DefineFunction("toJSON", pulumiBuiltins["toJSON"])
|
||||
scope.DefineFunction("getPromise", model.NewFunction(model.StaticFunctionSignature{
|
||||
Parameters: []model.Parameter{{
|
||||
Name: "p",
|
||||
Type: model.NewOptionalType(model.StringType),
|
||||
}},
|
||||
ReturnType: model.NewPromiseType(model.NewObjectType(map[string]model.Type{
|
||||
"property": model.StringType,
|
||||
"object": model.NewObjectType(map[string]model.Type{
|
||||
"foo": model.StringType,
|
||||
}),
|
||||
})),
|
||||
}))
|
||||
|
||||
for _, c := range cases {
|
||||
expr, diags := model.BindExpressionText(c.input, scope, hcl.Pos{})
|
||||
assert.Len(t, diags, 0)
|
||||
t.Run(c.input, func(t *testing.T) {
|
||||
expr, diags := model.BindExpressionText(c.input, scope, hcl.Pos{})
|
||||
assert.Len(t, diags, 0)
|
||||
|
||||
expr, diags = RewriteApplies(expr, nameInfo(0), true)
|
||||
assert.Len(t, diags, 0)
|
||||
expr, diags = RewriteApplies(expr, nameInfo(0), !c.skipPromises)
|
||||
assert.Len(t, diags, 0)
|
||||
|
||||
assert.Equal(t, c.output, fmt.Sprintf("%v", expr))
|
||||
assert.Equal(t, c.output, fmt.Sprintf("%v", expr))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package syntax
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
|
@ -110,6 +113,33 @@ func (trivia TriviaList) CollapseWhitespace() TriviaList {
|
|||
return result
|
||||
}
|
||||
|
||||
func (trivia TriviaList) EndsOnNewLine() bool {
|
||||
for _, trivia := range trivia {
|
||||
b := trivia.Bytes()
|
||||
for len(b) > 0 {
|
||||
r, sz := utf8.DecodeLastRune(b)
|
||||
if r == '\n' {
|
||||
return true
|
||||
}
|
||||
if !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
b = b[:len(b)-sz]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (trivia TriviaList) Index(sep string) (Trivia, int) {
|
||||
s := []byte(sep)
|
||||
for _, trivia := range trivia {
|
||||
if i := bytes.Index(trivia.Bytes(), s); i != -1 {
|
||||
return trivia, i
|
||||
}
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func (trivia TriviaList) Format(f fmt.State, c rune) {
|
||||
for _, trivia := range trivia {
|
||||
_, err := f.Write(trivia.Bytes())
|
||||
|
@ -173,6 +203,26 @@ type TemplateDelimiter struct {
|
|||
bytes []byte
|
||||
}
|
||||
|
||||
// NewTemplateDelimiter creates a new TemplateDelimiter value with the given delimiter type. If the token type is not a
|
||||
// template delimiter, this function will panic.
|
||||
func NewTemplateDelimiter(typ hclsyntax.TokenType) TemplateDelimiter {
|
||||
var s string
|
||||
switch typ {
|
||||
case hclsyntax.TokenTemplateInterp:
|
||||
s = "${"
|
||||
case hclsyntax.TokenTemplateControl:
|
||||
s = "%{"
|
||||
case hclsyntax.TokenTemplateSeqEnd:
|
||||
s = "}"
|
||||
default:
|
||||
panic(fmt.Errorf("%v is not a template delimiter", typ))
|
||||
}
|
||||
return TemplateDelimiter{
|
||||
Type: typ,
|
||||
bytes: []byte(s),
|
||||
}
|
||||
}
|
||||
|
||||
// Range returns the range of the delimiter in the source file.
|
||||
func (t TemplateDelimiter) Range() hcl.Range {
|
||||
return t.rng
|
||||
|
|
468
pkg/codegen/importer/hcl2.go
Normal file
468
pkg/codegen/importer/hcl2.go
Normal file
|
@ -0,0 +1,468 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package importer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/model"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/syntax"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
||||
"github.com/pulumi/pulumi/pkg/v2/resource/deploy/providers"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Null represents Pulumi HCL2's `null` variable.
|
||||
var Null = &model.Variable{
|
||||
Name: "null",
|
||||
VariableType: model.NoneType,
|
||||
}
|
||||
|
||||
// GenerateHCL2Definition generates a Pulumi HCL2 definition for a given resource.
|
||||
func GenerateHCL2Definition(loader schema.Loader, state *resource.State, names NameTable) (*model.Block, error) {
|
||||
// TODO: pull the package version from the resource's provider
|
||||
pkg, err := loader.LoadPackage(string(state.Type.Package()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, ok := pkg.GetResource(string(state.Type))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown resource type '%v'", r)
|
||||
}
|
||||
|
||||
var items []model.BodyItem
|
||||
for _, p := range r.InputProperties {
|
||||
x, err := generatePropertyValue(p, state.Inputs[resource.PropertyKey(p.Name)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if x != nil {
|
||||
items = append(items, &model.Attribute{
|
||||
Name: p.Name,
|
||||
Value: x,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resourceOptions, err := makeResourceOptions(state, names)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resourceOptions != nil {
|
||||
items = append(items, resourceOptions)
|
||||
}
|
||||
|
||||
typ, name := state.URN.Type(), state.URN.Name()
|
||||
return &model.Block{
|
||||
Tokens: syntax.NewBlockTokens("resource", string(name), string(typ)),
|
||||
Type: "resource",
|
||||
Labels: []string{string(name), string(typ)},
|
||||
Body: &model.Body{
|
||||
Items: items,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newVariableReference(name string) model.Expression {
|
||||
return model.VariableReference(&model.Variable{
|
||||
Name: name,
|
||||
VariableType: model.DynamicType,
|
||||
})
|
||||
}
|
||||
|
||||
func appendResourceOption(block *model.Block, name string, value model.Expression) *model.Block {
|
||||
if block == nil {
|
||||
block = &model.Block{
|
||||
Tokens: syntax.NewBlockTokens("options"),
|
||||
Type: "options",
|
||||
Body: &model.Body{},
|
||||
}
|
||||
}
|
||||
block.Body.Items = append(block.Body.Items, &model.Attribute{
|
||||
Tokens: syntax.NewAttributeTokens(name),
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
return block
|
||||
}
|
||||
|
||||
func makeResourceOptions(state *resource.State, names NameTable) (*model.Block, error) {
|
||||
var resourceOptions *model.Block
|
||||
if state.Parent != "" && state.Parent.Type() != resource.RootStackType {
|
||||
name, ok := names[state.Parent]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no name for parent %v", state.Parent)
|
||||
}
|
||||
resourceOptions = appendResourceOption(resourceOptions, "parent", newVariableReference(name))
|
||||
}
|
||||
if state.Provider != "" {
|
||||
ref, err := providers.ParseReference(state.Provider)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid provider reference %v: %w", state.Provider, err)
|
||||
}
|
||||
if !providers.IsDefaultProvider(ref.URN()) {
|
||||
name, ok := names[ref.URN()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no name for provider %v", state.Parent)
|
||||
}
|
||||
resourceOptions = appendResourceOption(resourceOptions, "provider", newVariableReference(name))
|
||||
}
|
||||
}
|
||||
if len(state.Dependencies) != 0 {
|
||||
deps := make([]model.Expression, len(state.Dependencies))
|
||||
for i, d := range state.Dependencies {
|
||||
name, ok := names[d]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no name for resource %v", d)
|
||||
}
|
||||
deps[i] = newVariableReference(name)
|
||||
}
|
||||
resourceOptions = appendResourceOption(resourceOptions, "dependsOn", &model.TupleConsExpression{
|
||||
Tokens: syntax.NewTupleConsTokens(len(deps)),
|
||||
Expressions: deps,
|
||||
})
|
||||
}
|
||||
if state.Protect {
|
||||
resourceOptions = appendResourceOption(resourceOptions, "protect", &model.LiteralValueExpression{
|
||||
Tokens: syntax.NewLiteralValueTokens(cty.True),
|
||||
Value: cty.True,
|
||||
})
|
||||
}
|
||||
return resourceOptions, nil
|
||||
}
|
||||
|
||||
// typeRank orders types by their simplicity.
|
||||
func typeRank(t schema.Type) int {
|
||||
switch t {
|
||||
case schema.BoolType:
|
||||
return 1
|
||||
case schema.IntType:
|
||||
return 2
|
||||
case schema.NumberType:
|
||||
return 3
|
||||
case schema.StringType:
|
||||
return 4
|
||||
case schema.AssetType:
|
||||
return 5
|
||||
case schema.ArchiveType:
|
||||
return 6
|
||||
case schema.JSONType:
|
||||
return 7
|
||||
case schema.AnyType:
|
||||
return 13
|
||||
default:
|
||||
switch t.(type) {
|
||||
case *schema.TokenType:
|
||||
return 8
|
||||
case *schema.ArrayType:
|
||||
return 9
|
||||
case *schema.MapType:
|
||||
return 10
|
||||
case *schema.ObjectType:
|
||||
return 11
|
||||
case *schema.UnionType:
|
||||
return 12
|
||||
default:
|
||||
return int(math.MaxInt32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// simplerType returns true if T is simpler than U.
|
||||
//
|
||||
// The first-order ranking is:
|
||||
//
|
||||
// bool < int < number < string < archive < asset < json < token < array < map < object < union < any
|
||||
//
|
||||
// Additional rules apply to composite types of the same kind:
|
||||
// - array(T) is simpler than array(U) if T is simpler than U
|
||||
// - map(T) is simpler than map(U) if T is simpler than U
|
||||
// - object({ ... }) is simpler than object({ ... }) if the former has a greater number of required properties that are
|
||||
// simpler than the latter's required properties
|
||||
// - union(...) is simpler than union(...) if the former's simplest element type is simpler than the latter's simplest
|
||||
// element type
|
||||
func simplerType(t, u schema.Type) bool {
|
||||
tRank, uRank := typeRank(t), typeRank(u)
|
||||
if tRank < uRank {
|
||||
return true
|
||||
} else if tRank > uRank {
|
||||
return false
|
||||
}
|
||||
|
||||
// At this point we know that t and u have the same concrete type.
|
||||
switch t := t.(type) {
|
||||
case *schema.TokenType:
|
||||
u := u.(*schema.TokenType)
|
||||
if t.UnderlyingType != nil && u.UnderlyingType != nil {
|
||||
return simplerType(t.UnderlyingType, u.UnderlyingType)
|
||||
}
|
||||
return false
|
||||
case *schema.ArrayType:
|
||||
return simplerType(t.ElementType, u.(*schema.ArrayType).ElementType)
|
||||
case *schema.MapType:
|
||||
return simplerType(t.ElementType, u.(*schema.MapType).ElementType)
|
||||
case *schema.ObjectType:
|
||||
// Count how many of T's required properties are simpler than U's required properties and vice versa.
|
||||
uu := u.(*schema.ObjectType)
|
||||
tscore, nt, uscore := 0, 0, 0
|
||||
for _, p := range t.Properties {
|
||||
if p.IsRequired {
|
||||
nt++
|
||||
for _, q := range uu.Properties {
|
||||
if q.IsRequired {
|
||||
if simplerType(p.Type, q.Type) {
|
||||
tscore++
|
||||
}
|
||||
if simplerType(q.Type, p.Type) {
|
||||
uscore++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the number of T's required properties that are simpler that U's required properties exceeds the number
|
||||
// of U's required properties that are simpler than T's required properties, T is simpler.
|
||||
if tscore > uscore {
|
||||
return true
|
||||
}
|
||||
if tscore < uscore {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the above counts are equal, T is simpler if it has fewer required properties.
|
||||
nu := 0
|
||||
for _, q := range uu.Properties {
|
||||
if q.IsRequired {
|
||||
nu++
|
||||
}
|
||||
}
|
||||
|
||||
return nt < nu
|
||||
case *schema.UnionType:
|
||||
// Pick whichever has the simplest element type.
|
||||
var simplestElementType schema.Type
|
||||
for _, u := range u.(*schema.UnionType).ElementTypes {
|
||||
if simplestElementType == nil || simplerType(u, simplestElementType) {
|
||||
simplestElementType = u
|
||||
}
|
||||
}
|
||||
for _, t := range t.ElementTypes {
|
||||
if simplestElementType == nil || simplerType(t, simplestElementType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// zeroValue constructs a zero value of the given type.
|
||||
func zeroValue(t schema.Type) model.Expression {
|
||||
switch t := t.(type) {
|
||||
case *schema.MapType:
|
||||
return &model.ObjectConsExpression{}
|
||||
case *schema.ArrayType:
|
||||
return &model.TupleConsExpression{}
|
||||
case *schema.UnionType:
|
||||
// If there is a default type, create a value of that type.
|
||||
if t.DefaultType != nil {
|
||||
return zeroValue(t.DefaultType)
|
||||
}
|
||||
// Otherwise, pick the simplest type in the list.
|
||||
var simplestType schema.Type
|
||||
for _, t := range t.ElementTypes {
|
||||
if simplestType == nil || simplerType(t, simplestType) {
|
||||
simplestType = t
|
||||
}
|
||||
}
|
||||
return zeroValue(simplestType)
|
||||
case *schema.ObjectType:
|
||||
var items []model.ObjectConsItem
|
||||
for _, p := range t.Properties {
|
||||
if p.IsRequired {
|
||||
items = append(items, model.ObjectConsItem{
|
||||
Key: &model.LiteralValueExpression{
|
||||
Value: cty.StringVal(p.Name),
|
||||
},
|
||||
Value: zeroValue(p.Type),
|
||||
})
|
||||
}
|
||||
}
|
||||
return &model.ObjectConsExpression{Items: items}
|
||||
case *schema.TokenType:
|
||||
if t.UnderlyingType != nil {
|
||||
return zeroValue(t.UnderlyingType)
|
||||
}
|
||||
return model.VariableReference(Null)
|
||||
}
|
||||
switch t {
|
||||
case schema.BoolType:
|
||||
x, err := generateValue(t, resource.NewBoolProperty(false))
|
||||
contract.IgnoreError(err)
|
||||
return x
|
||||
case schema.IntType, schema.NumberType:
|
||||
x, err := generateValue(t, resource.NewNumberProperty(0))
|
||||
contract.IgnoreError(err)
|
||||
return x
|
||||
case schema.StringType:
|
||||
x, err := generateValue(t, resource.NewStringProperty(""))
|
||||
contract.IgnoreError(err)
|
||||
return x
|
||||
case schema.ArchiveType, schema.AssetType:
|
||||
return model.VariableReference(Null)
|
||||
case schema.JSONType, schema.AnyType:
|
||||
return &model.ObjectConsExpression{}
|
||||
default:
|
||||
contract.Failf("unexpected schema type %v", t)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// generatePropertyValue generates the value for the given property. If the value is absent and the property is
|
||||
// required, a zero value for the property's type is generated. If the value is absent and the property is not
|
||||
// required, no value is generated (i.e. this function returns nil).
|
||||
func generatePropertyValue(property *schema.Property, value resource.PropertyValue) (model.Expression, error) {
|
||||
if !value.HasValue() {
|
||||
if !property.IsRequired {
|
||||
return nil, nil
|
||||
}
|
||||
return zeroValue(property.Type), nil
|
||||
}
|
||||
|
||||
return generateValue(property.Type, value)
|
||||
}
|
||||
|
||||
// generateValue generates a value from the given property value. The given type may or may not match the shape of the
|
||||
// given value.
|
||||
func generateValue(typ schema.Type, value resource.PropertyValue) (model.Expression, error) {
|
||||
switch {
|
||||
case value.IsArchive():
|
||||
return nil, fmt.Errorf("NYI: archives")
|
||||
case value.IsArray():
|
||||
elementType := schema.AnyType
|
||||
if typ, ok := typ.(*schema.ArrayType); ok {
|
||||
elementType = typ.ElementType
|
||||
}
|
||||
|
||||
arr := value.ArrayValue()
|
||||
exprs := make([]model.Expression, len(arr))
|
||||
for i, v := range arr {
|
||||
x, err := generateValue(elementType, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exprs[i] = x
|
||||
}
|
||||
return &model.TupleConsExpression{
|
||||
Tokens: syntax.NewTupleConsTokens(len(exprs)),
|
||||
Expressions: exprs,
|
||||
}, nil
|
||||
case value.IsAsset():
|
||||
return nil, fmt.Errorf("NYI: assets")
|
||||
case value.IsBool():
|
||||
return &model.LiteralValueExpression{
|
||||
Value: cty.BoolVal(value.BoolValue()),
|
||||
}, nil
|
||||
case value.IsComputed() || value.IsOutput():
|
||||
return nil, fmt.Errorf("cannot define computed values")
|
||||
case value.IsNull():
|
||||
return model.VariableReference(Null), nil
|
||||
case value.IsNumber():
|
||||
return &model.LiteralValueExpression{
|
||||
Value: cty.NumberFloatVal(value.NumberValue()),
|
||||
}, nil
|
||||
case value.IsObject():
|
||||
obj := value.ObjectValue()
|
||||
items := make([]model.ObjectConsItem, 0, len(obj))
|
||||
|
||||
if objectType, ok := typ.(*schema.ObjectType); ok {
|
||||
for _, p := range objectType.Properties {
|
||||
x, err := generatePropertyValue(p, obj[resource.PropertyKey(p.Name)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if x != nil {
|
||||
items = append(items, model.ObjectConsItem{
|
||||
Key: &model.LiteralValueExpression{
|
||||
Value: cty.StringVal(p.Name),
|
||||
},
|
||||
Value: x,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
elementType := schema.AnyType
|
||||
if mapType, ok := typ.(*schema.MapType); ok {
|
||||
elementType = mapType.ElementType
|
||||
}
|
||||
|
||||
for _, k := range obj.StableKeys() {
|
||||
// Ignore internal properties.
|
||||
if strings.HasPrefix(string(k), "__") {
|
||||
continue
|
||||
}
|
||||
|
||||
x, err := generateValue(elementType, obj[k])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, model.ObjectConsItem{
|
||||
Key: &model.LiteralValueExpression{
|
||||
Value: cty.StringVal(string(k)),
|
||||
},
|
||||
Value: x,
|
||||
})
|
||||
}
|
||||
}
|
||||
return &model.ObjectConsExpression{
|
||||
Tokens: syntax.NewObjectConsTokens(len(items)),
|
||||
Items: items,
|
||||
}, nil
|
||||
case value.IsSecret():
|
||||
arg, err := generateValue(typ, value.SecretValue().Element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.FunctionCallExpression{
|
||||
Name: "secret",
|
||||
Signature: model.StaticFunctionSignature{
|
||||
Parameters: []model.Parameter{{
|
||||
Name: "value",
|
||||
Type: arg.Type(),
|
||||
}},
|
||||
ReturnType: model.NewOutputType(arg.Type()),
|
||||
},
|
||||
Args: []model.Expression{arg},
|
||||
}, nil
|
||||
case value.IsString():
|
||||
return &model.TemplateExpression{
|
||||
Parts: []model.Expression{
|
||||
&model.LiteralValueExpression{
|
||||
Value: cty.StringVal(value.StringValue()),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
contract.Failf("unexpected property value %v", value)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
332
pkg/codegen/importer/hcl2_test.go
Normal file
332
pkg/codegen/importer/hcl2_test.go
Normal file
|
@ -0,0 +1,332 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package importer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/model"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/syntax"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/internal/test"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
||||
"github.com/pulumi/pulumi/pkg/v2/resource/deploy/providers"
|
||||
"github.com/pulumi/pulumi/pkg/v2/resource/stack"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/apitype"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/config"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/tokens"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
var testdataPath = filepath.Join("..", "internal", "test", "testdata")
|
||||
|
||||
const parentName = "parent"
|
||||
const providerName = "provider"
|
||||
|
||||
var parentURN = resource.NewURN("stack", "project", "", "my::parent", "parent")
|
||||
var providerURN = resource.NewURN("stack", "project", "", providers.MakeProviderType("pkg"), "provider")
|
||||
|
||||
var names = NameTable{
|
||||
parentURN: parentName,
|
||||
providerURN: providerName,
|
||||
}
|
||||
|
||||
func renderExpr(t *testing.T, x model.Expression) resource.PropertyValue {
|
||||
switch x := x.(type) {
|
||||
case *model.LiteralValueExpression:
|
||||
return renderLiteralValue(t, x)
|
||||
case *model.ScopeTraversalExpression:
|
||||
return renderScopeTraversal(t, x)
|
||||
case *model.TemplateExpression:
|
||||
return renderTemplate(t, x)
|
||||
case *model.TupleConsExpression:
|
||||
return renderTupleCons(t, x)
|
||||
case *model.ObjectConsExpression:
|
||||
return renderObjectCons(t, x)
|
||||
case *model.FunctionCallExpression:
|
||||
return renderFunctionCall(t, x)
|
||||
default:
|
||||
assert.Failf(t, "", "unexpected expression of type %T", x)
|
||||
return resource.NewNullProperty()
|
||||
}
|
||||
}
|
||||
|
||||
func renderLiteralValue(t *testing.T, x *model.LiteralValueExpression) resource.PropertyValue {
|
||||
switch x.Value.Type() {
|
||||
case cty.Bool:
|
||||
return resource.NewBoolProperty(x.Value.True())
|
||||
case cty.Number:
|
||||
f, _ := x.Value.AsBigFloat().Float64()
|
||||
return resource.NewNumberProperty(f)
|
||||
case cty.String:
|
||||
return resource.NewStringProperty(x.Value.AsString())
|
||||
default:
|
||||
assert.Failf(t, "", "unexpected literal of type %v", x.Value.Type())
|
||||
return resource.NewNullProperty()
|
||||
}
|
||||
}
|
||||
|
||||
func renderTemplate(t *testing.T, x *model.TemplateExpression) resource.PropertyValue {
|
||||
if !assert.Len(t, x.Parts, 1) {
|
||||
return resource.NewStringProperty("")
|
||||
}
|
||||
return renderLiteralValue(t, x.Parts[0].(*model.LiteralValueExpression))
|
||||
}
|
||||
|
||||
func renderObjectCons(t *testing.T, x *model.ObjectConsExpression) resource.PropertyValue {
|
||||
obj := resource.PropertyMap{}
|
||||
for _, item := range x.Items {
|
||||
kv := renderExpr(t, item.Key)
|
||||
if !assert.True(t, kv.IsString()) {
|
||||
continue
|
||||
}
|
||||
obj[resource.PropertyKey(kv.StringValue())] = renderExpr(t, item.Value)
|
||||
}
|
||||
return resource.NewObjectProperty(obj)
|
||||
}
|
||||
|
||||
func renderScopeTraversal(t *testing.T, x *model.ScopeTraversalExpression) resource.PropertyValue {
|
||||
if !assert.Len(t, x.Traversal, 1) {
|
||||
return resource.NewNullProperty()
|
||||
}
|
||||
|
||||
switch x.RootName {
|
||||
case "parent":
|
||||
return resource.NewStringProperty(string(parentURN))
|
||||
case "provider":
|
||||
return resource.NewStringProperty(string(providerURN))
|
||||
default:
|
||||
assert.Failf(t, "", "unexpected variable reference %v", x.RootName)
|
||||
return resource.NewNullProperty()
|
||||
}
|
||||
}
|
||||
|
||||
func renderTupleCons(t *testing.T, x *model.TupleConsExpression) resource.PropertyValue {
|
||||
arr := make([]resource.PropertyValue, len(x.Expressions))
|
||||
for i, x := range x.Expressions {
|
||||
arr[i] = renderExpr(t, x)
|
||||
}
|
||||
return resource.NewArrayProperty(arr)
|
||||
}
|
||||
|
||||
func renderFunctionCall(t *testing.T, x *model.FunctionCallExpression) resource.PropertyValue {
|
||||
switch x.Name {
|
||||
case "secret":
|
||||
if !assert.Len(t, x.Args, 1) {
|
||||
return resource.NewNullProperty()
|
||||
}
|
||||
return resource.MakeSecret(renderExpr(t, x.Args[0]))
|
||||
default:
|
||||
assert.Failf(t, "", "unexpected call to %v", x.Name)
|
||||
return resource.NewNullProperty()
|
||||
}
|
||||
}
|
||||
|
||||
func renderResource(t *testing.T, r *hcl2.Resource) *resource.State {
|
||||
inputs := resource.PropertyMap{}
|
||||
for _, attr := range r.Inputs {
|
||||
inputs[resource.PropertyKey(attr.Name)] = renderExpr(t, attr.Value)
|
||||
}
|
||||
|
||||
protect := false
|
||||
var parent resource.URN
|
||||
var providerRef string
|
||||
if r.Options != nil {
|
||||
if r.Options.Protect != nil {
|
||||
v, diags := r.Options.Protect.Evaluate(&hcl.EvalContext{})
|
||||
if assert.Len(t, diags, 0) && assert.Equal(t, cty.Bool, v.Type()) {
|
||||
protect = v.True()
|
||||
}
|
||||
}
|
||||
if r.Options.Parent != nil {
|
||||
v := renderExpr(t, r.Options.Parent)
|
||||
if assert.True(t, v.IsString()) {
|
||||
parent = resource.URN(v.StringValue())
|
||||
}
|
||||
}
|
||||
if r.Options.Provider != nil {
|
||||
v := renderExpr(t, r.Options.Provider)
|
||||
if assert.True(t, v.IsString()) {
|
||||
providerRef = v.StringValue() + "::id"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pull the raw token from the resource.
|
||||
token := tokens.Type(r.Definition.Labels[1])
|
||||
|
||||
var parentType tokens.Type
|
||||
if parent != "" {
|
||||
parentType = parent.QualifiedType()
|
||||
}
|
||||
return &resource.State{
|
||||
Type: token,
|
||||
URN: resource.NewURN("stack", "project", parentType, token, tokens.QName(r.Name())),
|
||||
Custom: true,
|
||||
Inputs: inputs,
|
||||
Parent: parent,
|
||||
Provider: providerRef,
|
||||
Protect: protect,
|
||||
}
|
||||
}
|
||||
|
||||
type testCases struct {
|
||||
Resources []apitype.ResourceV3 `json:"resources"`
|
||||
}
|
||||
|
||||
func readTestCases(path string) (testCases, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return testCases{}, err
|
||||
}
|
||||
defer contract.IgnoreClose(f)
|
||||
|
||||
var cases testCases
|
||||
if err = json.NewDecoder(f).Decode(&cases); err != nil {
|
||||
return testCases{}, err
|
||||
}
|
||||
return cases, nil
|
||||
}
|
||||
|
||||
func TestGenerateHCL2Definition(t *testing.T) {
|
||||
loader := schema.NewPluginLoader(test.NewHost(testdataPath))
|
||||
|
||||
cases, err := readTestCases("testdata/cases.json")
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
for _, s := range cases.Resources {
|
||||
t.Run(string(s.URN), func(t *testing.T) {
|
||||
state, err := stack.DeserializeResource(s, config.NopDecrypter, config.NopEncrypter)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
block, err := GenerateHCL2Definition(loader, state, names)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("%v", block)
|
||||
|
||||
parser := syntax.NewParser()
|
||||
err = parser.ParseFile(strings.NewReader(text), string(state.URN)+".pp")
|
||||
if !assert.NoError(t, err) || !assert.False(t, parser.Diagnostics.HasErrors()) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
p, diags, err := hcl2.BindProgram(parser.Files, hcl2.Loader(loader), hcl2.AllowMissingVariables)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, diags.HasErrors())
|
||||
|
||||
if !assert.Len(t, p.Nodes, 1) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
res, isResource := p.Nodes[0].(*hcl2.Resource)
|
||||
if !assert.True(t, isResource) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
actualState := renderResource(t, res)
|
||||
|
||||
assert.Equal(t, state.Type, actualState.Type)
|
||||
assert.Equal(t, state.URN, actualState.URN)
|
||||
assert.Equal(t, state.Parent, actualState.Parent)
|
||||
assert.Equal(t, state.Provider, actualState.Provider)
|
||||
assert.Equal(t, state.Protect, actualState.Protect)
|
||||
if !assert.True(t, actualState.Inputs.DeepEquals(state.Inputs)) {
|
||||
actual, err := stack.SerializeResource(actualState, config.NopEncrypter, false)
|
||||
contract.IgnoreError(err)
|
||||
|
||||
sb, err := json.MarshalIndent(s, "", " ")
|
||||
contract.IgnoreError(err)
|
||||
|
||||
ab, err := json.MarshalIndent(actual, "", " ")
|
||||
contract.IgnoreError(err)
|
||||
|
||||
t.Logf("%v\n\n%v\n\n%v\n", text, string(sb), string(ab))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimplerType(t *testing.T) {
|
||||
types := []schema.Type{
|
||||
schema.BoolType,
|
||||
schema.IntType,
|
||||
schema.NumberType,
|
||||
schema.StringType,
|
||||
schema.AssetType,
|
||||
schema.ArchiveType,
|
||||
schema.JSONType,
|
||||
&schema.ArrayType{ElementType: schema.BoolType},
|
||||
&schema.ArrayType{ElementType: schema.IntType},
|
||||
&schema.MapType{ElementType: schema.BoolType},
|
||||
&schema.MapType{ElementType: schema.IntType},
|
||||
&schema.ObjectType{},
|
||||
&schema.ObjectType{
|
||||
Properties: []*schema.Property{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: schema.BoolType,
|
||||
IsRequired: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&schema.ObjectType{
|
||||
Properties: []*schema.Property{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: schema.IntType,
|
||||
IsRequired: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&schema.ObjectType{
|
||||
Properties: []*schema.Property{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: schema.IntType,
|
||||
IsRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Type: schema.IntType,
|
||||
IsRequired: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&schema.UnionType{ElementTypes: []schema.Type{schema.BoolType, schema.IntType}},
|
||||
&schema.UnionType{ElementTypes: []schema.Type{schema.IntType, schema.JSONType}},
|
||||
&schema.UnionType{ElementTypes: []schema.Type{schema.NumberType, schema.StringType}},
|
||||
schema.AnyType,
|
||||
}
|
||||
|
||||
assert.True(t, sort.SliceIsSorted(types, func(i, j int) bool {
|
||||
return simplerType(types[i], types[j])
|
||||
}))
|
||||
}
|
108
pkg/codegen/importer/language.go
Normal file
108
pkg/codegen/importer/language.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package importer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/syntax"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
)
|
||||
|
||||
// A LangaugeGenerator generates code for a given Pulumi program to an io.Writer.
|
||||
type LanguageGenerator func(w io.Writer, p *hcl2.Program) error
|
||||
|
||||
// A NameTable maps URNs to language-specific variable names.
|
||||
type NameTable map[resource.URN]string
|
||||
|
||||
// A DiagnosticsError captures HCL2 diagnostics.
|
||||
type DiagnosticsError struct {
|
||||
diagnostics hcl.Diagnostics
|
||||
newDiagnosticWriter func(w io.Writer, width uint, color bool) hcl.DiagnosticWriter
|
||||
}
|
||||
|
||||
func (e *DiagnosticsError) Diagnostics() hcl.Diagnostics {
|
||||
return e.diagnostics
|
||||
}
|
||||
|
||||
// NewDiagnosticWriter returns an hcl.DiagnosticWriter that can be used to render the error's diagnostics.
|
||||
func (e *DiagnosticsError) NewDiagnosticWriter(w io.Writer, width uint, color bool) hcl.DiagnosticWriter {
|
||||
return e.newDiagnosticWriter(w, width, color)
|
||||
}
|
||||
|
||||
func (e *DiagnosticsError) Error() string {
|
||||
var text bytes.Buffer
|
||||
err := e.NewDiagnosticWriter(&text, 0, false).WriteDiagnostics(e.diagnostics)
|
||||
contract.IgnoreError(err)
|
||||
return text.String()
|
||||
}
|
||||
|
||||
func (e *DiagnosticsError) String() string {
|
||||
return e.Error()
|
||||
}
|
||||
|
||||
// GenerateLanguageDefintions generates a list of resource definitions from the given resource states.
|
||||
func GenerateLanguageDefinitions(w io.Writer, loader schema.Loader, gen LanguageGenerator, states []*resource.State,
|
||||
names NameTable) error {
|
||||
|
||||
var hcl2Text bytes.Buffer
|
||||
for i, state := range states {
|
||||
hcl2Def, err := GenerateHCL2Definition(loader, state, names)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pre := ""
|
||||
if i > 0 {
|
||||
pre = "\n"
|
||||
}
|
||||
_, err = fmt.Fprintf(&hcl2Text, "%s%v", pre, hcl2Def)
|
||||
contract.IgnoreError(err)
|
||||
}
|
||||
|
||||
parser := syntax.NewParser()
|
||||
if err := parser.ParseFile(&hcl2Text, string("anonymous.pp")); err != nil {
|
||||
return err
|
||||
}
|
||||
if parser.Diagnostics.HasErrors() {
|
||||
// HCL2 text generation should always generate proper code.
|
||||
return fmt.Errorf("internal error: %w", &DiagnosticsError{
|
||||
diagnostics: parser.Diagnostics,
|
||||
newDiagnosticWriter: parser.NewDiagnosticWriter,
|
||||
})
|
||||
}
|
||||
|
||||
program, diags, err := hcl2.BindProgram(parser.Files, hcl2.Loader(loader), hcl2.AllowMissingVariables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
// It is possible that the provided states do not contain appropriately-shaped inputs, so this may be user
|
||||
// error.
|
||||
return &DiagnosticsError{
|
||||
diagnostics: diags,
|
||||
newDiagnosticWriter: program.NewDiagnosticWriter,
|
||||
}
|
||||
}
|
||||
|
||||
return gen(w, program)
|
||||
}
|
85
pkg/codegen/importer/language_test.go
Normal file
85
pkg/codegen/importer/language_test.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package importer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/internal/test"
|
||||
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
||||
"github.com/pulumi/pulumi/pkg/v2/resource/stack"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/config"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerateLanguageDefinition(t *testing.T) {
|
||||
loader := schema.NewPluginLoader(test.NewHost(testdataPath))
|
||||
|
||||
cases, err := readTestCases("testdata/cases.json")
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
for _, s := range cases.Resources {
|
||||
t.Run(string(s.URN), func(t *testing.T) {
|
||||
state, err := stack.DeserializeResource(s, config.NopDecrypter, config.NopEncrypter)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
var actualState *resource.State
|
||||
err = GenerateLanguageDefinitions(ioutil.Discard, loader, func(_ io.Writer, p *hcl2.Program) error {
|
||||
if !assert.Len(t, p.Nodes, 1) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
res, isResource := p.Nodes[0].(*hcl2.Resource)
|
||||
if !assert.True(t, isResource) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
actualState = renderResource(t, res)
|
||||
return nil
|
||||
}, []*resource.State{state}, names)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
assert.Equal(t, state.Type, actualState.Type)
|
||||
assert.Equal(t, state.URN, actualState.URN)
|
||||
assert.Equal(t, state.Parent, actualState.Parent)
|
||||
assert.Equal(t, state.Provider, actualState.Provider)
|
||||
assert.Equal(t, state.Protect, actualState.Protect)
|
||||
if !assert.True(t, actualState.Inputs.DeepEquals(state.Inputs)) {
|
||||
actual, err := stack.SerializeResource(actualState, config.NopEncrypter, false)
|
||||
contract.IgnoreError(err)
|
||||
|
||||
sb, err := json.MarshalIndent(s, "", " ")
|
||||
contract.IgnoreError(err)
|
||||
|
||||
ab, err := json.MarshalIndent(actual, "", " ")
|
||||
contract.IgnoreError(err)
|
||||
|
||||
t.Logf("%v\n\n%v\n", string(sb), string(ab))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
1561
pkg/codegen/importer/testdata/cases.json
vendored
Normal file
1561
pkg/codegen/importer/testdata/cases.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -478,9 +478,6 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
|
|||
if r.IsProvider {
|
||||
trailingBrace, optionsType = " {", "ResourceOptions"
|
||||
}
|
||||
if r.StateInputs == nil {
|
||||
trailingBrace = " {"
|
||||
}
|
||||
|
||||
if r.DeprecationMessage != "" {
|
||||
fmt.Fprintf(w, " /** @deprecated %s */\n", r.DeprecationMessage)
|
||||
|
@ -498,27 +495,34 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
|
|||
// conditional state into sensible variables using dynamic type tests.
|
||||
fmt.Fprintf(w, " constructor(name: string, argsOrState?: %s | %s, opts?: pulumi.CustomResourceOptions) {\n",
|
||||
argsType, stateType)
|
||||
} else {
|
||||
// Otherwise, write out a constructor with no state and required opts, then another with all optional params.
|
||||
fmt.Fprintf(w, " constructor(name: string, state: undefined, opts: pulumi.CustomResourceOptions)\n")
|
||||
fmt.Fprintf(w, " constructor(name: string, argsOrState?: %s, opts?: pulumi.CustomResourceOptions) {\n",
|
||||
argsType)
|
||||
}
|
||||
if r.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
|
||||
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, r.DeprecationMessage)
|
||||
}
|
||||
fmt.Fprintf(w, " let inputs: pulumi.Inputs = {};\n")
|
||||
|
||||
if r.StateInputs != nil {
|
||||
// The lookup case:
|
||||
fmt.Fprintf(w, " if (opts && opts.id) {\n")
|
||||
fmt.Fprintf(w, " const state = argsOrState as %[1]s | undefined;\n", stateType)
|
||||
for _, prop := range r.Properties {
|
||||
for _, prop := range r.StateInputs.Properties {
|
||||
fmt.Fprintf(w, " inputs[\"%[1]s\"] = state ? state.%[1]s : undefined;\n", prop.Name)
|
||||
}
|
||||
// The creation case (with args):
|
||||
fmt.Fprintf(w, " } else {\n")
|
||||
fmt.Fprintf(w, " const args = argsOrState as %s | undefined;\n", argsType)
|
||||
} else {
|
||||
// The creation case:
|
||||
fmt.Fprintf(w, " if (!(opts && opts.id)) {\n")
|
||||
}
|
||||
fmt.Fprintf(w, " const args = argsOrState as %s | undefined;\n", argsType)
|
||||
} else {
|
||||
fmt.Fprintf(w, " let inputs: pulumi.Inputs = {};\n")
|
||||
if r.StateInputs != nil {
|
||||
fmt.Fprintf(w, " {\n")
|
||||
}
|
||||
fmt.Fprintf(w, " {\n")
|
||||
}
|
||||
for _, prop := range r.InputProperties {
|
||||
if prop.IsRequired {
|
||||
|
@ -531,10 +535,6 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
|
|||
arg := fmt.Sprintf("args ? args.%[1]s : undefined", prop.Name)
|
||||
|
||||
prefix := " "
|
||||
if r.StateInputs == nil {
|
||||
prefix = " "
|
||||
}
|
||||
|
||||
if prop.ConstValue != nil {
|
||||
cv, err := mod.getConstValue(prop.ConstValue)
|
||||
if err != nil {
|
||||
|
@ -566,9 +566,6 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
|
|||
var secretProps []string
|
||||
for _, prop := range r.Properties {
|
||||
prefix := " "
|
||||
if r.StateInputs == nil {
|
||||
prefix = " "
|
||||
}
|
||||
if !ins.Has(prop.Name) {
|
||||
fmt.Fprintf(w, "%sinputs[\"%s\"] = undefined /*out*/;\n", prefix, prop.Name)
|
||||
}
|
||||
|
@ -577,9 +574,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
|
|||
secretProps = append(secretProps, prop.Name)
|
||||
}
|
||||
}
|
||||
if r.StateInputs != nil {
|
||||
fmt.Fprintf(w, " }\n")
|
||||
}
|
||||
fmt.Fprintf(w, " }\n")
|
||||
|
||||
// If the caller didn't request a specific version, supply one using the version of this library.
|
||||
fmt.Fprintf(w, " if (!opts) {\n")
|
||||
|
@ -1059,8 +1054,10 @@ func (mod *modContext) isReservedSourceFileName(name string) bool {
|
|||
func (mod *modContext) gen(fs fs) error {
|
||||
files := append([]string(nil), mod.extraSourceFiles...)
|
||||
|
||||
modDir := strings.ToLower(mod.mod)
|
||||
|
||||
addFile := func(name, contents string) {
|
||||
p := path.Join(mod.mod, name)
|
||||
p := path.Join(modDir, name)
|
||||
files = append(files, p)
|
||||
fs.add(p, []byte(contents))
|
||||
}
|
||||
|
@ -1071,7 +1068,7 @@ func (mod *modContext) gen(fs fs) error {
|
|||
buffer := &bytes.Buffer{}
|
||||
mod.genHeader(buffer, nil, nil)
|
||||
fmt.Fprintf(buffer, "%s", utilitiesFile)
|
||||
fs.add(path.Join(mod.mod, "utilities.ts"), buffer.Bytes())
|
||||
fs.add(path.Join(modDir, "utilities.ts"), buffer.Bytes())
|
||||
|
||||
// Ensure that the top-level (provider) module directory contains a README.md file.
|
||||
readme := mod.pkg.Language["nodejs"].(NodePackageInfo).Readme
|
||||
|
@ -1090,7 +1087,7 @@ func (mod *modContext) gen(fs fs) error {
|
|||
if readme != "" && readme[len(readme)-1] != '\n' {
|
||||
readme += "\n"
|
||||
}
|
||||
fs.add(path.Join(mod.mod, "README.md"), []byte(readme))
|
||||
fs.add(path.Join(modDir, "README.md"), []byte(readme))
|
||||
case "config":
|
||||
if len(mod.pkg.Config) > 0 {
|
||||
buffer := &bytes.Buffer{}
|
||||
|
@ -1140,12 +1137,12 @@ func (mod *modContext) gen(fs fs) error {
|
|||
// Nested types
|
||||
if len(mod.types) > 0 {
|
||||
input, output := mod.genTypes()
|
||||
fs.add(path.Join(mod.mod, "input.ts"), []byte(input))
|
||||
fs.add(path.Join(mod.mod, "output.ts"), []byte(output))
|
||||
fs.add(path.Join(modDir, "input.ts"), []byte(input))
|
||||
fs.add(path.Join(modDir, "output.ts"), []byte(output))
|
||||
}
|
||||
|
||||
// Index
|
||||
fs.add(path.Join(mod.mod, "index.ts"), []byte(mod.genIndex(files)))
|
||||
fs.add(path.Join(modDir, "index.ts"), []byte(mod.genIndex(files)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1156,10 +1153,11 @@ func (mod *modContext) genIndex(exports []string) string {
|
|||
|
||||
// Export anything flatly that is a direct export rather than sub-module.
|
||||
if len(exports) > 0 {
|
||||
modDir := strings.ToLower(mod.mod)
|
||||
fmt.Fprintf(w, "// Export members:\n")
|
||||
sort.Strings(exports)
|
||||
for _, exp := range exports {
|
||||
rel, err := filepath.Rel(mod.mod, exp)
|
||||
rel, err := filepath.Rel(modDir, exp)
|
||||
contract.Assert(err == nil)
|
||||
if path.Base(rel) == "." {
|
||||
rel = path.Dir(rel)
|
||||
|
@ -1171,7 +1169,7 @@ func (mod *modContext) genIndex(exports []string) string {
|
|||
children := codegen.NewStringSet()
|
||||
|
||||
for _, mod := range mod.children {
|
||||
child := mod.mod
|
||||
child := strings.ToLower(mod.mod)
|
||||
if mod.compatibility == kubernetes20 {
|
||||
// Extract version suffix from child modules. Nested versions will have their own index.ts file.
|
||||
// Example: apps/v1beta1 -> v1beta1
|
||||
|
@ -1238,7 +1236,8 @@ type npmPackage struct {
|
|||
}
|
||||
|
||||
type npmPulumiManifest struct {
|
||||
Resource bool `json:"resource,omitempty"`
|
||||
Resource bool `json:"resource,omitempty"`
|
||||
PluginDownloadURL string `json:"pluginDownloadURL,omitempty"`
|
||||
}
|
||||
|
||||
func genNPMPackageMetadata(pkg *schema.Package, info NodePackageInfo) string {
|
||||
|
@ -1270,7 +1269,8 @@ func genNPMPackageMetadata(pkg *schema.Package, info NodePackageInfo) string {
|
|||
},
|
||||
DevDependencies: devDependencies,
|
||||
Pulumi: npmPulumiManifest{
|
||||
Resource: true,
|
||||
Resource: true,
|
||||
PluginDownloadURL: pkg.PluginDownloadURL,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -39,22 +39,28 @@ func (d DocLanguageHelper) GetDocLinkForPulumiType(pkg *schema.Package, typeName
|
|||
|
||||
// GetDocLinkForResourceType returns the Python API doc for a type belonging to a resource provider.
|
||||
func (d DocLanguageHelper) GetDocLinkForResourceType(pkg *schema.Package, modName, typeName string) string {
|
||||
var path string
|
||||
// The k8s module names contain the domain names. For now we are stripping them off manually so they link correctly.
|
||||
if modName != "" {
|
||||
modName = strings.ReplaceAll(modName, ".k8s.io", "")
|
||||
modName = strings.ReplaceAll(modName, ".apiserver", "")
|
||||
modName = strings.ReplaceAll(modName, ".authorization", "")
|
||||
}
|
||||
|
||||
var path string
|
||||
var fqdnTypeName string
|
||||
switch {
|
||||
case pkg.Name != "" && modName != "":
|
||||
path = fmt.Sprintf("pulumi_%s/%s", pkg.Name, modName)
|
||||
fqdnTypeName = fmt.Sprintf("pulumi_%s.%s.%s", pkg.Name, modName, typeName)
|
||||
case pkg.Name == "" && modName != "":
|
||||
path = modName
|
||||
fqdnTypeName = fmt.Sprintf("%s.%s", modName, typeName)
|
||||
case pkg.Name != "" && modName == "":
|
||||
path = pkg.Name
|
||||
path = fmt.Sprintf("pulumi_%s", pkg.Name)
|
||||
fqdnTypeName = fmt.Sprintf("pulumi_%s.%s", pkg.Name, typeName)
|
||||
}
|
||||
return fmt.Sprintf("/docs/reference/pkg/python/%s/#%s", path, typeName)
|
||||
|
||||
return fmt.Sprintf("/docs/reference/pkg/python/%s/#%s", path, fqdnTypeName)
|
||||
}
|
||||
|
||||
// GetDocLinkForResourceInputOrOutputType is not implemented at this time for Python.
|
||||
|
|
|
@ -128,7 +128,7 @@ func (mod *modContext) genHeader(w io.Writer, needsSDK bool, needsJSON bool) {
|
|||
fmt.Fprintf(w, "import pulumi\n")
|
||||
fmt.Fprintf(w, "import pulumi.runtime\n")
|
||||
fmt.Fprintf(w, "from typing import Union\n")
|
||||
fmt.Fprintf(w, "from %s import utilities, tables\n", relImport)
|
||||
fmt.Fprintf(w, "from %s import _utilities, _tables\n", relImport)
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,8 @@ func (mod *modContext) gen(fs fs) error {
|
|||
buffer := &bytes.Buffer{}
|
||||
mod.genHeader(buffer, false, false)
|
||||
fmt.Fprintf(buffer, "%s", utilitiesFile)
|
||||
fs.add(filepath.Join(dir, "utilities.py"), buffer.Bytes())
|
||||
fs.add(filepath.Join(dir, "_utilities.py"), buffer.Bytes())
|
||||
fs.add(filepath.Join(dir, "py.typed"), []byte{})
|
||||
|
||||
// Ensure that the top-level (provider) module directory contains a README.md file.
|
||||
readme := mod.pkg.Language["python"].(PackageInfo).Readme
|
||||
|
@ -248,13 +249,7 @@ func (mod *modContext) gen(fs fs) error {
|
|||
}
|
||||
|
||||
func (mod *modContext) submodulesExist() bool {
|
||||
if len(mod.children) <= 0 {
|
||||
return false
|
||||
}
|
||||
if len(mod.children) == 1 && mod.children[0].mod == "config" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return len(mod.children) > 0
|
||||
}
|
||||
|
||||
// genInit emits an __init__.py module, optionally re-exporting other members or submodules.
|
||||
|
@ -262,10 +257,6 @@ func (mod *modContext) genInit(exports []string) string {
|
|||
w := &bytes.Buffer{}
|
||||
mod.genHeader(w, false, false)
|
||||
|
||||
if mod.submodulesExist() {
|
||||
fmt.Fprintf(w, "import importlib\n")
|
||||
}
|
||||
|
||||
// Import anything to export flatly that is a direct export rather than sub-module.
|
||||
if len(exports) > 0 {
|
||||
sort.Slice(exports, func(i, j int) bool {
|
||||
|
@ -291,8 +282,8 @@ func (mod *modContext) genInit(exports []string) string {
|
|||
})
|
||||
|
||||
fmt.Fprintf(w, "\n# Make subpackages available:\n")
|
||||
fmt.Fprintf(w, "_submodules = [\n")
|
||||
for i, mod := range mod.children {
|
||||
fmt.Fprintf(w, "from . import (\n")
|
||||
for _, mod := range mod.children {
|
||||
child := mod.mod
|
||||
if mod.compatibility == kubernetes20 {
|
||||
// Extract version suffix from child modules. Nested versions will have their own __init__.py file.
|
||||
|
@ -301,15 +292,9 @@ func (mod *modContext) genInit(exports []string) string {
|
|||
child = child[match[2]:match[3]]
|
||||
}
|
||||
}
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, ",\n")
|
||||
}
|
||||
fmt.Fprintf(w, " '%s'", PyName(child))
|
||||
fmt.Fprintf(w, " %s,\n", PyName(child))
|
||||
}
|
||||
fmt.Fprintf(w, ",\n]\n")
|
||||
fmt.Fprintf(w, "for pkg in _submodules:\n")
|
||||
fmt.Fprintf(w, " if pkg != 'config':\n")
|
||||
fmt.Fprintf(w, " importlib.import_module(f'{__name__}.{pkg}')\n")
|
||||
fmt.Fprintf(w, ")\n")
|
||||
}
|
||||
|
||||
return w.String()
|
||||
|
@ -347,6 +332,7 @@ func (mod *modContext) genAwaitableType(w io.Writer, obj *schema.ObjectType) str
|
|||
baseName := pyClassName(tokenToName(obj.Token))
|
||||
|
||||
// Produce a class definition with optional """ comment.
|
||||
fmt.Fprint(w, "\n")
|
||||
fmt.Fprintf(w, "class %s:\n", baseName)
|
||||
printComment(w, obj.Comment, " ")
|
||||
|
||||
|
@ -378,6 +364,7 @@ func (mod *modContext) genAwaitableType(w io.Writer, obj *schema.ObjectType) str
|
|||
awaitableName := "Awaitable" + baseName
|
||||
|
||||
// Produce an awaitable subclass.
|
||||
fmt.Fprint(w, "\n\n")
|
||||
fmt.Fprintf(w, "class %s(%s):\n", awaitableName, baseName)
|
||||
|
||||
// Emit __await__ and __iter__ in order to make this type awaitable.
|
||||
|
@ -438,7 +425,8 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
|
|||
}
|
||||
|
||||
// Produce a class definition with optional """ comment.
|
||||
fmt.Fprintf(w, "\nclass %s(%s):\n", name, baseType)
|
||||
fmt.Fprint(w, "\n")
|
||||
fmt.Fprintf(w, "class %s(%s):\n", name, baseType)
|
||||
for _, prop := range res.Properties {
|
||||
name := PyName(prop.Name)
|
||||
ty := pyType(prop.Type)
|
||||
|
@ -493,7 +481,7 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
|
|||
fmt.Fprintf(w, " if not isinstance(opts, pulumi.ResourceOptions):\n")
|
||||
fmt.Fprintf(w, " raise TypeError('Expected resource options to be a ResourceOptions instance')\n")
|
||||
fmt.Fprintf(w, " if opts.version is None:\n")
|
||||
fmt.Fprintf(w, " opts.version = utilities.get_version()\n")
|
||||
fmt.Fprintf(w, " opts.version = _utilities.get_version()\n")
|
||||
fmt.Fprintf(w, " if opts.id is None:\n")
|
||||
fmt.Fprintf(w, " if __props__ is not None:\n")
|
||||
fmt.Fprintf(w, " raise TypeError(")
|
||||
|
@ -637,10 +625,10 @@ func (mod *modContext) genResource(res *schema.Resource) (string, error) {
|
|||
// camel case when interacting with tfbridge.
|
||||
fmt.Fprintf(w,
|
||||
` def translate_output_property(self, prop):
|
||||
return tables._CAMEL_TO_SNAKE_CASE_TABLE.get(prop) or prop
|
||||
return _tables.CAMEL_TO_SNAKE_CASE_TABLE.get(prop) or prop
|
||||
|
||||
def translate_input_property(self, prop):
|
||||
return tables._SNAKE_TO_CAMEL_CASE_TABLE.get(prop) or prop
|
||||
return _tables.SNAKE_TO_CAMEL_CASE_TABLE.get(prop) or prop
|
||||
`)
|
||||
|
||||
return w.String(), nil
|
||||
|
@ -693,9 +681,10 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) {
|
|||
}
|
||||
|
||||
// Write out the function signature.
|
||||
fmt.Fprint(w, "\n")
|
||||
fmt.Fprintf(w, "def %s(", name)
|
||||
for _, arg := range args {
|
||||
fmt.Fprintf(w, "%s=None,", PyName(arg.Name))
|
||||
fmt.Fprintf(w, "%s=None, ", PyName(arg.Name))
|
||||
}
|
||||
fmt.Fprintf(w, "opts=None")
|
||||
fmt.Fprintf(w, "):\n")
|
||||
|
@ -723,8 +712,7 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) {
|
|||
}
|
||||
|
||||
// Copy the function arguments into a dictionary.
|
||||
fmt.Fprintf(w, " __args__ = dict()\n\n")
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, " __args__ = dict()\n")
|
||||
for _, arg := range args {
|
||||
// TODO: args validation.
|
||||
fmt.Fprintf(w, " __args__['%s'] = %s\n", arg.Name, PyName(arg.Name))
|
||||
|
@ -734,7 +722,7 @@ func (mod *modContext) genFunction(fun *schema.Function) (string, error) {
|
|||
fmt.Fprintf(w, " if opts is None:\n")
|
||||
fmt.Fprintf(w, " opts = pulumi.InvokeOptions()\n")
|
||||
fmt.Fprintf(w, " if opts.version is None:\n")
|
||||
fmt.Fprintf(w, " opts.version = utilities.get_version()\n")
|
||||
fmt.Fprintf(w, " opts.version = _utilities.get_version()\n")
|
||||
|
||||
// Now simply invoke the runtime function with the arguments.
|
||||
fmt.Fprintf(w, " __ret__ = pulumi.runtime.invoke('%s', __args__, opts=opts).value\n", fun.Token)
|
||||
|
@ -793,7 +781,11 @@ func genPackageMetadata(tool string, pkg *schema.Package, requires map[string]st
|
|||
fmt.Fprintf(w, " def run(self):\n")
|
||||
fmt.Fprintf(w, " install.run(self)\n")
|
||||
fmt.Fprintf(w, " try:\n")
|
||||
fmt.Fprintf(w, " check_call(['pulumi', 'plugin', 'install', 'resource', '%s', '${PLUGIN_VERSION}'])\n", pkg.Name)
|
||||
if pkg.PluginDownloadURL == "" {
|
||||
fmt.Fprintf(w, " check_call(['pulumi', 'plugin', 'install', 'resource', '%s', '${PLUGIN_VERSION}'])\n", pkg.Name)
|
||||
} else {
|
||||
fmt.Fprintf(w, " check_call(['pulumi', 'plugin', 'install', 'resource', '%s', '${PLUGIN_VERSION}', '--server', '%s'])\n", pkg.Name, pkg.PluginDownloadURL)
|
||||
}
|
||||
fmt.Fprintf(w, " except OSError as error:\n")
|
||||
fmt.Fprintf(w, " if error.errno == errno.ENOENT:\n")
|
||||
fmt.Fprintf(w, " print(\"\"\"\n")
|
||||
|
@ -933,7 +925,7 @@ func (mod *modContext) genPropertyConversionTables() string {
|
|||
}
|
||||
sort.Strings(allKeys)
|
||||
|
||||
fmt.Fprintf(w, "_SNAKE_TO_CAMEL_CASE_TABLE = {\n")
|
||||
fmt.Fprintf(w, "SNAKE_TO_CAMEL_CASE_TABLE = {\n")
|
||||
for _, key := range allKeys {
|
||||
value := mod.snakeCaseToCamelCase[key]
|
||||
if key != value {
|
||||
|
@ -941,7 +933,7 @@ func (mod *modContext) genPropertyConversionTables() string {
|
|||
}
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
fmt.Fprintf(w, "\n_CAMEL_TO_SNAKE_CASE_TABLE = {\n")
|
||||
fmt.Fprintf(w, "\nCAMEL_TO_SNAKE_CASE_TABLE = {\n")
|
||||
for _, value := range allKeys {
|
||||
key := mod.snakeCaseToCamelCase[value]
|
||||
if key != value {
|
||||
|
@ -1297,14 +1289,14 @@ func getDefaultValue(dv *schema.DefaultValue, t schema.Type) (string, error) {
|
|||
}
|
||||
|
||||
if len(dv.Environment) > 0 {
|
||||
envFunc := "utilities.get_env"
|
||||
envFunc := "_utilities.get_env"
|
||||
switch t {
|
||||
case schema.BoolType:
|
||||
envFunc = "utilities.get_env_bool"
|
||||
envFunc = "_utilities.get_env_bool"
|
||||
case schema.IntType:
|
||||
envFunc = "utilities.get_env_int"
|
||||
envFunc = "_utilities.get_env_int"
|
||||
case schema.NumberType:
|
||||
envFunc = "utilities.get_env_float"
|
||||
envFunc = "_utilities.get_env_float"
|
||||
}
|
||||
|
||||
envVars := fmt.Sprintf("'%s'", dv.Environment[0])
|
||||
|
@ -1360,7 +1352,7 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo
|
|||
|
||||
getModFromToken := func(token string) *modContext {
|
||||
canonicalModName := pkg.TokenToModule(token)
|
||||
modName := PyName(canonicalModName)
|
||||
modName := PyName(strings.ToLower(canonicalModName))
|
||||
if override, ok := info.ModuleNameOverrides[canonicalModName]; ok {
|
||||
modName = override
|
||||
}
|
||||
|
@ -1475,7 +1467,7 @@ func GeneratePackage(tool string, pkg *schema.Package, extraFiles map[string][]b
|
|||
}
|
||||
|
||||
// Emit casing tables.
|
||||
files.add(filepath.Join(pyPack(pkg.Name), "tables.py"), []byte(modules[""].genPropertyConversionTables()))
|
||||
files.add(filepath.Join(pyPack(pkg.Name), "_tables.py"), []byte(modules[""].genPropertyConversionTables()))
|
||||
|
||||
// Finally emit the package metadata (setup.py).
|
||||
setup, err := genPackageMetadata(tool, pkg, info.Requires)
|
||||
|
@ -1537,7 +1529,7 @@ def get_env_float(*args):
|
|||
|
||||
def get_version():
|
||||
# __name__ is set to the fully-qualified name of the current module, In our case, it will be
|
||||
# <some module>.utilities. <some module> is the module we want to query the version for.
|
||||
# <some module>._utilities. <some module> is the module we want to query the version for.
|
||||
root_package, *rest = __name__.split('.')
|
||||
|
||||
# pkg_resources uses setuptools to inspect the set of installed packages. We use it here to ask
|
||||
|
|
|
@ -133,10 +133,12 @@ func (p shortcodeParser) Open(parent ast.Node, reader text.Reader, pc parser.Con
|
|||
}
|
||||
|
||||
func (p shortcodeParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
|
||||
line, _ := reader.PeekLine()
|
||||
line, seg := reader.PeekLine()
|
||||
pos := pc.BlockOffset()
|
||||
if pos < 0 {
|
||||
return parser.Continue | parser.HasChildren
|
||||
} else if pos > seg.Len() {
|
||||
return parser.Continue | parser.HasChildren
|
||||
}
|
||||
|
||||
nameStart, nameEnd, shortcodeEnd, isClose, ok := p.parseShortcode(line, pos)
|
||||
|
|
79
pkg/codegen/schema/loader.go
Normal file
79
pkg/codegen/schema/loader.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/blang/semver"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/plugin"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/tokens"
|
||||
)
|
||||
|
||||
type Loader interface {
|
||||
LoadPackage(pkg string, version *semver.Version) (*Package, error)
|
||||
}
|
||||
|
||||
type pluginLoader struct {
|
||||
m sync.RWMutex
|
||||
|
||||
host plugin.Host
|
||||
entries map[string]*Package
|
||||
}
|
||||
|
||||
func NewPluginLoader(host plugin.Host) Loader {
|
||||
return &pluginLoader{
|
||||
host: host,
|
||||
entries: map[string]*Package{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *pluginLoader) getPackage(key string) (*Package, bool) {
|
||||
l.m.RLock()
|
||||
defer l.m.RUnlock()
|
||||
|
||||
p, ok := l.entries[key]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
func (l *pluginLoader) LoadPackage(pkg string, version *semver.Version) (*Package, error) {
|
||||
key := pkg + "@"
|
||||
if version != nil {
|
||||
key += version.String()
|
||||
}
|
||||
|
||||
if p, ok := l.getPackage(key); ok {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
provider, err := l.host.Provider(tokens.Package(pkg), version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schemaFormatVersion := 0
|
||||
schemaBytes, err := provider.GetSchema(schemaFormatVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spec PackageSpec
|
||||
if err := jsoniter.Unmarshal(schemaBytes, &spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := ImportSpec(spec, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.m.Lock()
|
||||
defer l.m.Unlock()
|
||||
|
||||
if p, ok := l.entries[pkg]; ok {
|
||||
return p, nil
|
||||
}
|
||||
l.entries[key] = p
|
||||
|
||||
return p, nil
|
||||
}
|
|
@ -303,6 +303,8 @@ type Package struct {
|
|||
Repository string
|
||||
// LogoURL is the URL for the package's logo, if any.
|
||||
LogoURL string
|
||||
// PluginDownloadURL is the URL to use to acquire the provider plugin binary, if any.
|
||||
PluginDownloadURL string
|
||||
|
||||
// Types is the list of non-resource types defined by the package.
|
||||
Types []Type
|
||||
|
@ -316,6 +318,9 @@ type Package struct {
|
|||
Functions []*Function
|
||||
// Language specifies additional language-specific data about the package.
|
||||
Language map[string]interface{}
|
||||
|
||||
resourceTable map[string]*Resource
|
||||
functionTable map[string]*Function
|
||||
}
|
||||
|
||||
// Language provides hooks for importing language-specific metadata in a package.
|
||||
|
@ -539,6 +544,16 @@ func (pkg *Package) TokenToModule(tok string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func (pkg *Package) GetResource(token string) (*Resource, bool) {
|
||||
r, ok := pkg.resourceTable[token]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
func (pkg *Package) GetFunction(token string) (*Function, bool) {
|
||||
f, ok := pkg.functionTable[token]
|
||||
return f, ok
|
||||
}
|
||||
|
||||
// TypeSpec is the serializable form of a reference to a type.
|
||||
type TypeSpec struct {
|
||||
// Type is the primitive or composite type, if any. May be "bool", "integer", "number", "string", "array", or
|
||||
|
@ -681,6 +696,8 @@ type PackageSpec struct {
|
|||
Repository string `json:"repository,omitempty"`
|
||||
// LogoURL is the URL for the package's logo, if any.
|
||||
LogoURL string `json:"logoUrl,omitempty"`
|
||||
// PluginDownloadURL is the URL to use to acquire the provider plugin binary, if any.
|
||||
PluginDownloadURL string `json:"pluginDownloadURL,omitempty"`
|
||||
|
||||
// Meta contains information for the importer about this package.
|
||||
Meta *MetadataSpec `json:"meta,omitempty"`
|
||||
|
@ -736,12 +753,12 @@ func ImportSpec(spec PackageSpec, languages map[string]Language) (*Package, erro
|
|||
return nil, errors.Wrap(err, "binding provider")
|
||||
}
|
||||
|
||||
resources, err := bindResources(spec.Resources, types)
|
||||
resources, resourceTable, err := bindResources(spec.Resources, types)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "binding resources")
|
||||
}
|
||||
|
||||
functions, err := bindFunctions(spec.Functions, types)
|
||||
functions, functionTable, err := bindFunctions(spec.Functions, types)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "binding functions")
|
||||
}
|
||||
|
@ -774,21 +791,24 @@ func ImportSpec(spec PackageSpec, languages map[string]Language) (*Package, erro
|
|||
}
|
||||
|
||||
pkg := &Package{
|
||||
moduleFormat: moduleFormatRegexp,
|
||||
Name: spec.Name,
|
||||
Version: version,
|
||||
Description: spec.Description,
|
||||
Keywords: spec.Keywords,
|
||||
Homepage: spec.Homepage,
|
||||
License: spec.License,
|
||||
Attribution: spec.Attribution,
|
||||
Repository: spec.Repository,
|
||||
Config: config,
|
||||
Types: typeList,
|
||||
Provider: provider,
|
||||
Resources: resources,
|
||||
Functions: functions,
|
||||
Language: language,
|
||||
moduleFormat: moduleFormatRegexp,
|
||||
Name: spec.Name,
|
||||
Version: version,
|
||||
Description: spec.Description,
|
||||
Keywords: spec.Keywords,
|
||||
Homepage: spec.Homepage,
|
||||
License: spec.License,
|
||||
Attribution: spec.Attribution,
|
||||
Repository: spec.Repository,
|
||||
PluginDownloadURL: spec.PluginDownloadURL,
|
||||
Config: config,
|
||||
Types: typeList,
|
||||
Provider: provider,
|
||||
Resources: resources,
|
||||
Functions: functions,
|
||||
Language: language,
|
||||
resourceTable: resourceTable,
|
||||
functionTable: functionTable,
|
||||
}
|
||||
if err := pkg.ImportLanguages(languages); err != nil {
|
||||
return nil, err
|
||||
|
@ -1186,13 +1206,15 @@ func bindProvider(pkgName string, spec ResourceSpec, types *types) (*Resource, e
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func bindResources(specs map[string]ResourceSpec, types *types) ([]*Resource, error) {
|
||||
func bindResources(specs map[string]ResourceSpec, types *types) ([]*Resource, map[string]*Resource, error) {
|
||||
resourceTable := map[string]*Resource{}
|
||||
var resources []*Resource
|
||||
for token, spec := range specs {
|
||||
res, err := bindResource(token, spec, types)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error binding resource %v", token)
|
||||
return nil, nil, errors.Wrapf(err, "error binding resource %v", token)
|
||||
}
|
||||
resourceTable[token] = res
|
||||
resources = append(resources, res)
|
||||
}
|
||||
|
||||
|
@ -1200,7 +1222,7 @@ func bindResources(specs map[string]ResourceSpec, types *types) ([]*Resource, er
|
|||
return resources[i].Token < resources[j].Token
|
||||
})
|
||||
|
||||
return resources, nil
|
||||
return resources, resourceTable, nil
|
||||
}
|
||||
|
||||
func bindFunction(token string, spec FunctionSpec, types *types) (*Function, error) {
|
||||
|
@ -1237,13 +1259,15 @@ func bindFunction(token string, spec FunctionSpec, types *types) (*Function, err
|
|||
}, nil
|
||||
}
|
||||
|
||||
func bindFunctions(specs map[string]FunctionSpec, types *types) ([]*Function, error) {
|
||||
func bindFunctions(specs map[string]FunctionSpec, types *types) ([]*Function, map[string]*Function, error) {
|
||||
functionTable := map[string]*Function{}
|
||||
var functions []*Function
|
||||
for token, spec := range specs {
|
||||
f, err := bindFunction(token, spec, types)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error binding function %v", token)
|
||||
return nil, nil, errors.Wrapf(err, "error binding function %v", token)
|
||||
}
|
||||
functionTable[token] = f
|
||||
functions = append(functions, f)
|
||||
}
|
||||
|
||||
|
@ -1251,5 +1275,5 @@ func bindFunctions(specs map[string]FunctionSpec, types *types) ([]*Function, er
|
|||
return functions[i].Token < functions[j].Token
|
||||
})
|
||||
|
||||
return functions, nil
|
||||
return functions, functionTable, nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
package codegen
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
|
@ -79,3 +82,27 @@ func SortedKeys(m interface{}) []string {
|
|||
|
||||
return keys
|
||||
}
|
||||
|
||||
// CleanDir removes all existing files from a directory except those in the exclusions list.
|
||||
// Note: The exclusions currently don't function recursively, so you cannot exclude a single file
|
||||
// in a subdirectory, only entire subdirectories. This function will need improvements to be able to
|
||||
// target that use-case.
|
||||
func CleanDir(dirPath string, exclusions StringSet) error {
|
||||
subPaths, err := ioutil.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(subPaths) > 0 {
|
||||
for _, path := range subPaths {
|
||||
if !exclusions.Has(path.Name()) {
|
||||
err = os.RemoveAll(filepath.Join(dirPath, path.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ func printStepHeader(b io.StringWriter, step StepEventMetadata) {
|
|||
// show a locked symbol, since we are either newly protecting this resource, or retaining protection.
|
||||
extra = " 🔒"
|
||||
}
|
||||
writeString(b, fmt.Sprintf("%s: (%s)%s\n", string(step.Res.URN.Type()), step.Op, extra))
|
||||
writeString(b, fmt.Sprintf("%s: (%s)%s\n", string(step.Type), step.Op, extra))
|
||||
}
|
||||
|
||||
func GetIndentationString(indent int) string {
|
||||
|
@ -117,7 +117,7 @@ func GetResourcePropertiesSummary(step StepEventMetadata, indent int) string {
|
|||
var b bytes.Buffer
|
||||
|
||||
op := step.Op
|
||||
urn := step.Res.URN
|
||||
urn := step.URN
|
||||
old := step.Old
|
||||
|
||||
// Print the indentation.
|
||||
|
@ -232,25 +232,32 @@ func PrintObject(
|
|||
}
|
||||
}
|
||||
|
||||
func massageStackPreviewAdd(p resource.PropertyValue) {
|
||||
func massageStackPreviewAdd(p resource.PropertyValue) resource.PropertyValue {
|
||||
switch {
|
||||
case p.IsArray():
|
||||
for _, v := range p.ArrayValue() {
|
||||
massageStackPreviewAdd(v)
|
||||
arr := make([]resource.PropertyValue, len(p.ArrayValue()))
|
||||
for i, v := range p.ArrayValue() {
|
||||
arr[i] = massageStackPreviewAdd(v)
|
||||
}
|
||||
return resource.NewArrayProperty(arr)
|
||||
case p.IsObject():
|
||||
delete(p.ObjectValue(), "@isPulumiResource")
|
||||
for _, v := range p.ObjectValue() {
|
||||
massageStackPreviewAdd(v)
|
||||
obj := resource.PropertyMap{}
|
||||
for k, v := range p.ObjectValue() {
|
||||
if k != "@isPulumiResource" {
|
||||
obj[k] = massageStackPreviewAdd(v)
|
||||
}
|
||||
}
|
||||
return resource.NewObjectProperty(obj)
|
||||
default:
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
func massageStackPreviewDiff(diff resource.ValueDiff, inResource bool) {
|
||||
switch {
|
||||
case diff.Array != nil:
|
||||
for _, p := range diff.Array.Adds {
|
||||
massageStackPreviewAdd(p)
|
||||
for i, p := range diff.Array.Adds {
|
||||
diff.Array.Adds[i] = massageStackPreviewAdd(p)
|
||||
}
|
||||
for _, d := range diff.Array.Updates {
|
||||
massageStackPreviewDiff(d, inResource)
|
||||
|
@ -278,8 +285,8 @@ func massageStackPreviewOutputDiff(diff *resource.ObjectDiff, inResource bool) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, p := range diff.Adds {
|
||||
massageStackPreviewAdd(p)
|
||||
for i, p := range diff.Adds {
|
||||
diff.Adds[i] = massageStackPreviewAdd(p)
|
||||
}
|
||||
for k, d := range diff.Updates {
|
||||
if isResource && d.New.IsComputed() && !shouldPrintPropertyValue(d.Old, false) {
|
||||
|
@ -296,7 +303,7 @@ func GetResourceOutputsPropertiesString(
|
|||
step StepEventMetadata, indent int, planning, debug, refresh, showSames bool) string {
|
||||
|
||||
// During the actual update we always show all the outputs for the stack, even if they are unchanged.
|
||||
if !showSames && !planning && step.Res.URN.Type() == resource.RootStackType {
|
||||
if !showSames && !planning && step.URN.Type() == resource.RootStackType {
|
||||
showSames = true
|
||||
}
|
||||
|
||||
|
@ -321,7 +328,7 @@ func GetResourceOutputsPropertiesString(
|
|||
step.Op == deploy.OpReadReplacement ||
|
||||
step.Op == deploy.OpImport ||
|
||||
step.Op == deploy.OpImportReplacement ||
|
||||
step.Res.URN.Type() == resource.RootStackType
|
||||
step.URN.Type() == resource.RootStackType
|
||||
if !printOutputDuringPlanning {
|
||||
return ""
|
||||
}
|
||||
|
@ -353,7 +360,7 @@ func GetResourceOutputsPropertiesString(
|
|||
|
||||
// If this is the root stack type, we want to strip out any nested resource outputs that are not known if
|
||||
// they have no corresponding output in the old state.
|
||||
if planning && step.Res.URN.Type() == resource.RootStackType {
|
||||
if planning && step.URN.Type() == resource.RootStackType {
|
||||
massageStackPreviewOutputDiff(outputDiff, false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/plugin"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/tokens"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/deepcopy"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/logging"
|
||||
)
|
||||
|
||||
|
@ -35,7 +36,38 @@ import (
|
|||
// type for the `Payload` field will differ depending on the value of the `Type` field
|
||||
type Event struct {
|
||||
Type EventType
|
||||
Payload interface{}
|
||||
payload interface{}
|
||||
}
|
||||
|
||||
func NewEvent(typ EventType, payload interface{}) Event {
|
||||
ok := false
|
||||
switch typ {
|
||||
case CancelEvent:
|
||||
ok = payload == nil
|
||||
case StdoutColorEvent:
|
||||
_, ok = payload.(StdoutEventPayload)
|
||||
case DiagEvent:
|
||||
_, ok = payload.(DiagEventPayload)
|
||||
case PreludeEvent:
|
||||
_, ok = payload.(PreludeEventPayload)
|
||||
case SummaryEvent:
|
||||
_, ok = payload.(SummaryEventPayload)
|
||||
case ResourcePreEvent:
|
||||
_, ok = payload.(ResourcePreEventPayload)
|
||||
case ResourceOutputsEvent:
|
||||
_, ok = payload.(ResourceOutputsEventPayload)
|
||||
case ResourceOperationFailed:
|
||||
_, ok = payload.(ResourceOperationFailedPayload)
|
||||
case PolicyViolationEvent:
|
||||
_, ok = payload.(PolicyViolationEventPayload)
|
||||
default:
|
||||
contract.Failf("unknown event type %v", typ)
|
||||
}
|
||||
contract.Assertf(ok, "invalid payload of type %T for event type %v", payload, typ)
|
||||
return Event{
|
||||
Type: typ,
|
||||
payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// EventType is the kind of event being emitted.
|
||||
|
@ -53,6 +85,10 @@ const (
|
|||
PolicyViolationEvent EventType = "policy-violation"
|
||||
)
|
||||
|
||||
func (e Event) Payload() interface{} {
|
||||
return deepcopy.Copy(e.payload)
|
||||
}
|
||||
|
||||
func cancelEvent() Event {
|
||||
return Event{Type: CancelEvent}
|
||||
}
|
||||
|
@ -119,6 +155,8 @@ type ResourcePreEventPayload struct {
|
|||
// StepEventMetadata contains the metadata associated with a step the engine is performing.
|
||||
type StepEventMetadata struct {
|
||||
Op deploy.StepOp // the operation performed by this step.
|
||||
URN resource.URN // the resource URN (for before and after).
|
||||
Type tokens.Type // the type affected by this step.
|
||||
Old *StepEventStateMetadata // the state of the resource before performing this step.
|
||||
New *StepEventStateMetadata // the state of the resource after performing this step.
|
||||
Res *StepEventStateMetadata // the latest state for the resource that is known (worst case, old).
|
||||
|
@ -272,6 +310,8 @@ func makeStepEventMetadata(op deploy.StepOp, step deploy.Step, debug bool) StepE
|
|||
|
||||
return StepEventMetadata{
|
||||
Op: op,
|
||||
URN: step.URN(),
|
||||
Type: step.Type(),
|
||||
Keys: keys,
|
||||
Diffs: diffs,
|
||||
DetailedDiff: detailedDiff,
|
||||
|
@ -425,27 +465,21 @@ func (e *eventEmitter) resourceOperationFailedEvent(
|
|||
|
||||
contract.Requiref(e != nil, "e", "!= nil")
|
||||
|
||||
e.ch <- Event{
|
||||
Type: ResourceOperationFailed,
|
||||
Payload: ResourceOperationFailedPayload{
|
||||
Metadata: makeStepEventMetadata(step.Op(), step, debug),
|
||||
Status: status,
|
||||
Steps: steps,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(ResourceOperationFailed, ResourceOperationFailedPayload{
|
||||
Metadata: makeStepEventMetadata(step.Op(), step, debug),
|
||||
Status: status,
|
||||
Steps: steps,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) resourceOutputsEvent(op deploy.StepOp, step deploy.Step, planning bool, debug bool) {
|
||||
contract.Requiref(e != nil, "e", "!= nil")
|
||||
|
||||
e.ch <- Event{
|
||||
Type: ResourceOutputsEvent,
|
||||
Payload: ResourceOutputsEventPayload{
|
||||
Metadata: makeStepEventMetadata(op, step, debug),
|
||||
Planning: planning,
|
||||
Debug: debug,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(ResourceOutputsEvent, ResourceOutputsEventPayload{
|
||||
Metadata: makeStepEventMetadata(op, step, debug),
|
||||
Planning: planning,
|
||||
Debug: debug,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) resourcePreEvent(
|
||||
|
@ -453,14 +487,11 @@ func (e *eventEmitter) resourcePreEvent(
|
|||
|
||||
contract.Requiref(e != nil, "e", "!= nil")
|
||||
|
||||
e.ch <- Event{
|
||||
Type: ResourcePreEvent,
|
||||
Payload: ResourcePreEventPayload{
|
||||
Metadata: makeStepEventMetadata(step.Op(), step, debug),
|
||||
Planning: planning,
|
||||
Debug: debug,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(ResourcePreEvent, ResourcePreEventPayload{
|
||||
Metadata: makeStepEventMetadata(step.Op(), step, debug),
|
||||
Planning: planning,
|
||||
Debug: debug,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) preludeEvent(isPreview bool, cfg config.Map) {
|
||||
|
@ -474,44 +505,35 @@ func (e *eventEmitter) preludeEvent(isPreview bool, cfg config.Map) {
|
|||
configStringMap[keyString] = valueString
|
||||
}
|
||||
|
||||
e.ch <- Event{
|
||||
Type: PreludeEvent,
|
||||
Payload: PreludeEventPayload{
|
||||
IsPreview: isPreview,
|
||||
Config: configStringMap,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(PreludeEvent, PreludeEventPayload{
|
||||
IsPreview: isPreview,
|
||||
Config: configStringMap,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) previewSummaryEvent(resourceChanges ResourceChanges, policyPacks map[string]string) {
|
||||
contract.Requiref(e != nil, "e", "!= nil")
|
||||
|
||||
e.ch <- Event{
|
||||
Type: SummaryEvent,
|
||||
Payload: SummaryEventPayload{
|
||||
IsPreview: true,
|
||||
MaybeCorrupt: false,
|
||||
Duration: 0,
|
||||
ResourceChanges: resourceChanges,
|
||||
PolicyPacks: policyPacks,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(SummaryEvent, SummaryEventPayload{
|
||||
IsPreview: true,
|
||||
MaybeCorrupt: false,
|
||||
Duration: 0,
|
||||
ResourceChanges: resourceChanges,
|
||||
PolicyPacks: policyPacks,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) updateSummaryEvent(maybeCorrupt bool,
|
||||
duration time.Duration, resourceChanges ResourceChanges, policyPacks map[string]string) {
|
||||
contract.Requiref(e != nil, "e", "!= nil")
|
||||
|
||||
e.ch <- Event{
|
||||
Type: SummaryEvent,
|
||||
Payload: SummaryEventPayload{
|
||||
IsPreview: false,
|
||||
MaybeCorrupt: maybeCorrupt,
|
||||
Duration: duration,
|
||||
ResourceChanges: resourceChanges,
|
||||
PolicyPacks: policyPacks,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(SummaryEvent, SummaryEventPayload{
|
||||
IsPreview: false,
|
||||
MaybeCorrupt: maybeCorrupt,
|
||||
Duration: duration,
|
||||
ResourceChanges: resourceChanges,
|
||||
PolicyPacks: policyPacks,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) policyViolationEvent(urn resource.URN, d plugin.AnalyzeDiagnostic) {
|
||||
|
@ -542,37 +564,31 @@ func (e *eventEmitter) policyViolationEvent(urn resource.URN, d plugin.AnalyzeDi
|
|||
buffer.WriteString(colors.Reset)
|
||||
buffer.WriteRune('\n')
|
||||
|
||||
e.ch <- Event{
|
||||
Type: PolicyViolationEvent,
|
||||
Payload: PolicyViolationEventPayload{
|
||||
ResourceURN: urn,
|
||||
Message: logging.FilterString(buffer.String()),
|
||||
Color: colors.Raw,
|
||||
PolicyName: d.PolicyName,
|
||||
PolicyPackName: d.PolicyPackName,
|
||||
PolicyPackVersion: d.PolicyPackVersion,
|
||||
EnforcementLevel: d.EnforcementLevel,
|
||||
Prefix: logging.FilterString(prefix.String()),
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(PolicyViolationEvent, PolicyViolationEventPayload{
|
||||
ResourceURN: urn,
|
||||
Message: logging.FilterString(buffer.String()),
|
||||
Color: colors.Raw,
|
||||
PolicyName: d.PolicyName,
|
||||
PolicyPackName: d.PolicyPackName,
|
||||
PolicyPackVersion: d.PolicyPackVersion,
|
||||
EnforcementLevel: d.EnforcementLevel,
|
||||
Prefix: logging.FilterString(prefix.String()),
|
||||
})
|
||||
}
|
||||
|
||||
func diagEvent(e *eventEmitter, d *diag.Diag, prefix, msg string, sev diag.Severity,
|
||||
ephemeral bool) {
|
||||
contract.Requiref(e != nil, "e", "!= nil")
|
||||
|
||||
e.ch <- Event{
|
||||
Type: DiagEvent,
|
||||
Payload: DiagEventPayload{
|
||||
URN: d.URN,
|
||||
Prefix: logging.FilterString(prefix),
|
||||
Message: logging.FilterString(msg),
|
||||
Color: colors.Raw,
|
||||
Severity: sev,
|
||||
StreamID: d.StreamID,
|
||||
Ephemeral: ephemeral,
|
||||
},
|
||||
}
|
||||
e.ch <- NewEvent(DiagEvent, DiagEventPayload{
|
||||
URN: d.URN,
|
||||
Prefix: logging.FilterString(prefix),
|
||||
Message: logging.FilterString(msg),
|
||||
Color: colors.Raw,
|
||||
Severity: sev,
|
||||
StreamID: d.StreamID,
|
||||
Ephemeral: ephemeral,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *eventEmitter) diagDebugEvent(d *diag.Diag, prefix, msg string, ephemeral bool) {
|
||||
|
|
|
@ -119,7 +119,7 @@ func (j *Journal) Snap(base *deploy.Snapshot) *deploy.Snapshot {
|
|||
resources, dones := []*resource.State{}, make(map[*resource.State]bool)
|
||||
ops, doneOps := []resource.Operation{}, make(map[*resource.State]bool)
|
||||
for _, e := range j.Entries {
|
||||
logging.V(7).Infof("%v %v (%v)", e.Step.Op(), e.Step.Res().URN, e.Kind)
|
||||
logging.V(7).Infof("%v %v (%v)", e.Step.Op(), e.Step.URN(), e.Kind)
|
||||
|
||||
// Begin journal entries add pending operations to the snapshot. As we see success or failure
|
||||
// entries, we'll record them in doneOps.
|
||||
|
@ -229,7 +229,7 @@ func AssertSameSteps(t *testing.T, expected []StepSummary, actual []deploy.Step)
|
|||
act := actual[0]
|
||||
actual = actual[1:]
|
||||
|
||||
if !assert.Equal(t, exp.Op, act.Op()) || !assert.Equal(t, exp.URN, act.Res().URN) {
|
||||
if !assert.Equal(t, exp.Op, act.Op()) || !assert.Equal(t, exp.URN, act.URN()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -672,7 +672,7 @@ func TestSingleResourceDefaultProviderUpgrade(t *testing.T) {
|
|||
// Should see only sames: the default provider should be injected into the old state before the update
|
||||
// runs.
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
expect := deploy.OpSame
|
||||
if isRefresh {
|
||||
|
@ -707,7 +707,7 @@ func TestSingleResourceDefaultProviderUpgrade(t *testing.T) {
|
|||
// runs.
|
||||
deleted := make(map[resource.URN]bool)
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
deleted[urn] = true
|
||||
assert.Equal(t, deploy.OpDelete, entry.Step.Op())
|
||||
|
@ -783,7 +783,7 @@ func TestSingleResourceDefaultProviderReplace(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
replacedProvider = true
|
||||
case resURN:
|
||||
|
@ -875,7 +875,7 @@ func TestSingleResourceExplicitProviderReplace(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
replacedProvider = true
|
||||
case resURN:
|
||||
|
@ -967,7 +967,7 @@ func TestSingleResourceExplicitProviderDeleteBeforeReplace(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
if entry.Step.Op() == deploy.OpDeleteReplaced {
|
||||
assert.False(t, createdProvider)
|
||||
|
@ -1050,7 +1050,7 @@ func TestSingleResourceDiffUnavailable(t *testing.T) {
|
|||
found := false
|
||||
for _, e := range events {
|
||||
if e.Type == DiagEvent {
|
||||
p := e.Payload.(DiagEventPayload)
|
||||
p := e.Payload().(DiagEventPayload)
|
||||
if p.URN == resURN && p.Severity == diag.Warning && p.Message == "diff unavailable" {
|
||||
found = true
|
||||
break
|
||||
|
@ -1114,7 +1114,7 @@ func TestDestroyWithPendingDelete(t *testing.T) {
|
|||
deletedID0, deletedID1 := false, false
|
||||
for _, entry := range j.Entries {
|
||||
// Ignore non-terminal steps and steps that affect the injected default provider.
|
||||
if entry.Kind != JournalEntrySuccess || entry.Step.Res().URN != resURN ||
|
||||
if entry.Kind != JournalEntrySuccess || entry.Step.URN() != resURN ||
|
||||
(entry.Step.Op() != deploy.OpDelete && entry.Step.Op() != deploy.OpDeleteReplaced) {
|
||||
continue
|
||||
}
|
||||
|
@ -1188,7 +1188,7 @@ func TestUpdateWithPendingDelete(t *testing.T) {
|
|||
deletedID0, deletedID1 := false, false
|
||||
for _, entry := range j.Entries {
|
||||
// Ignore non-terminal steps and steps that affect the injected default provider.
|
||||
if entry.Kind != JournalEntrySuccess || entry.Step.Res().URN != resURN ||
|
||||
if entry.Kind != JournalEntrySuccess || entry.Step.URN() != resURN ||
|
||||
(entry.Step.Op() != deploy.OpDelete && entry.Step.Op() != deploy.OpDeleteReplaced) {
|
||||
continue
|
||||
}
|
||||
|
@ -1457,7 +1457,7 @@ func TestCheckFailureRecord(t *testing.T) {
|
|||
sawFailure := false
|
||||
for _, evt := range evts {
|
||||
if evt.Type == DiagEvent {
|
||||
e := evt.Payload.(DiagEventPayload)
|
||||
e := evt.Payload().(DiagEventPayload)
|
||||
msg := colors.Never.Colorize(e.Message)
|
||||
sawFailure = msg == "oh no, check had an error\n" && e.Severity == diag.Error
|
||||
}
|
||||
|
@ -1507,7 +1507,7 @@ func TestCheckFailureInvalidPropertyRecord(t *testing.T) {
|
|||
sawFailure := false
|
||||
for _, evt := range evts {
|
||||
if evt.Type == DiagEvent {
|
||||
e := evt.Payload.(DiagEventPayload)
|
||||
e := evt.Payload().(DiagEventPayload)
|
||||
msg := colors.Never.Colorize(e.Message)
|
||||
sawFailure = strings.Contains(msg, "field is not valid") && e.Severity == diag.Error
|
||||
if sawFailure {
|
||||
|
@ -1674,8 +1674,8 @@ func validateRefreshDeleteCombination(t *testing.T, names []string, targets []st
|
|||
for _, entry := range j.Entries {
|
||||
if len(refreshTargets) > 0 {
|
||||
// should only see changes to urns we explicitly asked to change
|
||||
assert.Containsf(t, refreshTargets, entry.Step.Res().URN,
|
||||
"Refreshed a resource that wasn't a target: %v", entry.Step.Res().URN)
|
||||
assert.Containsf(t, refreshTargets, entry.Step.URN(),
|
||||
"Refreshed a resource that wasn't a target: %v", entry.Step.URN())
|
||||
}
|
||||
|
||||
assert.Equal(t, deploy.OpRefresh, entry.Step.Op())
|
||||
|
@ -1843,8 +1843,8 @@ func validateRefreshBasicsCombination(t *testing.T, names []string, targets []st
|
|||
for _, entry := range j.Entries {
|
||||
if len(refreshTargets) > 0 {
|
||||
// should only see changes to urns we explicitly asked to change
|
||||
assert.Containsf(t, refreshTargets, entry.Step.Res().URN,
|
||||
"Refreshed a resource that wasn't a target: %v", entry.Step.Res().URN)
|
||||
assert.Containsf(t, refreshTargets, entry.Step.URN(),
|
||||
"Refreshed a resource that wasn't a target: %v", entry.Step.URN())
|
||||
}
|
||||
|
||||
assert.Equal(t, deploy.OpRefresh, entry.Step.Op())
|
||||
|
@ -2090,7 +2090,7 @@ func TestLanguageHostDiagnostics(t *testing.T) {
|
|||
sawExitCode := false
|
||||
for _, evt := range evts {
|
||||
if evt.Type == DiagEvent {
|
||||
e := evt.Payload.(DiagEventPayload)
|
||||
e := evt.Payload().(DiagEventPayload)
|
||||
msg := colors.Never.Colorize(e.Message)
|
||||
sawExitCode = strings.Contains(msg, errorText) && e.Severity == diag.Error
|
||||
if sawExitCode {
|
||||
|
@ -2375,7 +2375,7 @@ func TestUpdatePartialFailure(t *testing.T) {
|
|||
|
||||
assertIsErrorOrBailResult(t, res)
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case resURN:
|
||||
assert.Equal(t, deploy.OpUpdate, entry.Step.Op())
|
||||
switch entry.Kind {
|
||||
|
@ -2482,7 +2482,7 @@ func TestStackReference(t *testing.T) {
|
|||
|
||||
assert.Nil(t, res)
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case resURN:
|
||||
switch entry.Step.Op() {
|
||||
case deploy.OpCreateReplacement, deploy.OpDeleteReplaced, deploy.OpReplace:
|
||||
|
@ -2811,7 +2811,7 @@ func TestDeleteBeforeReplace(t *testing.T) {
|
|||
replaced := make(map[resource.URN]bool)
|
||||
for _, entry := range j.Entries {
|
||||
if entry.Step.Op() == deploy.OpReplace {
|
||||
replaced[entry.Step.Res().URN] = true
|
||||
replaced[entry.Step.URN()] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3159,7 +3159,7 @@ func TestSingleResourceIgnoreChanges(t *testing.T) {
|
|||
events []Event, res result.Result) result.Result {
|
||||
for _, event := range events {
|
||||
if event.Type == ResourcePreEvent {
|
||||
payload := event.Payload.(ResourcePreEventPayload)
|
||||
payload := event.Payload().(ResourcePreEventPayload)
|
||||
assert.Subset(t, allowedOps, []deploy.StepOp{payload.Metadata.Op})
|
||||
}
|
||||
}
|
||||
|
@ -3255,7 +3255,7 @@ func TestDefaultProviderDiff(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
switch entry.Step.Res().URN.Name().String() {
|
||||
switch entry.Step.URN().Name().String() {
|
||||
case resName, resBName:
|
||||
assert.Equal(t, expectedStep, entry.Step.Op())
|
||||
}
|
||||
|
@ -3376,7 +3376,7 @@ func TestDefaultProviderDiffReplacement(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
switch entry.Step.Res().URN.Name().String() {
|
||||
switch entry.Step.URN().Name().String() {
|
||||
case resName:
|
||||
assert.Subset(t, expectedSteps, []deploy.StepOp{entry.Step.Op()})
|
||||
case resBName:
|
||||
|
@ -3494,13 +3494,13 @@ func TestAliases(t *testing.T) {
|
|||
events []Event, res result.Result) result.Result {
|
||||
for _, event := range events {
|
||||
if event.Type == ResourcePreEvent {
|
||||
payload := event.Payload.(ResourcePreEventPayload)
|
||||
payload := event.Payload().(ResourcePreEventPayload)
|
||||
assert.Subset(t, allowedOps, []deploy.StepOp{payload.Metadata.Op})
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range j.Entries {
|
||||
if entry.Step.Res().URN.Type() == "pulumi:providers:pkgA" {
|
||||
if entry.Step.Type() == "pulumi:providers:pkgA" {
|
||||
continue
|
||||
}
|
||||
switch entry.Kind {
|
||||
|
@ -3803,8 +3803,8 @@ func TestPersistentDiff(t *testing.T) {
|
|||
found := false
|
||||
for _, e := range events {
|
||||
if e.Type == ResourcePreEvent {
|
||||
p := e.Payload.(ResourcePreEventPayload).Metadata
|
||||
if p.Res.URN == resURN {
|
||||
p := e.Payload().(ResourcePreEventPayload).Metadata
|
||||
if p.URN == resURN {
|
||||
assert.Equal(t, deploy.OpUpdate, p.Op)
|
||||
found = true
|
||||
}
|
||||
|
@ -3824,8 +3824,8 @@ func TestPersistentDiff(t *testing.T) {
|
|||
found := false
|
||||
for _, e := range events {
|
||||
if e.Type == ResourcePreEvent {
|
||||
p := e.Payload.(ResourcePreEventPayload).Metadata
|
||||
if p.Res.URN == resURN {
|
||||
p := e.Payload().(ResourcePreEventPayload).Metadata
|
||||
if p.URN == resURN {
|
||||
assert.Equal(t, deploy.OpSame, p.Op)
|
||||
found = true
|
||||
}
|
||||
|
@ -3884,8 +3884,8 @@ func TestDetailedDiffReplace(t *testing.T) {
|
|||
found := false
|
||||
for _, e := range events {
|
||||
if e.Type == ResourcePreEvent {
|
||||
p := e.Payload.(ResourcePreEventPayload).Metadata
|
||||
if p.Res.URN == resURN && p.Op == deploy.OpReplace {
|
||||
p := e.Payload().(ResourcePreEventPayload).Metadata
|
||||
if p.URN == resURN && p.Op == deploy.OpReplace {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -3973,7 +3973,7 @@ func TestImport(t *testing.T) {
|
|||
snap, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
case resURN:
|
||||
|
@ -3991,7 +3991,7 @@ func TestImport(t *testing.T) {
|
|||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
default:
|
||||
|
@ -4007,7 +4007,7 @@ func TestImport(t *testing.T) {
|
|||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
case resURN:
|
||||
|
@ -4029,7 +4029,7 @@ func TestImport(t *testing.T) {
|
|||
_, res = TestOp(Destroy).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpDelete, entry.Step.Op())
|
||||
default:
|
||||
|
@ -4045,7 +4045,7 @@ func TestImport(t *testing.T) {
|
|||
snap, res = TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
default:
|
||||
|
@ -4066,7 +4066,7 @@ func TestImport(t *testing.T) {
|
|||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
default:
|
||||
|
@ -4083,7 +4083,7 @@ func TestImport(t *testing.T) {
|
|||
_, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
case resURN:
|
||||
|
@ -4106,7 +4106,7 @@ func TestImport(t *testing.T) {
|
|||
snap, res = TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
case resURN:
|
||||
|
@ -4125,7 +4125,7 @@ func TestImport(t *testing.T) {
|
|||
_, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
case resURN:
|
||||
|
@ -4211,7 +4211,7 @@ func TestImportWithDifferingImportIdentifierFormat(t *testing.T) {
|
|||
snap, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
case resURN:
|
||||
|
@ -4229,7 +4229,7 @@ func TestImportWithDifferingImportIdentifierFormat(t *testing.T) {
|
|||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, j *Journal, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range j.Entries {
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
default:
|
||||
|
@ -4349,7 +4349,7 @@ func TestProviderDiffMissingOldOutputs(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
switch urn := entry.Step.Res().URN; urn {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
case providerURN:
|
||||
replacedProvider = true
|
||||
case resURN:
|
||||
|
@ -4600,7 +4600,7 @@ func destroySpecificTargets(
|
|||
deleted := make(map[resource.URN]bool)
|
||||
for _, entry := range j.Entries {
|
||||
assert.Equal(t, deploy.OpDelete, entry.Step.Op())
|
||||
deleted[entry.Step.Res().URN] = true
|
||||
deleted[entry.Step.URN()] = true
|
||||
}
|
||||
|
||||
for _, target := range p.Options.DestroyTargets {
|
||||
|
@ -4693,9 +4693,9 @@ func updateSpecificTargets(t *testing.T, targets []string) {
|
|||
sames := make(map[resource.URN]bool)
|
||||
for _, entry := range j.Entries {
|
||||
if entry.Step.Op() == deploy.OpUpdate {
|
||||
updated[entry.Step.Res().URN] = true
|
||||
updated[entry.Step.URN()] = true
|
||||
} else if entry.Step.Op() == deploy.OpSame {
|
||||
sames[entry.Step.Res().URN] = true
|
||||
sames[entry.Step.URN()] = true
|
||||
} else {
|
||||
assert.FailNowf(t, "", "Got a step that wasn't a same/update: %v", entry.Step.Op())
|
||||
}
|
||||
|
@ -4803,9 +4803,9 @@ func TestCreateDuringTargetedUpdate_CreateMentionedAsTarget(t *testing.T) {
|
|||
assert.True(t, len(j.Entries) > 0)
|
||||
|
||||
for _, entry := range j.Entries {
|
||||
if entry.Step.Res().URN == resA {
|
||||
if entry.Step.URN() == resA {
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
} else if entry.Step.Res().URN == resB {
|
||||
} else if entry.Step.URN() == resB {
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
}
|
||||
}
|
||||
|
@ -5072,7 +5072,7 @@ func TestDependencyChangeDBR(t *testing.T) {
|
|||
|
||||
resBDeleted, resBSame := false, false
|
||||
for _, entry := range j.Entries {
|
||||
if entry.Step.Res().URN == urnB {
|
||||
if entry.Step.URN() == urnB {
|
||||
switch entry.Step.Op() {
|
||||
case deploy.OpDelete, deploy.OpDeleteReplaced:
|
||||
resBDeleted = true
|
||||
|
@ -5148,9 +5148,9 @@ func TestReplaceSpecificTargets(t *testing.T) {
|
|||
sames := make(map[resource.URN]bool)
|
||||
for _, entry := range j.Entries {
|
||||
if entry.Step.Op() == deploy.OpReplace {
|
||||
replaced[entry.Step.Res().URN] = true
|
||||
replaced[entry.Step.URN()] = true
|
||||
} else if entry.Step.Op() == deploy.OpSame {
|
||||
sames[entry.Step.Res().URN] = true
|
||||
sames[entry.Step.URN()] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5566,7 +5566,7 @@ func TestIgnoreChangesGolangLifecycle(t *testing.T) {
|
|||
events []Event, res result.Result) result.Result {
|
||||
for _, event := range events {
|
||||
if event.Type == ResourcePreEvent {
|
||||
payload := event.Payload.(ResourcePreEventPayload)
|
||||
payload := event.Payload().(ResourcePreEventPayload)
|
||||
assert.Equal(t, []deploy.StepOp{deploy.OpCreate}, []deploy.StepOp{payload.Metadata.Op})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ func newPlanActions(opts planOptions) *planActions {
|
|||
|
||||
func (acts *planActions) OnResourceStepPre(step deploy.Step) (interface{}, error) {
|
||||
acts.MapLock.Lock()
|
||||
acts.Seen[step.Res().URN] = step
|
||||
acts.Seen[step.URN()] = step
|
||||
acts.MapLock.Unlock()
|
||||
|
||||
// Skip reporting if necessary.
|
||||
|
@ -292,7 +292,7 @@ func (acts *planActions) OnResourceStepPost(ctx interface{},
|
|||
// global message.
|
||||
reportedURN := resource.URN("")
|
||||
if reportStep {
|
||||
reportedURN = step.Res().URN
|
||||
reportedURN = step.URN()
|
||||
}
|
||||
|
||||
acts.Opts.Diag.Errorf(diag.GetPreviewFailedError(reportedURN), err)
|
||||
|
@ -354,10 +354,10 @@ func (acts *planActions) OnPolicyViolation(urn resource.URN, d plugin.AnalyzeDia
|
|||
}
|
||||
|
||||
func assertSeen(seen map[resource.URN]deploy.Step, step deploy.Step) {
|
||||
_, has := seen[step.Res().URN]
|
||||
contract.Assertf(has, "URN '%v' had not been marked as seen", step.Res().URN)
|
||||
_, has := seen[step.URN()]
|
||||
contract.Assertf(has, "URN '%v' had not been marked as seen", step.URN())
|
||||
}
|
||||
|
||||
func isDefaultProviderStep(step deploy.Step) bool {
|
||||
return providers.IsDefaultProvider(step.Res().URN)
|
||||
return providers.IsDefaultProvider(step.URN())
|
||||
}
|
||||
|
|
|
@ -504,7 +504,7 @@ func newUpdateActions(context *Context, u UpdateInfo, opts planOptions) *updateA
|
|||
func (acts *updateActions) OnResourceStepPre(step deploy.Step) (interface{}, error) {
|
||||
// Ensure we've marked this step as observed.
|
||||
acts.MapLock.Lock()
|
||||
acts.Seen[step.Res().URN] = step
|
||||
acts.Seen[step.URN()] = step
|
||||
acts.MapLock.Unlock()
|
||||
|
||||
// Skip reporting if necessary.
|
||||
|
@ -540,7 +540,7 @@ func (acts *updateActions) OnResourceStepPost(
|
|||
|
||||
errorURN := resource.URN("")
|
||||
if reportStep {
|
||||
errorURN = step.Res().URN
|
||||
errorURN = step.URN()
|
||||
}
|
||||
|
||||
// Issue a true, bonafide error.
|
||||
|
@ -589,7 +589,7 @@ func (acts *updateActions) OnResourceStepPost(
|
|||
if status == resource.StatusPartialFailure && step.Op() == deploy.OpUpdate {
|
||||
logging.V(7).Infof(
|
||||
"OnResourceStepPost(%s): Step is partially-failed update, saving old inputs instead of new inputs",
|
||||
step.Res().URN)
|
||||
step.URN())
|
||||
new := step.New()
|
||||
old := step.Old()
|
||||
contract.Assert(new != nil)
|
||||
|
|
|
@ -134,6 +134,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -323,12 +325,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
|
|
|
@ -282,7 +282,12 @@ func NewPlan(ctx *plugin.Context, target *Target, prev *Snapshot, source Source,
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (p *Plan) diag() diag.Sink { return p.ctx.Diag }
|
||||
func (p *Plan) Ctx() *plugin.Context { return p.ctx }
|
||||
func (p *Plan) Target() *Target { return p.target }
|
||||
func (p *Plan) Diag() diag.Sink { return p.ctx.Diag }
|
||||
func (p *Plan) Prev() *Snapshot { return p.prev }
|
||||
func (p *Plan) Olds() map[resource.URN]*resource.State { return p.olds }
|
||||
func (p *Plan) Source() Source { return p.source }
|
||||
|
||||
func (p *Plan) GetProvider(ref providers.Reference) (plugin.Provider, bool) {
|
||||
return p.providers.GetProvider(ref)
|
||||
|
@ -298,7 +303,7 @@ func (p *Plan) generateURN(parent resource.URN, ty tokens.Type, name tokens.QNam
|
|||
parentType = parent.QualifiedType()
|
||||
}
|
||||
|
||||
return resource.NewURN(p.target.Name, p.source.Project(), parentType, ty, name)
|
||||
return resource.NewURN(p.Target().Name, p.source.Project(), parentType, ty, name)
|
||||
}
|
||||
|
||||
// defaultProviderURN generates the URN for the global provider given a package.
|
||||
|
|
|
@ -82,9 +82,9 @@ func (pe *planExecutor) checkTargets(targets []resource.URN, op StepOp) result.R
|
|||
|
||||
logging.V(7).Infof("Resource to %v (%v) could not be found in the stack.", op, target)
|
||||
if strings.Contains(string(target), "$") {
|
||||
pe.plan.diag().Errorf(diag.GetTargetCouldNotBeFoundError(), target)
|
||||
pe.plan.Diag().Errorf(diag.GetTargetCouldNotBeFoundError(), target)
|
||||
} else {
|
||||
pe.plan.diag().Errorf(diag.GetTargetCouldNotBeFoundDidYouForgetError(), target)
|
||||
pe.plan.Diag().Errorf(diag.GetTargetCouldNotBeFoundDidYouForgetError(), target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func (pe *planExecutor) reportExecResult(message string, preview bool) {
|
|||
|
||||
// reportError reports a single error to the executor's diag stream with the indicated URN for context.
|
||||
func (pe *planExecutor) reportError(urn resource.URN, err error) {
|
||||
pe.plan.diag().Errorf(diag.RawMessage(urn, err.Error()))
|
||||
pe.plan.Diag().Errorf(diag.RawMessage(urn, err.Error()))
|
||||
}
|
||||
|
||||
// Execute executes a plan to completion, using the given cancellation context and running a preview
|
||||
|
@ -343,7 +343,7 @@ func (pe *planExecutor) performDeletes(
|
|||
if targetsOpt != nil {
|
||||
resourceToStep := make(map[*resource.State]Step)
|
||||
for _, step := range deleteSteps {
|
||||
resourceToStep[pe.plan.olds[step.Res().URN]] = step
|
||||
resourceToStep[pe.plan.olds[step.URN()]] = step
|
||||
}
|
||||
|
||||
pe.rebuildBaseState(resourceToStep, false /*refresh*/)
|
||||
|
@ -400,7 +400,7 @@ func (pe *planExecutor) retirePendingDeletes(callerCtx context.Context, opts Opt
|
|||
// Submit the deletes for execution and wait for them all to retire.
|
||||
for _, antichain := range antichains {
|
||||
for _, step := range antichain {
|
||||
pe.plan.ctx.StatusDiag.Infof(diag.RawMessage(step.Res().URN, "completing deletion from previous update"))
|
||||
pe.plan.Ctx().StatusDiag.Infof(diag.RawMessage(step.URN(), "completing deletion from previous update"))
|
||||
}
|
||||
|
||||
tok := stepExec.ExecuteParallel(antichain)
|
||||
|
|
|
@ -39,6 +39,8 @@ type Source interface {
|
|||
|
||||
// Project returns the package name of the Pulumi project we are obtaining resources from.
|
||||
Project() tokens.PackageName
|
||||
// Info returns a serializable payload that can be used to stamp snapshots for future reconciliation.
|
||||
Info() interface{}
|
||||
|
||||
// Iterate begins iterating the source. Error is non-nil upon failure; otherwise, a valid iterator is returned.
|
||||
Iterate(ctx context.Context, opts Options, providers ProviderSource) (SourceIterator, result.Result)
|
||||
|
|
|
@ -35,6 +35,7 @@ type errorSource struct {
|
|||
|
||||
func (src *errorSource) Close() error { return nil }
|
||||
func (src *errorSource) Project() tokens.PackageName { return src.project }
|
||||
func (src *errorSource) Info() interface{} { return nil }
|
||||
|
||||
func (src *errorSource) Iterate(
|
||||
ctx context.Context, opts Options, providers ProviderSource) (SourceIterator, result.Result) {
|
||||
|
|
|
@ -41,11 +41,11 @@ import (
|
|||
|
||||
// EvalRunInfo provides information required to execute and deploy resources within a package.
|
||||
type EvalRunInfo struct {
|
||||
Proj *workspace.Project // the package metadata.
|
||||
Pwd string // the package's working directory.
|
||||
Program string // the path to the program.
|
||||
Args []string // any arguments to pass to the package.
|
||||
Target *Target // the target being deployed into.
|
||||
Proj *workspace.Project `json:"proj" yaml:"proj"` // the package metadata.
|
||||
Pwd string `json:"pwd" yaml:"pwd"` // the package's working directory.
|
||||
Program string `json:"program" yaml:"program"` // the path to the program.
|
||||
Args []string `json:"args,omitempty" yaml:"args,omitempty"` // any arguments to pass to the package.
|
||||
Target *Target `json:"target,omitempty" yaml:"target,omitempty"` // the target being deployed into.
|
||||
}
|
||||
|
||||
// NewEvalSource returns a planning source that fetches resources by evaluating a package with a set of args and
|
||||
|
@ -78,6 +78,13 @@ func (src *evalSource) Project() tokens.PackageName {
|
|||
return src.runinfo.Proj.Name
|
||||
}
|
||||
|
||||
// Stack is the name of the stack being targeted by this evaluation source.
|
||||
func (src *evalSource) Stack() tokens.QName {
|
||||
return src.runinfo.Target.Name
|
||||
}
|
||||
|
||||
func (src *evalSource) Info() interface{} { return src.runinfo }
|
||||
|
||||
// Iterate will spawn an evaluator coroutine and prepare to interact with it on subsequent calls to Next.
|
||||
func (src *evalSource) Iterate(
|
||||
ctx context.Context, opts Options, providers ProviderSource) (SourceIterator, result.Result) {
|
||||
|
@ -575,7 +582,51 @@ func (rm *resmon) Invoke(ctx context.Context, req *pulumirpc.InvokeRequest) (*pu
|
|||
func (rm *resmon) StreamInvoke(
|
||||
req *pulumirpc.InvokeRequest, stream pulumirpc.ResourceMonitor_StreamInvokeServer) error {
|
||||
|
||||
return fmt.Errorf("the resource monitor does not implement streaming invokes")
|
||||
tok := tokens.ModuleMember(req.GetTok())
|
||||
label := fmt.Sprintf("ResourceMonitor.StreamInvoke(%s)", tok)
|
||||
|
||||
providerReq, err := parseProviderRequest(tok.Package(), req.GetVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prov, err := getProviderFromSource(rm.providers, rm.defaultProviders, providerReq, req.GetProvider())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, err := plugin.UnmarshalProperties(
|
||||
req.GetArgs(), plugin.MarshalOptions{Label: label, KeepUnknowns: true})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to unmarshal %v args", tok)
|
||||
}
|
||||
|
||||
// Synchronously do the StreamInvoke and then return the arguments. This will block until the
|
||||
// streaming operation completes!
|
||||
logging.V(5).Infof("ResourceMonitor.StreamInvoke received: tok=%v #args=%v", tok, len(args))
|
||||
failures, err := prov.StreamInvoke(tok, args, func(event resource.PropertyMap) error {
|
||||
mret, err := plugin.MarshalProperties(event, plugin.MarshalOptions{Label: label, KeepUnknowns: true})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to marshal return")
|
||||
}
|
||||
|
||||
return stream.Send(&pulumirpc.InvokeResponse{Return: mret})
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "streaming invocation of %v returned an error", tok)
|
||||
}
|
||||
|
||||
var chkfails []*pulumirpc.CheckFailure
|
||||
for _, failure := range failures {
|
||||
chkfails = append(chkfails, &pulumirpc.CheckFailure{
|
||||
Property: string(failure.Property),
|
||||
Reason: failure.Reason,
|
||||
})
|
||||
}
|
||||
|
||||
if len(chkfails) > 0 {
|
||||
return stream.Send(&pulumirpc.InvokeResponse{Failures: chkfails})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadResource reads the current state associated with a resource from its provider plugin.
|
||||
|
|
|
@ -35,6 +35,7 @@ type fixedSource struct {
|
|||
|
||||
func (src *fixedSource) Close() error { return nil }
|
||||
func (src *fixedSource) Project() tokens.PackageName { return src.ctx }
|
||||
func (src *fixedSource) Info() interface{} { return nil }
|
||||
|
||||
func (src *fixedSource) Iterate(
|
||||
ctx context.Context, opts Options, providers ProviderSource) (SourceIterator, result.Result) {
|
||||
|
|
|
@ -32,6 +32,7 @@ type nullSource struct {
|
|||
|
||||
func (src *nullSource) Close() error { return nil }
|
||||
func (src *nullSource) Project() tokens.PackageName { return "" }
|
||||
func (src *nullSource) Info() interface{} { return nil }
|
||||
|
||||
func (src *nullSource) Iterate(
|
||||
ctx context.Context, opts Options, providers ProviderSource) (SourceIterator, result.Result) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/pulumi/pulumi/sdk/v2/go/common/diag/colors"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/resource/plugin"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/tokens"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
||||
"github.com/pulumi/pulumi/sdk/v2/go/common/util/logging"
|
||||
)
|
||||
|
@ -44,6 +45,8 @@ type Step interface {
|
|||
Apply(preview bool) (resource.Status, StepCompleteFunc, error) // applies or previews this step.
|
||||
|
||||
Op() StepOp // the operation performed by this step.
|
||||
URN() resource.URN // the resource URN (for before and after).
|
||||
Type() tokens.Type // the type affected by this step.
|
||||
Provider() string // the provider reference for this step.
|
||||
Old() *resource.State // the state of the resource before performing this step.
|
||||
New() *resource.State // the state of the resource after performing this step.
|
||||
|
@ -109,7 +112,9 @@ func NewSkippedCreateStep(plan *Plan, reg RegisterResourceEvent, new *resource.S
|
|||
|
||||
func (s *SameStep) Op() StepOp { return OpSame }
|
||||
func (s *SameStep) Plan() *Plan { return s.plan }
|
||||
func (s *SameStep) Type() tokens.Type { return s.new.Type }
|
||||
func (s *SameStep) Provider() string { return s.new.Provider }
|
||||
func (s *SameStep) URN() resource.URN { return s.new.URN }
|
||||
func (s *SameStep) Old() *resource.State { return s.old }
|
||||
func (s *SameStep) New() *resource.State { return s.new }
|
||||
func (s *SameStep) Res() *resource.State { return s.new }
|
||||
|
@ -191,7 +196,9 @@ func (s *CreateStep) Op() StepOp {
|
|||
return OpCreate
|
||||
}
|
||||
func (s *CreateStep) Plan() *Plan { return s.plan }
|
||||
func (s *CreateStep) Type() tokens.Type { return s.new.Type }
|
||||
func (s *CreateStep) Provider() string { return s.new.Provider }
|
||||
func (s *CreateStep) URN() resource.URN { return s.new.URN }
|
||||
func (s *CreateStep) Old() *resource.State { return s.old }
|
||||
func (s *CreateStep) New() *resource.State { return s.new }
|
||||
func (s *CreateStep) Res() *resource.State { return s.new }
|
||||
|
@ -211,7 +218,7 @@ func (s *CreateStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err
|
|||
return resource.StatusOK, nil, err
|
||||
}
|
||||
|
||||
id, outs, rst, err := prov.Create(s.new.URN, s.new.Inputs, s.new.CustomTimeouts.Create)
|
||||
id, outs, rst, err := prov.Create(s.URN(), s.new.Inputs, s.new.CustomTimeouts.Create)
|
||||
if err != nil {
|
||||
if rst != resource.StatusPartialFailure {
|
||||
return rst, nil, err
|
||||
|
@ -309,7 +316,9 @@ func (s *DeleteStep) Op() StepOp {
|
|||
return OpDelete
|
||||
}
|
||||
func (s *DeleteStep) Plan() *Plan { return s.plan }
|
||||
func (s *DeleteStep) Type() tokens.Type { return s.old.Type }
|
||||
func (s *DeleteStep) Provider() string { return s.old.Provider }
|
||||
func (s *DeleteStep) URN() resource.URN { return s.old.URN }
|
||||
func (s *DeleteStep) Old() *resource.State { return s.old }
|
||||
func (s *DeleteStep) New() *resource.State { return nil }
|
||||
func (s *DeleteStep) Res() *resource.State { return s.old }
|
||||
|
@ -331,7 +340,7 @@ func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err
|
|||
return resource.StatusOK, nil, err
|
||||
}
|
||||
|
||||
if rst, err := prov.Delete(s.old.URN, s.old.ID, s.old.Outputs, s.old.CustomTimeouts.Delete); err != nil {
|
||||
if rst, err := prov.Delete(s.URN(), s.old.ID, s.old.Outputs, s.old.CustomTimeouts.Delete); err != nil {
|
||||
return rst, nil, err
|
||||
}
|
||||
}
|
||||
|
@ -358,7 +367,9 @@ func (s *RemovePendingReplaceStep) Op() StepOp {
|
|||
return OpRemovePendingReplace
|
||||
}
|
||||
func (s *RemovePendingReplaceStep) Plan() *Plan { return s.plan }
|
||||
func (s *RemovePendingReplaceStep) Type() tokens.Type { return s.old.Type }
|
||||
func (s *RemovePendingReplaceStep) Provider() string { return s.old.Provider }
|
||||
func (s *RemovePendingReplaceStep) URN() resource.URN { return s.old.URN }
|
||||
func (s *RemovePendingReplaceStep) Old() *resource.State { return s.old }
|
||||
func (s *RemovePendingReplaceStep) New() *resource.State { return nil }
|
||||
func (s *RemovePendingReplaceStep) Res() *resource.State { return s.old }
|
||||
|
@ -411,7 +422,9 @@ func NewUpdateStep(plan *Plan, reg RegisterResourceEvent, old *resource.State,
|
|||
|
||||
func (s *UpdateStep) Op() StepOp { return OpUpdate }
|
||||
func (s *UpdateStep) Plan() *Plan { return s.plan }
|
||||
func (s *UpdateStep) Type() tokens.Type { return s.new.Type }
|
||||
func (s *UpdateStep) Provider() string { return s.new.Provider }
|
||||
func (s *UpdateStep) URN() resource.URN { return s.new.URN }
|
||||
func (s *UpdateStep) Old() *resource.State { return s.old }
|
||||
func (s *UpdateStep) New() *resource.State { return s.new }
|
||||
func (s *UpdateStep) Res() *resource.State { return s.new }
|
||||
|
@ -434,7 +447,7 @@ func (s *UpdateStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err
|
|||
}
|
||||
|
||||
// Update to the combination of the old "all" state, but overwritten with new inputs.
|
||||
outs, rst, upderr := prov.Update(s.old.URN, s.old.ID, s.old.Outputs, s.new.Inputs,
|
||||
outs, rst, upderr := prov.Update(s.URN(), s.old.ID, s.old.Outputs, s.new.Inputs,
|
||||
s.new.CustomTimeouts.Update, s.ignoreChanges)
|
||||
if upderr != nil {
|
||||
if rst != resource.StatusPartialFailure {
|
||||
|
@ -502,7 +515,9 @@ func NewReplaceStep(plan *Plan, old *resource.State, new *resource.State,
|
|||
|
||||
func (s *ReplaceStep) Op() StepOp { return OpReplace }
|
||||
func (s *ReplaceStep) Plan() *Plan { return s.plan }
|
||||
func (s *ReplaceStep) Type() tokens.Type { return s.new.Type }
|
||||
func (s *ReplaceStep) Provider() string { return s.new.Provider }
|
||||
func (s *ReplaceStep) URN() resource.URN { return s.new.URN }
|
||||
func (s *ReplaceStep) Old() *resource.State { return s.old }
|
||||
func (s *ReplaceStep) New() *resource.State { return s.new }
|
||||
func (s *ReplaceStep) Res() *resource.State { return s.new }
|
||||
|
@ -581,7 +596,9 @@ func (s *ReadStep) Op() StepOp {
|
|||
}
|
||||
|
||||
func (s *ReadStep) Plan() *Plan { return s.plan }
|
||||
func (s *ReadStep) Type() tokens.Type { return s.new.Type }
|
||||
func (s *ReadStep) Provider() string { return s.new.Provider }
|
||||
func (s *ReadStep) URN() resource.URN { return s.new.URN }
|
||||
func (s *ReadStep) Old() *resource.State { return s.old }
|
||||
func (s *ReadStep) New() *resource.State { return s.new }
|
||||
func (s *ReadStep) Res() *resource.State { return s.new }
|
||||
|
@ -666,7 +683,9 @@ func NewRefreshStep(plan *Plan, old *resource.State, done chan<- bool) Step {
|
|||
|
||||
func (s *RefreshStep) Op() StepOp { return OpRefresh }
|
||||
func (s *RefreshStep) Plan() *Plan { return s.plan }
|
||||
func (s *RefreshStep) Type() tokens.Type { return s.old.Type }
|
||||
func (s *RefreshStep) Provider() string { return s.old.Provider }
|
||||
func (s *RefreshStep) URN() resource.URN { return s.old.URN }
|
||||
func (s *RefreshStep) Old() *resource.State { return s.old }
|
||||
func (s *RefreshStep) New() *resource.State { return s.new }
|
||||
func (s *RefreshStep) Res() *resource.State { return s.old }
|
||||
|
@ -719,7 +738,7 @@ func (s *RefreshStep) Apply(preview bool) (resource.Status, StepCompleteFunc, er
|
|||
// `pulumi up` will surface them to the user.
|
||||
err = nil
|
||||
msg := fmt.Sprintf("Refreshed resource is in an unhealthy state:\n* %s", strings.Join(initErrors, "\n* "))
|
||||
s.plan.diag().Warningf(diag.RawMessage(s.old.URN, msg))
|
||||
s.Plan().Diag().Warningf(diag.RawMessage(s.URN(), msg))
|
||||
}
|
||||
}
|
||||
outputs := refreshed.Outputs
|
||||
|
@ -807,7 +826,9 @@ func (s *ImportStep) Op() StepOp {
|
|||
}
|
||||
|
||||
func (s *ImportStep) Plan() *Plan { return s.plan }
|
||||
func (s *ImportStep) Type() tokens.Type { return s.new.Type }
|
||||
func (s *ImportStep) Provider() string { return s.new.Provider }
|
||||
func (s *ImportStep) URN() resource.URN { return s.new.URN }
|
||||
func (s *ImportStep) Old() *resource.State { return s.old }
|
||||
func (s *ImportStep) New() *resource.State { return s.new }
|
||||
func (s *ImportStep) Res() *resource.State { return s.new }
|
||||
|
@ -1030,16 +1051,16 @@ func (op StepOp) Suffix() string {
|
|||
|
||||
// getProvider fetches the provider for the given step.
|
||||
func getProvider(s Step) (plugin.Provider, error) {
|
||||
if providers.IsProviderType(s.Res().URN.Type()) {
|
||||
if providers.IsProviderType(s.Type()) {
|
||||
return s.Plan().providers, nil
|
||||
}
|
||||
ref, err := providers.ParseReference(s.Provider())
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("bad provider reference '%v' for resource %v: %v", s.Provider(), s.Res().URN, err)
|
||||
return nil, errors.Errorf("bad provider reference '%v' for resource %v: %v", s.Provider(), s.URN(), err)
|
||||
}
|
||||
provider, ok := s.Plan().GetProvider(ref)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unknown provider '%v' for resource %v", s.Provider(), s.Res().URN)
|
||||
return nil, errors.Errorf("unknown provider '%v' for resource %v", s.Provider(), s.URN())
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
|
|
|
@ -173,8 +173,8 @@ func (se *stepExecutor) ExecuteRegisterResourceOutputs(e RegisterResourceOutputs
|
|||
// or 2) promote RRE to be step-like so that it can be scheduled as if it were a step. Neither
|
||||
// of these are particularly appealing right now.
|
||||
outErr := errors.Wrap(eventerr, "resource complete event returned an error")
|
||||
diagMsg := diag.RawMessage(reg.Res().URN, outErr.Error())
|
||||
se.plan.diag().Errorf(diagMsg)
|
||||
diagMsg := diag.RawMessage(reg.URN(), outErr.Error())
|
||||
se.plan.Diag().Errorf(diagMsg)
|
||||
se.cancelDueToError()
|
||||
return
|
||||
}
|
||||
|
@ -213,13 +213,13 @@ func (se *stepExecutor) executeChain(workerID int, chain chain) {
|
|||
for _, step := range chain {
|
||||
select {
|
||||
case <-se.ctx.Done():
|
||||
se.log(workerID, "step %v on %v canceled", step.Op(), step.Res().URN)
|
||||
se.log(workerID, "step %v on %v canceled", step.Op(), step.URN())
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
if err := se.executeStep(workerID, step); err != nil {
|
||||
se.log(workerID, "step %v on %v failed, signalling cancellation", step.Op(), step.Res().URN)
|
||||
se.log(workerID, "step %v on %v failed, signalling cancellation", step.Op(), step.URN())
|
||||
se.cancelDueToError()
|
||||
if err != errStepApplyFailed {
|
||||
// Step application errors are recorded by the OnResourceStepPost callback. This is confusing,
|
||||
|
@ -227,8 +227,8 @@ func (se *stepExecutor) executeChain(workerID int, chain chain) {
|
|||
//
|
||||
// The errStepApplyFailed sentinel signals that the error that failed this chain was a step apply
|
||||
// error and that we shouldn't log it. Everything else should be logged to the diag system as usual.
|
||||
diagMsg := diag.RawMessage(step.Res().URN, err.Error())
|
||||
se.plan.diag().Errorf(diagMsg)
|
||||
diagMsg := diag.RawMessage(step.URN(), err.Error())
|
||||
se.plan.Diag().Errorf(diagMsg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -262,23 +262,23 @@ func (se *stepExecutor) executeStep(workerID int, step Step) error {
|
|||
var err error
|
||||
payload, err = events.OnResourceStepPre(step)
|
||||
if err != nil {
|
||||
se.log(workerID, "step %v on %v failed pre-resource step: %v", step.Op(), step.Res().URN, err)
|
||||
se.log(workerID, "step %v on %v failed pre-resource step: %v", step.Op(), step.URN(), err)
|
||||
return errors.Wrap(err, "pre-step event returned an error")
|
||||
}
|
||||
}
|
||||
|
||||
se.log(workerID, "applying step %v on %v (preview %v)", step.Op(), step.Res().URN, se.preview)
|
||||
se.log(workerID, "applying step %v on %v (preview %v)", step.Op(), step.URN(), se.preview)
|
||||
status, stepComplete, err := step.Apply(se.preview)
|
||||
|
||||
if err == nil {
|
||||
// If we have a state object, and this is a create or update, remember it, as we may need to update it later.
|
||||
if step.Logical() && step.New() != nil {
|
||||
if prior, has := se.pendingNews.Load(step.Res().URN); has {
|
||||
if prior, has := se.pendingNews.Load(step.URN()); has {
|
||||
return errors.Errorf(
|
||||
"resource '%s' registered twice (%s and %s)", step.Res().URN, prior.(Step).Op(), step.Op())
|
||||
"resource '%s' registered twice (%s and %s)", step.URN(), prior.(Step).Op(), step.Op())
|
||||
}
|
||||
|
||||
se.pendingNews.Store(step.Res().URN, step)
|
||||
se.pendingNews.Store(step.URN(), step)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ func (se *stepExecutor) executeStep(workerID int, step Step) error {
|
|||
|
||||
if events != nil {
|
||||
if postErr := events.OnResourceStepPost(payload, step, status, err); postErr != nil {
|
||||
se.log(workerID, "step %v on %v failed post-resource step: %v", step.Op(), step.Res().URN, postErr)
|
||||
se.log(workerID, "step %v on %v failed post-resource step: %v", step.Op(), step.URN(), postErr)
|
||||
return errors.Wrap(postErr, "post-step event returned an error")
|
||||
}
|
||||
}
|
||||
|
@ -302,12 +302,12 @@ func (se *stepExecutor) executeStep(workerID int, step Step) error {
|
|||
// Calling stepComplete allows steps that depend on this step to continue. OnResourceStepPost saved the results
|
||||
// of the step in the snapshot, so we are ready to go.
|
||||
if stepComplete != nil {
|
||||
se.log(workerID, "step %v on %v retired", step.Op(), step.Res().URN)
|
||||
se.log(workerID, "step %v on %v retired", step.Op(), step.URN())
|
||||
stepComplete()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
se.log(workerID, "step %v on %v failed with an error: %v", step.Op(), step.Res().URN, err)
|
||||
se.log(workerID, "step %v on %v failed with an error: %v", step.Op(), step.URN(), err)
|
||||
return errStepApplyFailed
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ func (sg *stepGenerator) GenerateReadSteps(event ReadResourceEvent) ([]Step, res
|
|||
nil, /* customTimeouts */
|
||||
"", /* importID */
|
||||
)
|
||||
old, hasOld := sg.plan.olds[urn]
|
||||
old, hasOld := sg.plan.Olds()[urn]
|
||||
|
||||
// If the snapshot has an old resource for this URN and it's not external, we're going
|
||||
// to have to delete the old resource and conceptually replace it with the resource we
|
||||
|
@ -176,9 +176,9 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
// Give a particular error in that case to let them know. Also mark that we're
|
||||
// in an error state so that we eventually will error out of the entire
|
||||
// application run.
|
||||
d := diag.GetResourceWillBeCreatedButWasNotSpecifiedInTargetList(step.Res().URN)
|
||||
d := diag.GetResourceWillBeCreatedButWasNotSpecifiedInTargetList(step.URN())
|
||||
|
||||
sg.plan.diag().Errorf(d, step.Res().URN, urn)
|
||||
sg.plan.Diag().Errorf(d, step.URN(), urn)
|
||||
sg.sawError = true
|
||||
|
||||
if !sg.plan.preview {
|
||||
|
@ -209,7 +209,7 @@ func (sg *stepGenerator) generateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
if sg.urns[urn] {
|
||||
invalid = true
|
||||
// TODO[pulumi/pulumi-framework#19]: improve this error message!
|
||||
sg.plan.diag().Errorf(diag.GetDuplicateResourceURNError(urn), urn)
|
||||
sg.plan.Diag().Errorf(diag.GetDuplicateResourceURNError(urn), urn)
|
||||
}
|
||||
sg.urns[urn] = true
|
||||
|
||||
|
@ -221,14 +221,14 @@ func (sg *stepGenerator) generateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
var old *resource.State
|
||||
var hasOld bool
|
||||
for _, urnOrAlias := range append([]resource.URN{urn}, goal.Aliases...) {
|
||||
old, hasOld = sg.plan.olds[urnOrAlias]
|
||||
old, hasOld = sg.plan.Olds()[urnOrAlias]
|
||||
if hasOld {
|
||||
oldInputs = old.Inputs
|
||||
oldOutputs = old.Outputs
|
||||
if urnOrAlias != urn {
|
||||
if previousAliasURN, alreadyAliased := sg.aliased[urnOrAlias]; alreadyAliased {
|
||||
invalid = true
|
||||
sg.plan.diag().Errorf(diag.GetDuplicateResourceAliasError(urn), urnOrAlias, urn, previousAliasURN)
|
||||
sg.plan.Diag().Errorf(diag.GetDuplicateResourceAliasError(urn), urnOrAlias, urn, previousAliasURN)
|
||||
}
|
||||
sg.aliased[urnOrAlias] = urn
|
||||
}
|
||||
|
@ -725,7 +725,7 @@ func (sg *stepGenerator) GenerateDeletes(targetsOpt map[resource.URN]bool) ([]St
|
|||
if allowedResourcesToDelete != nil {
|
||||
filtered := []Step{}
|
||||
for _, step := range dels {
|
||||
if _, has := allowedResourcesToDelete[step.Res().URN]; has {
|
||||
if _, has := allowedResourcesToDelete[step.URN()]; has {
|
||||
filtered = append(filtered, step)
|
||||
}
|
||||
}
|
||||
|
@ -735,7 +735,7 @@ func (sg *stepGenerator) GenerateDeletes(targetsOpt map[resource.URN]bool) ([]St
|
|||
|
||||
deletingUnspecifiedTarget := false
|
||||
for _, step := range dels {
|
||||
urn := step.Res().URN
|
||||
urn := step.URN()
|
||||
if targetsOpt != nil && !targetsOpt[urn] && !sg.opts.TargetDependents {
|
||||
d := diag.GetResourceWillBeDestroyedButWasNotSpecifiedInTargetList(urn)
|
||||
|
||||
|
@ -744,7 +744,7 @@ func (sg *stepGenerator) GenerateDeletes(targetsOpt map[resource.URN]bool) ([]St
|
|||
// re-running the operation.
|
||||
//
|
||||
// Mark that step generation entered an error state so that the entire app run fails.
|
||||
sg.plan.diag().Errorf(d, urn)
|
||||
sg.plan.Diag().Errorf(d, urn)
|
||||
sg.sawError = true
|
||||
|
||||
deletingUnspecifiedTarget = true
|
||||
|
@ -814,7 +814,7 @@ func (sg *stepGenerator) determineAllowedResourcesToDeleteFromTargets(
|
|||
}
|
||||
|
||||
if _, has := resourcesToDelete[res.Parent]; has {
|
||||
sg.plan.diag().Errorf(diag.GetCannotDeleteParentResourceWithoutAlsoDeletingChildError(res.Parent),
|
||||
sg.plan.Diag().Errorf(diag.GetCannotDeleteParentResourceWithoutAlsoDeletingChildError(res.Parent),
|
||||
res.Parent, res.URN)
|
||||
return nil, result.Bail()
|
||||
}
|
||||
|
@ -1084,10 +1084,10 @@ func issueCheckErrors(plan *Plan, new *resource.State, urn resource.URN, failure
|
|||
inputs := new.Inputs
|
||||
for _, failure := range failures {
|
||||
if failure.Property != "" {
|
||||
plan.diag().Errorf(diag.GetResourcePropertyInvalidValueError(urn),
|
||||
plan.Diag().Errorf(diag.GetResourcePropertyInvalidValueError(urn),
|
||||
new.Type, urn.Name(), failure.Property, inputs[failure.Property], failure.Reason)
|
||||
} else {
|
||||
plan.diag().Errorf(
|
||||
plan.Diag().Errorf(
|
||||
diag.GetResourceInvalidError(urn), new.Type, urn.Name(), failure.Reason)
|
||||
}
|
||||
}
|
||||
|
@ -1150,12 +1150,12 @@ func (sg *stepGenerator) loadResourceProvider(
|
|||
contract.Assert(provider != "")
|
||||
ref, refErr := providers.ParseReference(provider)
|
||||
if refErr != nil {
|
||||
sg.plan.diag().Errorf(diag.GetBadProviderError(urn), provider, urn, refErr)
|
||||
sg.plan.Diag().Errorf(diag.GetBadProviderError(urn), provider, urn, refErr)
|
||||
return nil, result.Bail()
|
||||
}
|
||||
p, ok := sg.plan.GetProvider(ref)
|
||||
if !ok {
|
||||
sg.plan.diag().Errorf(diag.GetUnknownProviderError(urn), provider, urn)
|
||||
sg.plan.Diag().Errorf(diag.GetUnknownProviderError(urn), provider, urn)
|
||||
return nil, result.Bail()
|
||||
}
|
||||
return p, nil
|
||||
|
@ -1340,7 +1340,7 @@ func (sg *stepGenerator) AnalyzeResources() result.Result {
|
|||
}
|
||||
}
|
||||
if urn == "" {
|
||||
urn = resource.DefaultRootStackURN(sg.plan.target.Name, sg.plan.source.Project())
|
||||
urn = resource.DefaultRootStackURN(sg.plan.Target().Name, sg.plan.source.Project())
|
||||
}
|
||||
sg.opts.Events.OnPolicyViolation(urn, d)
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ publish_containers() {
|
|||
docker logout
|
||||
|
||||
# This publishes the SDK specific containers and uses a dispatch event to trigger a GitHub Action
|
||||
pulumictl create containers ${CLI_VERSION}
|
||||
pulumictl create containers "${CLI_VERSION//v}"
|
||||
}
|
||||
|
||||
echo_header "Building Pulumi containers (${CLI_VERSION})"
|
||||
|
|
|
@ -24,6 +24,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
|
|||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -80,10 +81,14 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
|
|
|
@ -17,14 +17,24 @@
|
|||
<NoWarn>NU5105</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Library.fs" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Pulumi\Pulumi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Library.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\pulumi_logo_64x64.png">
|
||||
<Pack>True</Pack>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright 2016-2019, Pulumi Corporation
|
||||
// Copyright 2016-2020, Pulumi Corporation
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pulumi
|
||||
|
@ -92,6 +93,7 @@ namespace Pulumi
|
|||
{
|
||||
if (_inFlightTasks.Count == 0)
|
||||
{
|
||||
// No more tasks in flight: exit the loop.
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -99,24 +101,56 @@ namespace Pulumi
|
|||
tasks.AddRange(_inFlightTasks.Keys);
|
||||
}
|
||||
|
||||
// Now, wait for one of them to finish.
|
||||
var task = await Task.WhenAny(tasks).ConfigureAwait(false);
|
||||
List<string> descriptions;
|
||||
lock (_inFlightTasks)
|
||||
// Wait for one of the two events to happen:
|
||||
// 1. All tasks in the list complete successfully, or
|
||||
// 2. Any task throws an exception.
|
||||
// There's no standard API with this semantics, so we create a custom completion source that is
|
||||
// completed when remaining count is zero, or when an exception is thrown.
|
||||
var remaining = tasks.Count;
|
||||
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
tasks.ForEach(HandleCompletion);
|
||||
async void HandleCompletion(Task task)
|
||||
{
|
||||
// Once finished, remove it from the set of tasks that are running.
|
||||
descriptions = _inFlightTasks[task];
|
||||
_inFlightTasks.Remove(task);
|
||||
try
|
||||
{
|
||||
// Wait for the task completion.
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
// Log the descriptions of completed tasks.
|
||||
var descriptions = _inFlightTasks[task];
|
||||
foreach (var description in descriptions)
|
||||
{
|
||||
Serilog.Log.Information($"Completed task: {description}");
|
||||
}
|
||||
|
||||
// Check if all the tasks are completed and signal the completion source if so.
|
||||
if (Interlocked.Decrement(ref remaining) == 0)
|
||||
{
|
||||
tcs.TrySetResult(0);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Once finished, remove the task from the set of tasks that are running.
|
||||
lock (_inFlightTasks)
|
||||
{
|
||||
_inFlightTasks.Remove(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var description in descriptions)
|
||||
Serilog.Log.Information($"Completed task: {description}");
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// Now actually await that completed task so that we will realize any exceptions
|
||||
// is may have thrown.
|
||||
await task.ConfigureAwait(false);
|
||||
// Now actually await that combined task and realize any exceptions it may have thrown.
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -21,26 +21,26 @@ namespace Pulumi
|
|||
var type = res.GetResourceType();
|
||||
var name = res.GetResourceName();
|
||||
|
||||
Log.Debug($"Gathering explicit dependencies: t={type}, name={name}, custom={custom}");
|
||||
LogExcessive($"Gathering explicit dependencies: t={type}, name={name}, custom={custom}");
|
||||
var explicitDirectDependencies = new HashSet<Resource>(
|
||||
await GatherExplicitDependenciesAsync(options.DependsOn).ConfigureAwait(false));
|
||||
Log.Debug($"Gathered explicit dependencies: t={type}, name={name}, custom={custom}");
|
||||
LogExcessive($"Gathered explicit dependencies: t={type}, name={name}, custom={custom}");
|
||||
|
||||
// Serialize out all our props to their final values. In doing so, we'll also collect all
|
||||
// the Resources pointed to by any Dependency objects we encounter, adding them to 'propertyDependencies'.
|
||||
Log.Debug($"Serializing properties: t={type}, name={name}, custom={custom}");
|
||||
LogExcessive($"Serializing properties: t={type}, name={name}, custom={custom}");
|
||||
var dictionary = await args.ToDictionaryAsync().ConfigureAwait(false);
|
||||
var (serializedProps, propertyToDirectDependencies) =
|
||||
await SerializeResourcePropertiesAsync(label, dictionary).ConfigureAwait(false);
|
||||
Log.Debug($"Serialized properties: t={type}, name={name}, custom={custom}");
|
||||
LogExcessive($"Serialized properties: t={type}, name={name}, custom={custom}");
|
||||
|
||||
// Wait for the parent to complete.
|
||||
// If no parent was provided, parent to the root resource.
|
||||
Log.Debug($"Getting parent urn: t={type}, name={name}, custom={custom}");
|
||||
LogExcessive($"Getting parent urn: t={type}, name={name}, custom={custom}");
|
||||
var parentURN = options.Parent != null
|
||||
? await options.Parent.Urn.GetValueAsync().ConfigureAwait(false)
|
||||
: await GetRootResourceAsync(type).ConfigureAwait(false);
|
||||
Log.Debug($"Got parent urn: t={type}, name={name}, custom={custom}");
|
||||
LogExcessive($"Got parent urn: t={type}, name={name}, custom={custom}");
|
||||
|
||||
string? providerRef = null;
|
||||
if (custom)
|
||||
|
@ -89,6 +89,12 @@ namespace Pulumi
|
|||
allDirectDependencyURNs,
|
||||
propertyToDirectDependencyURNs,
|
||||
aliases);
|
||||
|
||||
void LogExcessive(string message)
|
||||
{
|
||||
if (_excessiveDebugOutput)
|
||||
Log.Debug(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static Task<ImmutableArray<Resource>> GatherExplicitDependenciesAsync(InputList<Resource> resources)
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Pulumi
|
|||
{
|
||||
public partial class Deployment
|
||||
{
|
||||
internal static bool _excessiveDebugOutput = true;
|
||||
internal static bool _excessiveDebugOutput = false;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SerializeResourcePropertiesAsync"/> walks the props object passed in,
|
||||
|
|
|
@ -19,7 +19,14 @@
|
|||
<NoWarn>1701;1702;1591;NU5105</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="2.9.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
@ -15,6 +15,8 @@ require (
|
|||
github.com/golang/protobuf v1.3.5
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
|
||||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/kr/pretty v0.2.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.8 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
|
|
|
@ -32,6 +32,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
|
|||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -104,10 +105,14 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
|
|
|
@ -208,3 +208,21 @@ func decryptAES256GCM(ciphertext []byte, key []byte, nonce []byte) (string, erro
|
|||
|
||||
return string(msg), err
|
||||
}
|
||||
|
||||
// Crypter that just adds a prefix to the plaintext string when encrypting,
|
||||
// and removes the prefix from the ciphertext when decrypting, for use in tests.
|
||||
type prefixCrypter struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func newPrefixCrypter(prefix string) Crypter {
|
||||
return prefixCrypter{prefix: prefix}
|
||||
}
|
||||
|
||||
func (c prefixCrypter) DecryptValue(ciphertext string) (string, error) {
|
||||
return strings.TrimPrefix(ciphertext, c.prefix), nil
|
||||
}
|
||||
|
||||
func (c prefixCrypter) EncryptValue(plaintext string) (string, error) {
|
||||
return c.prefix + plaintext, nil
|
||||
}
|
||||
|
|
|
@ -43,6 +43,20 @@ func (m Map) Decrypt(decrypter Decrypter) (map[Key]string, error) {
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func (m Map) Copy(decrypter Decrypter, encrypter Encrypter) (Map, error) {
|
||||
newConfig := make(Map)
|
||||
for k, c := range m {
|
||||
val, err := c.Copy(decrypter, encrypter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newConfig[k] = val
|
||||
}
|
||||
|
||||
return newConfig, nil
|
||||
}
|
||||
|
||||
// HasSecureValue returns true if the config map contains a secure (encrypted) value.
|
||||
func (m Map) HasSecureValue() bool {
|
||||
for _, v := range m {
|
||||
|
|
|
@ -1170,6 +1170,82 @@ func TestSetFail(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCopyMap(t *testing.T) {
|
||||
tests := []struct {
|
||||
Config Map
|
||||
Expected Map
|
||||
}{
|
||||
{
|
||||
Config: Map{
|
||||
MustMakeKey("my", "testKey"): NewValue("testValue"),
|
||||
},
|
||||
Expected: Map{
|
||||
MustMakeKey("my", "testKey"): NewValue("testValue"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: Map{
|
||||
MustMakeKey("my", "testKey"): NewSecureValue("stackAsecurevalue"),
|
||||
},
|
||||
Expected: Map{
|
||||
MustMakeKey("my", "testKey"): NewSecureValue("stackBsecurevalue"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: Map{
|
||||
MustMakeKey("my", "testKey"): NewObjectValue(`{"inner":"value"}`),
|
||||
},
|
||||
Expected: Map{
|
||||
MustMakeKey("my", "testKey"): NewObjectValue(`{"inner":"value"}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: Map{
|
||||
MustMakeKey("my", "testKey"): NewSecureObjectValue(`{"inner":{"secure":"stackAsecurevalue"}}`),
|
||||
},
|
||||
Expected: Map{
|
||||
MustMakeKey("my", "testKey"): NewSecureObjectValue(`{"inner":{"secure":"stackBsecurevalue"}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: Map{
|
||||
//nolint:lll
|
||||
MustMakeKey("my", "testKey"): NewSecureObjectValue(`[{"inner":{"secure":"stackAsecurevalue"}},{"secure":"stackAsecurevalue2"}]`),
|
||||
},
|
||||
Expected: Map{
|
||||
//nolint:lll
|
||||
MustMakeKey("my", "testKey"): NewSecureObjectValue(`[{"inner":{"secure":"stackBsecurevalue"}},{"secure":"stackBsecurevalue2"}]`),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: Map{
|
||||
MustMakeKey("my", "test.Key"): NewValue("testValue"),
|
||||
},
|
||||
Expected: Map{
|
||||
MustMakeKey("my", "test.Key"): NewValue("testValue"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Config: Map{
|
||||
MustMakeKey("my", "name"): NewObjectValue(`[["value"]]`),
|
||||
},
|
||||
Expected: Map{
|
||||
MustMakeKey("my", "name"): NewObjectValue(`[["value"]]`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
|
||||
newConfig, err := test.Config.Copy(newPrefixCrypter("stackA"), newPrefixCrypter("stackB"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.Expected, newConfig)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func roundtripMapYAML(m Map) (Map, error) {
|
||||
return roundtripMap(m, yaml.Marshal, yaml.Unmarshal)
|
||||
}
|
||||
|
|
|
@ -72,6 +72,46 @@ func (c Value) Value(decrypter Decrypter) (string, error) {
|
|||
return decrypter.DecryptValue(c.value)
|
||||
}
|
||||
|
||||
func (c Value) Copy(decrypter Decrypter, encrypter Encrypter) (Value, error) {
|
||||
var val Value
|
||||
raw, err := c.Value(decrypter)
|
||||
if err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
if c.Secure() {
|
||||
if c.Object() {
|
||||
objVal, err := c.ToObject()
|
||||
if err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
encryptedObj, err := reencryptObject(objVal, decrypter, encrypter)
|
||||
if err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
json, err := json.Marshal(encryptedObj)
|
||||
if err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
|
||||
val = NewSecureObjectValue(string(json))
|
||||
} else {
|
||||
enc, eerr := encrypter.EncryptValue(raw)
|
||||
if eerr != nil {
|
||||
return Value{}, eerr
|
||||
}
|
||||
val = NewSecureValue(enc)
|
||||
}
|
||||
} else {
|
||||
if c.Object() {
|
||||
val = NewObjectValue(raw)
|
||||
} else {
|
||||
val = NewValue(raw)
|
||||
}
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (c Value) SecureValues(decrypter Decrypter) ([]string, error) {
|
||||
d := NewTrackingDecrypter(decrypter)
|
||||
if _, err := c.Value(d); err != nil {
|
||||
|
@ -240,6 +280,53 @@ func isSecureValue(v interface{}) (bool, string) {
|
|||
return false, ""
|
||||
}
|
||||
|
||||
func reencryptObject(v interface{}, decrypter Decrypter, encrypter Encrypter) (interface{}, error) {
|
||||
reencryptIt := func(val interface{}) (interface{}, error) {
|
||||
if isSecure, secureVal := isSecureValue(val); isSecure {
|
||||
newVal := NewSecureValue(secureVal)
|
||||
raw, err := newVal.Value(decrypter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encVal, err := encrypter.EncryptValue(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]string)
|
||||
m["secure"] = encVal
|
||||
|
||||
return m, nil
|
||||
}
|
||||
return reencryptObject(val, decrypter, encrypter)
|
||||
}
|
||||
|
||||
switch t := v.(type) {
|
||||
case map[string]interface{}:
|
||||
m := make(map[string]interface{})
|
||||
for key, val := range t {
|
||||
encrypted, err := reencryptIt(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[key] = encrypted
|
||||
}
|
||||
return m, nil
|
||||
case []interface{}:
|
||||
a := make([]interface{}, len(t))
|
||||
for i, val := range t {
|
||||
encrypted, err := reencryptIt(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a[i] = encrypted
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// decryptObject returns a new object with all secure values in the object converted to decrypted strings.
|
||||
func decryptObject(v interface{}, decrypter Decrypter) (interface{}, error) {
|
||||
decryptIt := func(val interface{}) (interface{}, error) {
|
||||
|
|
|
@ -245,6 +245,43 @@ func TestSecureValues(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCopyValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
Val Value
|
||||
Expected Value
|
||||
}{
|
||||
{
|
||||
Val: NewValue("value"),
|
||||
Expected: NewValue("value"),
|
||||
},
|
||||
{
|
||||
Val: NewObjectValue(`{"foo":"bar"}`),
|
||||
Expected: NewObjectValue(`{"foo":"bar"}`),
|
||||
},
|
||||
{
|
||||
Val: NewSecureObjectValue(`{"foo":{"secure":"stackAsecurevalue"}}`),
|
||||
Expected: NewSecureObjectValue(`{"foo":{"secure":"stackBsecurevalue"}}`),
|
||||
},
|
||||
{
|
||||
Val: NewSecureValue("stackAsecurevalue"),
|
||||
Expected: NewSecureValue("stackBsecurevalue"),
|
||||
},
|
||||
{
|
||||
Val: NewSecureObjectValue(`["a",{"secure":"stackAalpha"},{"test":{"secure":"stackAbeta"}}]`),
|
||||
Expected: NewSecureObjectValue(`["a",{"secure":"stackBalpha"},{"test":{"secure":"stackBbeta"}}]`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
|
||||
newConfig, err := test.Val.Copy(newPrefixCrypter("stackA"), newPrefixCrypter("stackB"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.Expected, newConfig)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func roundtripValueYAML(v Value) (Value, error) {
|
||||
return roundtripValue(v, yaml.Marshal, yaml.Unmarshal)
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ func (p PropertyPath) Get(v PropertyValue) (PropertyValue, bool) {
|
|||
return v, true
|
||||
}
|
||||
|
||||
// Set attempts to set the location inside a PropertyValue indicated by the PropertyPath to the given value If any
|
||||
// Set attempts to set the location inside a PropertyValue indicated by the PropertyPath to the given value. If any
|
||||
// component of the path besides the last component does not exist, this function will return false.
|
||||
func (p PropertyPath) Set(dest, v PropertyValue) bool {
|
||||
if len(p) == 0 {
|
||||
|
@ -160,6 +160,72 @@ func (p PropertyPath) Set(dest, v PropertyValue) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Add sets the location inside a PropertyValue indicated by the PropertyPath to the given value. Any components
|
||||
// referred to by the path that do not exist will be created. If there is a mismatch between the type of an existing
|
||||
// component and a key that traverses that component, this function will return false. If the destination is a null
|
||||
// property value, this function will create and return a new property value.
|
||||
func (p PropertyPath) Add(dest, v PropertyValue) (PropertyValue, bool) {
|
||||
if len(p) == 0 {
|
||||
return PropertyValue{}, false
|
||||
}
|
||||
|
||||
// set sets the destination referred to by the last element of the path to the given value.
|
||||
rv := dest
|
||||
set := func(v PropertyValue) {
|
||||
dest, rv = v, v
|
||||
}
|
||||
for _, key := range p {
|
||||
switch key := key.(type) {
|
||||
case int:
|
||||
// This key is an int, so we expect an array.
|
||||
switch {
|
||||
case dest.IsNull():
|
||||
// If the destination array does not exist, create a new array with enough room to store the value at
|
||||
// the requested index.
|
||||
dest = NewArrayProperty(make([]PropertyValue, key+1))
|
||||
set(dest)
|
||||
case dest.IsArray():
|
||||
// If the destination array does exist, ensure that it is large enough to accommodate the requested
|
||||
// index.
|
||||
arr := dest.ArrayValue()
|
||||
if key >= len(arr) {
|
||||
arr = append(make([]PropertyValue, key+1-len(arr)), arr...)
|
||||
v.V = arr
|
||||
}
|
||||
default:
|
||||
return PropertyValue{}, false
|
||||
}
|
||||
destV := dest.ArrayValue()
|
||||
set = func(v PropertyValue) {
|
||||
destV[key] = v
|
||||
}
|
||||
dest = destV[key]
|
||||
case string:
|
||||
// This key is a string, so we expect an object.
|
||||
switch {
|
||||
case dest.IsNull():
|
||||
// If the destination does not exist, create a new object.
|
||||
dest = NewObjectProperty(PropertyMap{})
|
||||
set(dest)
|
||||
case dest.IsObject():
|
||||
// OK
|
||||
default:
|
||||
return PropertyValue{}, false
|
||||
}
|
||||
destV := dest.ObjectValue()
|
||||
set = func(v PropertyValue) {
|
||||
destV[PropertyKey(key)] = v
|
||||
}
|
||||
dest = destV[PropertyKey(key)]
|
||||
default:
|
||||
return PropertyValue{}, false
|
||||
}
|
||||
}
|
||||
|
||||
set(v)
|
||||
return rv, true
|
||||
}
|
||||
|
||||
// Delete attempts to delete the value located by the PropertyPath inside the given PropertyValue. If any component
|
||||
// of the path does not exist, this function will return false.
|
||||
func (p PropertyPath) Delete(dest PropertyValue) bool {
|
||||
|
|
|
@ -133,6 +133,14 @@ func TestPropertyPath(t *testing.T) {
|
|||
u, ok := parsed.Get(value)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, v, u)
|
||||
|
||||
vv := PropertyValue{}
|
||||
vv, ok = parsed.Add(vv, v)
|
||||
assert.True(t, ok)
|
||||
|
||||
u, ok = parsed.Get(vv)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, v, u)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
102
sdk/go/common/util/deepcopy/copy.go
Normal file
102
sdk/go/common/util/deepcopy/copy.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package deepcopy
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Copy returns a deep copy of the provided value.
|
||||
//
|
||||
// If there are multiple references to the same value inside the provided value, the multiply-referenced value will be
|
||||
// copied multiple times.
|
||||
func Copy(i interface{}) interface{} {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
return copy(reflect.ValueOf(i)).Interface()
|
||||
}
|
||||
|
||||
func copy(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() {
|
||||
return v
|
||||
}
|
||||
|
||||
typ := v.Type()
|
||||
switch typ.Kind() {
|
||||
case reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.Complex64, reflect.Complex128,
|
||||
reflect.String,
|
||||
reflect.Func:
|
||||
// These all have value semantics. Return them as-is.
|
||||
return v
|
||||
case reflect.Chan:
|
||||
// Channels have referential semantics, but deep-copying them has no meaning. Return them as-is.
|
||||
return v
|
||||
case reflect.Interface:
|
||||
rv := reflect.New(typ).Elem()
|
||||
if !v.IsNil() {
|
||||
rv.Set(copy(v.Elem()))
|
||||
}
|
||||
return rv
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
elem := copy(v.Elem())
|
||||
if elem.CanAddr() {
|
||||
return elem.Addr()
|
||||
}
|
||||
rv := reflect.New(typ.Elem())
|
||||
rv.Set(elem)
|
||||
return rv
|
||||
case reflect.Array:
|
||||
rv := reflect.New(typ).Elem()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
rv.Index(i).Set(copy(v.Index(i)))
|
||||
}
|
||||
return rv
|
||||
case reflect.Slice:
|
||||
rv := reflect.New(typ).Elem()
|
||||
if !v.IsNil() {
|
||||
rv.Set(reflect.MakeSlice(typ, v.Len(), v.Cap()))
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
rv.Index(i).Set(copy(v.Index(i)))
|
||||
}
|
||||
}
|
||||
return rv
|
||||
case reflect.Map:
|
||||
rv := reflect.New(typ).Elem()
|
||||
if !v.IsNil() {
|
||||
rv.Set(reflect.MakeMap(typ))
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
rv.SetMapIndex(copy(iter.Key()), copy(iter.Value()))
|
||||
}
|
||||
}
|
||||
return rv
|
||||
case reflect.Struct:
|
||||
rv := reflect.New(typ).Elem()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
if f := rv.Field(i); f.CanSet() {
|
||||
f.Set(copy(v.Field(i)))
|
||||
}
|
||||
}
|
||||
return rv
|
||||
default:
|
||||
panic("unexpected kind " + typ.Kind().String())
|
||||
}
|
||||
}
|
80
sdk/go/common/util/deepcopy/copy_test.go
Normal file
80
sdk/go/common/util/deepcopy/copy_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package deepcopy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeepCopy(t *testing.T) {
|
||||
cases := []interface{}{
|
||||
bool(false),
|
||||
bool(true),
|
||||
int(-42),
|
||||
int8(-42),
|
||||
int16(-42),
|
||||
int32(-42),
|
||||
int64(-42),
|
||||
uint(42),
|
||||
uint8(42),
|
||||
uint16(42),
|
||||
uint32(42),
|
||||
uint64(42),
|
||||
float32(3.14159),
|
||||
float64(3.14159),
|
||||
complex64(complex(3.14159, -42)),
|
||||
complex(3.14159, -42),
|
||||
"foo",
|
||||
[2]byte{42, 24},
|
||||
[]byte{0, 1, 2, 3},
|
||||
[]string{"foo", "bar"},
|
||||
map[string]int{
|
||||
"a": 42,
|
||||
"b": 24,
|
||||
},
|
||||
struct {
|
||||
Foo int
|
||||
Bar map[int]int
|
||||
}{
|
||||
Foo: 42,
|
||||
Bar: map[int]int{
|
||||
19: 77,
|
||||
},
|
||||
},
|
||||
[]map[string]string{
|
||||
{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
},
|
||||
{
|
||||
"alpha": "beta",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"bar": []int{42},
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
|
||||
assert.EqualValues(t, c, Copy(c))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -483,6 +483,7 @@ func (ctx *Context) RegisterResource(
|
|||
Aliases: inputs.aliases,
|
||||
AcceptSecrets: true,
|
||||
AdditionalSecretOutputs: inputs.additionalSecretOutputs,
|
||||
Version: inputs.version,
|
||||
})
|
||||
if err != nil {
|
||||
logging.V(9).Infof("RegisterResource(%s, %s): error: %v", t, name, err)
|
||||
|
@ -790,6 +791,7 @@ type resourceInputs struct {
|
|||
ignoreChanges []string
|
||||
aliases []string
|
||||
additionalSecretOutputs []string
|
||||
version string
|
||||
}
|
||||
|
||||
// prepareResourceInputs prepares the inputs for a resource operation, shared between read and register.
|
||||
|
@ -801,7 +803,7 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
|
|||
// Get the parent and dependency URNs from the options, in addition to the protection bit. If there wasn't an
|
||||
// explicit parent, and a root stack resource exists, we will automatically parent to that.
|
||||
parent, optDeps, protect, provider, deleteBeforeReplace,
|
||||
importID, ignoreChanges, additionalSecretOutputs, err := ctx.getOpts(t, providers, opts)
|
||||
importID, ignoreChanges, additionalSecretOutputs, version, err := ctx.getOpts(t, providers, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving options: %w", err)
|
||||
}
|
||||
|
@ -874,6 +876,7 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
|
|||
ignoreChanges: ignoreChanges,
|
||||
aliases: aliases,
|
||||
additionalSecretOutputs: additionalSecretOutputs,
|
||||
version: version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -890,13 +893,13 @@ func getTimeouts(custom *CustomTimeouts) *pulumirpc.RegisterResourceRequest_Cust
|
|||
// getOpts returns a set of resource options from an array of them. This includes the parent URN, any dependency URNs,
|
||||
// a boolean indicating whether the resource is to be protected, and the URN and ID of the resource's provider, if any.
|
||||
func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opts *resourceOptions) (
|
||||
URN, []URN, bool, string, bool, ID, []string, []string, error) {
|
||||
URN, []URN, bool, string, bool, ID, []string, []string, string, error) {
|
||||
|
||||
var importID ID
|
||||
if opts.Import != nil {
|
||||
id, _, _, err := opts.Import.ToIDOutput().awaitID(context.TODO())
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, "", nil, nil, err
|
||||
return "", nil, false, "", false, "", nil, nil, "", err
|
||||
}
|
||||
importID = id
|
||||
}
|
||||
|
@ -905,7 +908,7 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
|
|||
if opts.Parent != nil {
|
||||
urn, _, _, err := opts.Parent.URN().awaitURN(context.TODO())
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, "", nil, nil, err
|
||||
return "", nil, false, "", false, "", nil, nil, "", err
|
||||
}
|
||||
parentURN = urn
|
||||
}
|
||||
|
@ -916,7 +919,7 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
|
|||
for i, r := range opts.DependsOn {
|
||||
urn, _, _, err := r.URN().awaitURN(context.TODO())
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, "", nil, nil, err
|
||||
return "", nil, false, "", false, "", nil, nil, "", err
|
||||
}
|
||||
depURNs[i] = urn
|
||||
}
|
||||
|
@ -932,13 +935,13 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
|
|||
if provider != nil {
|
||||
pr, err := ctx.resolveProviderReference(provider)
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, "", nil, nil, err
|
||||
return "", nil, false, "", false, "", nil, nil, "", err
|
||||
}
|
||||
providerRef = pr
|
||||
}
|
||||
|
||||
return parentURN, depURNs, opts.Protect, providerRef, opts.DeleteBeforeReplace,
|
||||
importID, opts.IgnoreChanges, opts.AdditionalSecretOutputs, nil
|
||||
importID, opts.IgnoreChanges, opts.AdditionalSecretOutputs, opts.Version, nil
|
||||
}
|
||||
|
||||
func (ctx *Context) resolveProviderReference(provider ProviderResource) (string, error) {
|
||||
|
|
|
@ -181,6 +181,10 @@ type resourceOptions struct {
|
|||
// The transformations are applied in order, and are applied prior to transformation and to parents
|
||||
// walking from the resource up to the stack.
|
||||
Transformations []ResourceTransformation
|
||||
// An optional version, corresponding to the version of the provider plugin that should be used when operating on
|
||||
// this resource. This version overrides the version information inferred from the current package and should
|
||||
// rarely be used.
|
||||
Version string
|
||||
}
|
||||
|
||||
type invokeOptions struct {
|
||||
|
@ -315,6 +319,15 @@ func Timeouts(o *CustomTimeouts) ResourceOption {
|
|||
})
|
||||
}
|
||||
|
||||
// An optional version, corresponding to the version of the provider plugin that should be used when operating on
|
||||
// this resource. This version overrides the version information inferred from the current package and should
|
||||
// rarely be used.
|
||||
func Version(o string) ResourceOption {
|
||||
return resourceOption(func(ro *resourceOptions) {
|
||||
ro.Version = o
|
||||
})
|
||||
}
|
||||
|
||||
// Ignore changes to any of the specified properties.
|
||||
func IgnoreChanges(o []string) ResourceOption {
|
||||
return resourceOption(func(ro *resourceOptions) {
|
||||
|
|
|
@ -104,7 +104,7 @@ export async function streamInvoke(
|
|||
queue.push(live);
|
||||
});
|
||||
call.on("error", (err: any) => {
|
||||
if (err.code === 1 && err.details === "Cancelled") {
|
||||
if (err.code === 1) {
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
|
|
|
@ -18,8 +18,6 @@ providers and libraries in the Pulumi ecosystem use to create and manage
|
|||
resources.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
|
||||
# Make all module members inside of this package available as package members.
|
||||
from .asset import (
|
||||
Asset,
|
||||
|
@ -86,6 +84,4 @@ from .stack_reference import (
|
|||
StackReference,
|
||||
)
|
||||
|
||||
# Make subpackages available.
|
||||
for pkg in ['runtime', 'dynamic', 'policy']:
|
||||
importlib.import_module(f'{__name__}.{pkg}')
|
||||
from . import runtime, dynamic, policy
|
||||
|
|
|
@ -18,10 +18,7 @@ Assets are the Pulumi notion of data blobs that can be passed to resources.
|
|||
from os import PathLike, fspath
|
||||
from typing import Dict, Union
|
||||
|
||||
from .runtime import known_types
|
||||
|
||||
|
||||
@known_types.asset
|
||||
class Asset:
|
||||
"""
|
||||
Asset represents a single blob of text or data that is managed as a first
|
||||
|
@ -29,64 +26,59 @@ class Asset:
|
|||
"""
|
||||
|
||||
|
||||
@known_types.file_asset
|
||||
class FileAsset(Asset):
|
||||
path: str
|
||||
|
||||
"""
|
||||
A FileAsset is a kind of asset produced from a given path to a file on
|
||||
the local filesysetm.
|
||||
the local filesystem.
|
||||
"""
|
||||
path: str
|
||||
|
||||
def __init__(self, path: Union[str, PathLike]) -> None:
|
||||
|
||||
if not isinstance(path, (str, PathLike)):
|
||||
raise TypeError("FileAsset path must be a string or os.PathLike")
|
||||
self.path = fspath(path)
|
||||
|
||||
|
||||
@known_types.string_asset
|
||||
class StringAsset(Asset):
|
||||
text: str
|
||||
|
||||
"""
|
||||
A StringAsset is a kind of asset produced from an in-memory UTF-8 encoded string.
|
||||
"""
|
||||
text: str
|
||||
|
||||
def __init__(self, text: str) -> None:
|
||||
if not isinstance(text, str):
|
||||
raise TypeError("StringAsset data must be a string")
|
||||
self.text = text
|
||||
|
||||
|
||||
@known_types.remote_asset
|
||||
class RemoteAsset(Asset):
|
||||
uri: str
|
||||
|
||||
"""
|
||||
A RemoteAsset is a kind of asset produced from a given URI string. The URI's scheme
|
||||
dictates the protocol for fetching contents: "file://" specifies a local file, "http://"
|
||||
and "https://" specify HTTP and HTTPS, respectively. Note that specific providers may recognize
|
||||
alternative schemes; this is merely the base-most set that all providers support.
|
||||
"""
|
||||
uri: str
|
||||
|
||||
def __init__(self, uri: str) -> None:
|
||||
if not isinstance(uri, str):
|
||||
raise TypeError("RemoteAsset URI must be a string")
|
||||
self.uri = uri
|
||||
|
||||
|
||||
@known_types.archive
|
||||
class Archive:
|
||||
"""
|
||||
Asset represents a collection of named assets.
|
||||
Archive represents a collection of named assets.
|
||||
"""
|
||||
|
||||
|
||||
@known_types.asset_archive
|
||||
class AssetArchive(Archive):
|
||||
assets: Dict[str, Union[Asset, Archive]]
|
||||
|
||||
"""
|
||||
An AssetArchive is an archive created from an in-memory collection of named assets or other archives.
|
||||
"""
|
||||
assets: Dict[str, Union[Asset, Archive]]
|
||||
|
||||
def __init__(self, assets: Dict[str, Union[Asset, Archive]]) -> None:
|
||||
if not isinstance(assets, dict):
|
||||
raise TypeError("AssetArchive assets must be a dictionary")
|
||||
|
@ -98,29 +90,27 @@ class AssetArchive(Archive):
|
|||
self.assets = assets
|
||||
|
||||
|
||||
@known_types.file_archive
|
||||
class FileArchive(Archive):
|
||||
path: str
|
||||
|
||||
"""
|
||||
A FileArchive is a file-based archive, or collection of file-based assets. This can be
|
||||
a raw directory or a single archive file in one of the supported formats (.tar, .tar.gz, or .zip).
|
||||
"""
|
||||
path: str
|
||||
|
||||
def __init__(self, path: str) -> None:
|
||||
if not isinstance(path, str):
|
||||
raise TypeError("FileArchive path must be a string")
|
||||
self.path = path
|
||||
|
||||
|
||||
@known_types.remote_archive
|
||||
class RemoteArchive(Archive):
|
||||
uri: str
|
||||
|
||||
"""
|
||||
A RemoteArchive is a file-based archive fetched from a remote location. The URI's scheme dictates
|
||||
the protocol for fetching contents: "file://" specifies a local file, "http://" and "https://"
|
||||
specify HTTP and HTTPS, respectively, and specific providers may recognize custom schemes.
|
||||
"""
|
||||
uri: str
|
||||
|
||||
def __init__(self, uri: str) -> None:
|
||||
if not isinstance(uri, str):
|
||||
raise TypeError("RemoteArchive URI must be a string")
|
||||
|
|
|
@ -30,7 +30,6 @@ from typing import (
|
|||
)
|
||||
|
||||
from . import runtime
|
||||
from .runtime import known_types
|
||||
from .runtime import rpc
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -43,7 +42,6 @@ Input = Union[T, Awaitable[T], 'Output[T]']
|
|||
Inputs = Mapping[str, Input[Any]]
|
||||
|
||||
|
||||
@known_types.output
|
||||
class Output(Generic[T]):
|
||||
"""
|
||||
Output helps encode the relationship between Resources in a Pulumi application. Specifically an
|
||||
|
@ -223,7 +221,6 @@ class Output(Generic[T]):
|
|||
"""
|
||||
return self.apply(lambda v: UNKNOWN if isinstance(v, Unknown) else getattr(v, item), True)
|
||||
|
||||
|
||||
def __getitem__(self, key: Any) -> 'Output[Any]':
|
||||
"""
|
||||
Syntax sugar for looking up attributes dynamically off of outputs.
|
||||
|
@ -371,7 +368,6 @@ class Output(Generic[T]):
|
|||
return Output.all(*transformed_items).apply("".join) # type: ignore
|
||||
|
||||
|
||||
@known_types.unknown
|
||||
class Unknown:
|
||||
"""
|
||||
Unknown represents a value that is unknown.
|
||||
|
@ -380,10 +376,12 @@ class Unknown:
|
|||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
UNKNOWN = Unknown()
|
||||
"""
|
||||
UNKNOWN is the singleton unknown value.
|
||||
"""
|
||||
|
||||
|
||||
def contains_unknowns(val: Any) -> bool:
|
||||
return rpc.contains_unknowns(val)
|
||||
|
|
|
@ -23,15 +23,11 @@ from .runtime.settings import get_root_resource
|
|||
|
||||
from .metadata import get_project, get_stack
|
||||
|
||||
from .output import Output
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .output import Input, Inputs
|
||||
from .output import Input, Inputs, Output
|
||||
from .runtime.stack import Stack
|
||||
|
||||
|
||||
|
||||
@known_types.custom_timeouts
|
||||
class CustomTimeouts:
|
||||
create: Optional[str]
|
||||
"""
|
||||
|
@ -81,6 +77,7 @@ def inherited_child_alias(
|
|||
# * parentAliasName: "app"
|
||||
# * aliasName: "app-function"
|
||||
# * childAlias: "urn:pulumi:stackname::projectname::aws:s3/bucket:Bucket::app-function"
|
||||
from . import Output # pylint: disable=import-outside-toplevel
|
||||
alias_name = Output.from_input(child_name)
|
||||
if child_name.startswith(parent_name):
|
||||
alias_name = Output.from_input(parent_alias).apply(
|
||||
|
@ -163,14 +160,14 @@ class Alias:
|
|||
"""
|
||||
|
||||
# Ignoring type errors associated with the ellipsis constant being assigned to a string value.
|
||||
# We use it as a internal sentinal value, and don't need to expose this in the user facing type system.
|
||||
# We use it as a internal sentinel value, and don't need to expose this in the user facing type system.
|
||||
# https://docs.python.org/3/library/constants.html#Ellipsis
|
||||
def __init__(self,
|
||||
name: Optional[str] = ..., # type: ignore
|
||||
type_: Optional[str] = ..., # type: ignore
|
||||
parent: Optional[Union['Resource', 'Input[str]']] = ..., # type: ignore
|
||||
stack: Optional['Input[str]'] = ..., # type: ignore
|
||||
project: Optional['Input[str]'] = ...) -> None: # type: ignore
|
||||
name: Optional[str] = ..., # type: ignore
|
||||
type_: Optional[str] = ..., # type: ignore
|
||||
parent: Optional[Union['Resource', 'Input[str]']] = ..., # type: ignore
|
||||
stack: Optional['Input[str]'] = ..., # type: ignore
|
||||
project: Optional['Input[str]'] = ...) -> None: # type: ignore
|
||||
|
||||
self.name = name
|
||||
self.type_ = type_
|
||||
|
@ -187,8 +184,9 @@ def collapse_alias_to_urn(
|
|||
"""
|
||||
collapse_alias_to_urn turns an Alias into a URN given a set of default data
|
||||
"""
|
||||
from . import Output # pylint: disable=import-outside-toplevel
|
||||
|
||||
def collapse_alias_to_urn_worker(inner: Union[Alias, str]) -> 'Output[str]':
|
||||
def collapse_alias_to_urn_worker(inner: Union[Alias, str]) -> Output[str]:
|
||||
if isinstance(inner, str):
|
||||
return Output.from_input(inner)
|
||||
|
||||
|
@ -209,6 +207,7 @@ def collapse_alias_to_urn(
|
|||
inputAlias: Output[Union[Alias, str]] = Output.from_input(alias)
|
||||
return inputAlias.apply(collapse_alias_to_urn_worker)
|
||||
|
||||
|
||||
class ResourceTransformationArgs:
|
||||
"""
|
||||
ResourceTransformationArgs is the argument bag passed to a resource transformation.
|
||||
|
@ -251,6 +250,7 @@ class ResourceTransformationArgs:
|
|||
self.props = props
|
||||
self.opts = opts
|
||||
|
||||
|
||||
class ResourceTransformationResult:
|
||||
"""
|
||||
ResourceTransformationResult is the result that must be returned by a resource transformation
|
||||
|
@ -274,6 +274,7 @@ class ResourceTransformationResult:
|
|||
self.props = props
|
||||
self.opts = opts
|
||||
|
||||
|
||||
ResourceTransformation = Callable[[ResourceTransformationArgs], Optional[ResourceTransformationResult]]
|
||||
"""
|
||||
ResourceTransformation is the callback signature for the `transformations` resource option. A
|
||||
|
@ -284,6 +285,7 @@ of the original call to the `Resource` constructor. If the transformation retur
|
|||
this indicates that the resource will not be transformed.
|
||||
"""
|
||||
|
||||
|
||||
class ResourceOptions:
|
||||
"""
|
||||
ResourceOptions is a bag of optional settings that control a resource's behavior.
|
||||
|
@ -458,7 +460,7 @@ class ResourceOptions:
|
|||
values from each options object. Both original collections in each options object will
|
||||
be unchanged.
|
||||
|
||||
2. Simple scaler values from `opts2` (i.e. strings, numbers, bools) will replace the values
|
||||
2. Simple scalar values from `opts2` (i.e. strings, numbers, bools) will replace the values
|
||||
from `opts1`.
|
||||
|
||||
3. For the purposes of merging `depends_on`, `provider` and `providers` are always treated
|
||||
|
@ -551,6 +553,7 @@ def _merge_lists(dest, source):
|
|||
|
||||
return dest + source
|
||||
|
||||
|
||||
# !!! IMPORTANT !!! If you add a new attribute to this type, make sure to verify that merge_options
|
||||
# works properly for it.
|
||||
class Resource:
|
||||
|
@ -621,8 +624,8 @@ class Resource:
|
|||
elif not isinstance(opts, ResourceOptions):
|
||||
raise TypeError('Expected resource options to be a ResourceOptions instance')
|
||||
|
||||
# Before anything else - if there are transformations registered, give them a chance to run to modify the user provided
|
||||
# properties and options assigned to this resource.
|
||||
# Before anything else - if there are transformations registered, give them a chance to run to modify the user
|
||||
# provided properties and options assigned to this resource.
|
||||
parent = opts.parent
|
||||
if parent is None:
|
||||
parent = get_root_resource()
|
||||
|
@ -667,7 +670,7 @@ class Resource:
|
|||
for parent_alias in opts.parent._aliases:
|
||||
child_alias = inherited_child_alias(
|
||||
name, opts.parent._name, parent_alias, t)
|
||||
opts.aliases.append(cast(Output[Union[str, Alias]], child_alias))
|
||||
opts.aliases.append(cast('Output[Union[str, Alias]]', child_alias))
|
||||
|
||||
# Infer providers and provider maps from parent, if one was provided.
|
||||
self._providers = opts.parent._providers
|
||||
|
@ -776,7 +779,6 @@ class Resource:
|
|||
return self._providers.get(pkg)
|
||||
|
||||
|
||||
@known_types.custom_resource
|
||||
class CustomResource(Resource):
|
||||
"""
|
||||
CustomResource is a resource whose create, read, update, and delete (CRUD) operations are
|
||||
|
@ -903,7 +905,7 @@ def create_urn(
|
|||
create_urn computes a URN from the combination of a resource name, resource type, optional
|
||||
parent, optional project and optional stack.
|
||||
"""
|
||||
|
||||
from . import Output # pylint: disable=import-outside-toplevel
|
||||
parent_prefix: Optional[Output[str]] = None
|
||||
if parent is not None:
|
||||
parent_urn = None
|
||||
|
|
|
@ -13,11 +13,10 @@
|
|||
# limitations under the License.
|
||||
import asyncio
|
||||
import sys
|
||||
from typing import Any, Awaitable
|
||||
from typing import Any, Awaitable, TYPE_CHECKING
|
||||
import grpc
|
||||
|
||||
from .. import log
|
||||
from ..output import Inputs
|
||||
from ..invoke import InvokeOptions
|
||||
from ..runtime.proto import provider_pb2
|
||||
from . import rpc
|
||||
|
@ -25,6 +24,9 @@ from .rpc_manager import RPC_MANAGER
|
|||
from .settings import get_monitor
|
||||
from .sync_await import _sync_await
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Inputs
|
||||
|
||||
# This setting overrides a hardcoded maximum protobuf size in the python protobuf bindings. This avoids deserialization
|
||||
# exceptions on large gRPC payloads, but makes it possible to use enough memory to cause an OOM error instead [1].
|
||||
# Note: We hit the default maximum protobuf size in practice when processing Kubernetes CRDs. If this setting ends up
|
||||
|
@ -57,7 +59,7 @@ class InvokeResult:
|
|||
|
||||
__iter__ = __await__
|
||||
|
||||
def invoke(tok: str, props: Inputs, opts: InvokeOptions = None) -> InvokeResult:
|
||||
def invoke(tok: str, props: 'Inputs', opts: InvokeOptions = None) -> InvokeResult:
|
||||
"""
|
||||
invoke dynamically invokes the function, tok, which is offered by a provider plugin. The inputs
|
||||
can be a bag of computed values (Ts or Awaitable[T]s), and the result is a Awaitable[Any] that
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
The known_types module contains state for keeping track of types that
|
||||
are known to be special in the Pulumi type system.
|
||||
The known_types module lazily loads classes defined in the parent module to
|
||||
allow for type checking.
|
||||
|
||||
Python strictly disallows circular references between imported packages.
|
||||
Because the Pulumi top-level module depends on the `pulumi.runtime` submodule,
|
||||
|
@ -21,285 +21,69 @@ it is not allowed for `pulumi.runtime` to reach back to the `pulumi` top-level
|
|||
to reference types that are defined there.
|
||||
|
||||
In order to break this circular reference, and to be clear about what types
|
||||
the runtime knows about and treats specially, this module exports a number of
|
||||
"known type" decorators that can be applied to types in `pulumi` to indicate
|
||||
that they are specially treated.
|
||||
|
||||
The implementation of this mechanism is that, for every known type, that type
|
||||
is stashed away in a global variable. Whenever the runtime wants to do a type
|
||||
test using that type (or instantiate an instance of this type), it uses the
|
||||
functions defined in this module to do so.
|
||||
the runtime knows about and treats specially, we defer loading of the types from
|
||||
within the functions themselves.
|
||||
"""
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
# We override this global in test/test_next_serialize.py to stub the CustomResource type.
|
||||
# TODO: Rework the test to remove the need for this global. https://github.com/pulumi/pulumi/issues/5000
|
||||
_custom_resource_type: Optional[type] = None
|
||||
"""The type of CustomResource. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_custom_timeouts_type: Optional[type] = None
|
||||
"""The type of CustomTimeouts. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_asset_resource_type: Optional[type] = None
|
||||
"""The type of Asset. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_file_asset_resource_type: Optional[type] = None
|
||||
"""The type of FileAsset. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_string_asset_resource_type: Optional[type] = None
|
||||
"""The type of StringAsset. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_remote_asset_resource_type: Optional[type] = None
|
||||
"""The type of RemoteAsset. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_archive_resource_type: Optional[type] = None
|
||||
"""The type of Archive. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_asset_archive_resource_type: Optional[type] = None
|
||||
"""The type of AssetArchive. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_file_archive_resource_type: Optional[type] = None
|
||||
"""The type of FileArchive. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_remote_archive_resource_type: Optional[type] = None
|
||||
"""The type of RemoteArchive. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_stack_resource_type: Optional[type] = None
|
||||
"""The type of Stack. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_output_type: Optional[type] = None
|
||||
"""The type of Output. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
_unknown_type: Optional[type] = None
|
||||
"""The type of unknown. Filled-in as the Pulumi package is initializing."""
|
||||
|
||||
def asset(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the Asset class. Registers the decorated class
|
||||
as the Asset known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _asset_resource_type
|
||||
_asset_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def file_asset(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the FileAsset class. Registers the decorated class
|
||||
as the FileAsset known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _file_asset_resource_type
|
||||
_file_asset_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def string_asset(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the StringAsset class. Registers the decorated class
|
||||
as the StringAsset known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _string_asset_resource_type
|
||||
_string_asset_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def remote_asset(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the RemoteAsset class. Registers the decorated class
|
||||
as the RemoteAsset known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _remote_asset_resource_type
|
||||
_remote_asset_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def archive(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the Archive class. Registers the decorated class
|
||||
as the Archive known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _archive_resource_type
|
||||
_archive_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def asset_archive(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the AssetArchive class. Registers the decorated class
|
||||
as the AssetArchive known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _asset_archive_resource_type
|
||||
_asset_archive_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def file_archive(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the FileArchive class. Registers the decorated class
|
||||
as the FileArchive known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _file_archive_resource_type
|
||||
_file_archive_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def remote_archive(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the RemoteArchive class. Registers the decorated class
|
||||
as the RemoteArchive known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _remote_archive_resource_type
|
||||
_remote_archive_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def custom_resource(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the CustomResource class. Registers the decorated class
|
||||
as the CustomResource known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _custom_resource_type
|
||||
_custom_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
def custom_timeouts(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the CustomTimeouts class. Registers the decorated class
|
||||
as the CustomTimeouts known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _custom_timeouts_type
|
||||
_custom_timeouts_type = class_obj
|
||||
return class_obj
|
||||
|
||||
def stack(class_obj: type) -> type:
|
||||
"""
|
||||
Decorator to annotate the Stack class. Registers the decorated class
|
||||
as the Stack known type.
|
||||
"""
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _stack_resource_type
|
||||
_stack_resource_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def output(class_obj: type) -> type:
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _output_type
|
||||
_output_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def unknown(class_obj: type) -> type:
|
||||
assert isinstance(class_obj, type), "class_obj is not a Class"
|
||||
global _unknown_type
|
||||
_unknown_type = class_obj
|
||||
return class_obj
|
||||
|
||||
|
||||
def new_file_asset(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new FileAsset, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _file_asset_resource_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_string_asset(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new StringAsset, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _string_asset_resource_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_remote_asset(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new StringAsset, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _remote_asset_resource_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_asset_archive(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new AssetArchive, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _asset_archive_resource_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_file_archive(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new FileArchive, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _file_archive_resource_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_remote_archive(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new StringArchive, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _remote_archive_resource_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_output(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new Output, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _output_type(*args) # type: ignore
|
||||
|
||||
|
||||
def new_unknown(*args: Any) -> Any:
|
||||
"""
|
||||
Instantiates a new Unknown, passing the given arguments to the constructor.
|
||||
"""
|
||||
return _unknown_type(*args) # type: ignore
|
||||
"""The type of CustomResource."""
|
||||
|
||||
|
||||
def is_asset(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given type is an Asset, false otherwise.
|
||||
"""
|
||||
return _asset_resource_type is not None and isinstance(obj, _asset_resource_type)
|
||||
from .. import Asset # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, Asset)
|
||||
|
||||
|
||||
def is_archive(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given type is an Archive, false otherwise.
|
||||
"""
|
||||
return _archive_resource_type is not None and isinstance(obj, _archive_resource_type)
|
||||
from .. import Archive # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, Archive)
|
||||
|
||||
|
||||
def is_custom_resource(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given type is a CustomResource, false otherwise.
|
||||
"""
|
||||
return _custom_resource_type is not None and isinstance(obj, _custom_resource_type)
|
||||
from .. import CustomResource # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, _custom_resource_type or CustomResource)
|
||||
|
||||
|
||||
def is_custom_timeouts(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given type is a CustomTimeouts, false otherwise.
|
||||
"""
|
||||
return _custom_timeouts_type is not None and isinstance(obj, _custom_timeouts_type)
|
||||
from .. import CustomTimeouts # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, CustomTimeouts)
|
||||
|
||||
|
||||
def is_stack(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given type is an Output, false otherwise.
|
||||
"""
|
||||
return _stack_resource_type is not None and isinstance(obj, _stack_resource_type)
|
||||
from .stack import Stack # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, Stack)
|
||||
|
||||
|
||||
def is_output(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given type is an Output, false otherwise.
|
||||
"""
|
||||
return _output_type is not None and isinstance(obj, _output_type)
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, Output)
|
||||
|
||||
|
||||
def is_unknown(obj: Any) -> bool:
|
||||
"""
|
||||
Returns true if the given object is an Unknown, false otherwise.
|
||||
"""
|
||||
return _unknown_type is not None and isinstance(obj, _unknown_type)
|
||||
from ..output import Unknown # pylint: disable=import-outside-toplevel
|
||||
return isinstance(obj, Unknown)
|
||||
|
|
|
@ -27,7 +27,6 @@ from .settings import Settings, configure, get_stack, get_project, get_root_reso
|
|||
from .sync_await import _sync_await
|
||||
from ..runtime.proto import engine_pb2, engine_pb2_grpc, provider_pb2, resource_pb2, resource_pb2_grpc
|
||||
from ..runtime.stack import Stack, run_pulumi_func
|
||||
from ..output import Output
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..resource import Resource
|
||||
|
@ -35,6 +34,7 @@ if TYPE_CHECKING:
|
|||
|
||||
def test(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
_sync_await(run_pulumi_func(lambda: _sync_await(Output.from_input(fn(*args, **kwargs)).future())))
|
||||
return wrapper
|
||||
|
||||
|
@ -119,11 +119,11 @@ class MockMonitor:
|
|||
return resource_pb2.RegisterResourceResponse(urn=urn, id=id_, object=obj_proto)
|
||||
|
||||
def RegisterResourceOutputs(self, request):
|
||||
#pylint: disable=unused-argument
|
||||
# pylint: disable=unused-argument
|
||||
return empty_pb2.Empty()
|
||||
|
||||
def SupportsFeature(self, request):
|
||||
#pylint: disable=unused-argument
|
||||
# pylint: disable=unused-argument
|
||||
return type('SupportsFeatureResponse', (object,), {'hasSupport' : True})
|
||||
|
||||
|
||||
|
|
|
@ -25,11 +25,8 @@ from ..runtime.proto import resource_pb2
|
|||
from .rpc_manager import RPC_MANAGER
|
||||
from ..metadata import get_project, get_stack
|
||||
|
||||
from ..output import Output
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Resource, ResourceOptions, CustomResource
|
||||
from ..output import Inputs
|
||||
from .. import Resource, ResourceOptions, CustomResource, Inputs, Output
|
||||
|
||||
|
||||
class ResourceResolverOperations(NamedTuple):
|
||||
|
@ -75,6 +72,7 @@ async def prepare_resource(res: 'Resource',
|
|||
custom: bool,
|
||||
props: 'Inputs',
|
||||
opts: Optional['ResourceOptions']) -> ResourceResolverOperations:
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
log.debug(f"resource {props} preparing to wait for dependencies")
|
||||
# Before we can proceed, all our dependencies must be finished.
|
||||
explicit_urn_dependencies = []
|
||||
|
@ -141,11 +139,11 @@ async def prepare_resource(res: 'Resource',
|
|||
|
||||
|
||||
class _ResourceResult(NamedTuple):
|
||||
urn: Output[str]
|
||||
urn: 'Output[str]'
|
||||
"""
|
||||
The URN of the resource.
|
||||
"""
|
||||
id: Optional[Output[str]] = None
|
||||
id: Optional['Output[str]'] = None
|
||||
"""
|
||||
The id of the resource, if it's a CustomResource, otherwise None.
|
||||
"""
|
||||
|
@ -154,6 +152,7 @@ class _ResourceResult(NamedTuple):
|
|||
# pylint: disable=too-many-locals,too-many-statements
|
||||
|
||||
def _read_resource(res: 'CustomResource', ty: str, name: str, props: 'Inputs', opts: 'ResourceOptions') -> _ResourceResult:
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
if opts.id is None:
|
||||
raise Exception(
|
||||
"Cannot read resource whose options are lacking an ID value")
|
||||
|
@ -173,7 +172,7 @@ def _read_resource(res: 'CustomResource', ty: str, name: str, props: 'Inputs', o
|
|||
urn_secret.set_result(False)
|
||||
resolve_urn = urn_future.set_result
|
||||
resolve_urn_exn = urn_future.set_exception
|
||||
result_urn = known_types.new_output({res}, urn_future, urn_known, urn_secret)
|
||||
result_urn = Output({res}, urn_future, urn_known, urn_secret)
|
||||
|
||||
# Furthermore, since resources being Read must always be custom resources (enforced in the
|
||||
# Resource constructor), we'll need to set up the ID field which will be populated at the end of
|
||||
|
@ -185,7 +184,7 @@ def _read_resource(res: 'CustomResource', ty: str, name: str, props: 'Inputs', o
|
|||
resolve_value: asyncio.Future[Any] = asyncio.Future()
|
||||
resolve_perform_apply: asyncio.Future[bool] = asyncio.Future()
|
||||
resolve_secret: asyncio.Future[bool] = asyncio.Future()
|
||||
result_id = known_types.new_output(
|
||||
result_id = Output(
|
||||
{res}, resolve_value, resolve_perform_apply, resolve_secret)
|
||||
|
||||
def do_resolve(value: Any, perform_apply: bool, exn: Optional[Exception]):
|
||||
|
@ -236,7 +235,7 @@ def _read_resource(res: 'CustomResource', ty: str, name: str, props: 'Inputs', o
|
|||
additionalSecretOutputs=additional_secret_outputs,
|
||||
)
|
||||
|
||||
from ..resource import create_urn # pylint: disable=import-outside-toplevel
|
||||
from ..resource import create_urn # pylint: disable=import-outside-toplevel
|
||||
mock_urn = await create_urn(name, ty, resolver.parent_urn).future()
|
||||
|
||||
def do_rpc_call():
|
||||
|
@ -293,6 +292,7 @@ def _register_resource(res: 'Resource',
|
|||
opts: Optional['ResourceOptions']) -> _ResourceResult:
|
||||
log.debug(f"registering resource: ty={ty}, name={name}, custom={custom}")
|
||||
monitor = settings.get_monitor()
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
|
||||
# Prepare the resource.
|
||||
|
||||
|
@ -307,7 +307,7 @@ def _register_resource(res: 'Resource',
|
|||
urn_secret.set_result(False)
|
||||
resolve_urn = urn_future.set_result
|
||||
resolve_urn_exn = urn_future.set_exception
|
||||
result_urn = known_types.new_output({res}, urn_future, urn_known, urn_secret)
|
||||
result_urn = Output({res}, urn_future, urn_known, urn_secret)
|
||||
|
||||
# If a custom resource, make room for the ID property.
|
||||
result_id = None
|
||||
|
@ -317,7 +317,7 @@ def _register_resource(res: 'Resource',
|
|||
resolve_value: asyncio.Future[Any] = asyncio.Future()
|
||||
resolve_perform_apply: asyncio.Future[bool] = asyncio.Future()
|
||||
resolve_secret: asyncio.Future[bool] = asyncio.Future()
|
||||
result_id = known_types.new_output(
|
||||
result_id = Output(
|
||||
{res}, resolve_value, resolve_perform_apply, resolve_secret)
|
||||
|
||||
def do_resolve(value: Any, perform_apply: bool, exn: Optional[Exception]):
|
||||
|
|
|
@ -211,31 +211,32 @@ def deserialize_properties(props_struct: struct_pb2.Struct, keep_unknowns: Optio
|
|||
# We assume that we are deserializing properties that we got from a Resource RPC endpoint,
|
||||
# which has type `Struct` in our gRPC proto definition.
|
||||
if _special_sig_key in props_struct:
|
||||
from .. import FileAsset, StringAsset, RemoteAsset, AssetArchive, FileArchive, RemoteArchive # pylint: disable=import-outside-toplevel
|
||||
if props_struct[_special_sig_key] == _special_asset_sig:
|
||||
# This is an asset. Re-hydrate this object into an Asset.
|
||||
if "path" in props_struct:
|
||||
return known_types.new_file_asset(props_struct["path"])
|
||||
return FileAsset(props_struct["path"])
|
||||
if "text" in props_struct:
|
||||
return known_types.new_string_asset(props_struct["text"])
|
||||
return StringAsset(props_struct["text"])
|
||||
if "uri" in props_struct:
|
||||
return known_types.new_remote_asset(props_struct["uri"])
|
||||
raise AssertionError("Invalid asset encountered when unmarshaling resource property")
|
||||
return RemoteAsset(props_struct["uri"])
|
||||
raise AssertionError("Invalid asset encountered when unmarshalling resource property")
|
||||
if props_struct[_special_sig_key] == _special_archive_sig:
|
||||
# This is an archive. Re-hydrate this object into an Archive.
|
||||
if "assets" in props_struct:
|
||||
return known_types.new_asset_archive(deserialize_property(props_struct["assets"]))
|
||||
return AssetArchive(deserialize_property(props_struct["assets"]))
|
||||
if "path" in props_struct:
|
||||
return known_types.new_file_archive(props_struct["path"])
|
||||
return FileArchive(props_struct["path"])
|
||||
if "uri" in props_struct:
|
||||
return known_types.new_remote_archive(props_struct["uri"])
|
||||
raise AssertionError("Invalid archive encountered when unmarshaling resource property")
|
||||
return RemoteArchive(props_struct["uri"])
|
||||
raise AssertionError("Invalid archive encountered when unmarshalling resource property")
|
||||
if props_struct[_special_sig_key] == _special_secret_sig:
|
||||
return {
|
||||
_special_sig_key: _special_secret_sig,
|
||||
"value": deserialize_property(props_struct["value"])
|
||||
}
|
||||
|
||||
raise AssertionError("Unrecognized signature when unmarshaling resource property")
|
||||
raise AssertionError("Unrecognized signature when unmarshalling resource property")
|
||||
|
||||
# Struct is duck-typed like a dictionary, so we can iterate over it in the normal ways. Note
|
||||
# that if the struct had any secret properties, we push the secretness of the object up to us
|
||||
|
@ -269,8 +270,9 @@ def deserialize_property(value: Any, keep_unknowns: Optional[bool] = None) -> An
|
|||
Deserializes a single protobuf value (either `Struct` or `ListValue`) into idiomatic
|
||||
Python values.
|
||||
"""
|
||||
from ..output import Unknown # pylint: disable=import-outside-toplevel
|
||||
if value == UNKNOWN:
|
||||
return known_types.new_unknown() if settings.is_dry_run() or keep_unknowns else None
|
||||
return Unknown() if settings.is_dry_run() or keep_unknowns else None
|
||||
|
||||
# ListValues are projected to lists
|
||||
if isinstance(value, struct_pb2.ListValue):
|
||||
|
@ -321,6 +323,7 @@ result in the exception being re-thrown.
|
|||
|
||||
|
||||
def transfer_properties(res: 'Resource', props: 'Inputs') -> Dict[str, Resolver]:
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
resolvers: Dict[str, Resolver] = {}
|
||||
for name in props.keys():
|
||||
if name in ["id", "urn"]:
|
||||
|
@ -354,7 +357,7 @@ def transfer_properties(res: 'Resource', props: 'Inputs') -> Dict[str, Resolver]
|
|||
# using res.translate_output_property and then use *that* name to index into the resolvers table.
|
||||
log.debug(f"adding resolver {name}")
|
||||
resolvers[name] = functools.partial(do_resolve, resolve_value, resolve_is_known, resolve_is_secret)
|
||||
res.__setattr__(name, known_types.new_output({res}, resolve_value, resolve_is_known, resolve_is_secret))
|
||||
res.__setattr__(name, Output({res}, resolve_value, resolve_is_known, resolve_is_secret))
|
||||
|
||||
return resolvers
|
||||
|
||||
|
|
|
@ -18,16 +18,17 @@ Support for automatic stack components.
|
|||
import asyncio
|
||||
import collections
|
||||
from inspect import isawaitable
|
||||
from typing import Callable, Any, Dict, List
|
||||
from typing import Callable, Any, Dict, List, TYPE_CHECKING
|
||||
|
||||
from ..resource import ComponentResource, Resource, ResourceTransformation
|
||||
from .settings import get_project, get_stack, get_root_resource, is_dry_run, set_root_resource
|
||||
from .rpc_manager import RPC_MANAGER
|
||||
from .sync_await import _all_tasks, _get_current_task
|
||||
from .. import log
|
||||
from . import known_types
|
||||
|
||||
from ..output import Output
|
||||
if TYPE_CHECKING:
|
||||
from .. import Output
|
||||
|
||||
|
||||
async def run_pulumi_func(func: Callable):
|
||||
try:
|
||||
|
@ -72,6 +73,7 @@ async def run_pulumi_func(func: Callable):
|
|||
if RPC_MANAGER.unhandled_exception is not None:
|
||||
raise RPC_MANAGER.unhandled_exception.with_traceback(RPC_MANAGER.exception_traceback)
|
||||
|
||||
|
||||
async def run_in_stack(func: Callable):
|
||||
"""
|
||||
Run the given function inside of a new stack resource. This ensures that any stack export calls
|
||||
|
@ -81,7 +83,6 @@ async def run_in_stack(func: Callable):
|
|||
await run_pulumi_func(lambda: Stack(func))
|
||||
|
||||
|
||||
@known_types.stack
|
||||
class Stack(ComponentResource):
|
||||
"""
|
||||
A synthetic stack component that automatically parents resources as the program runs.
|
||||
|
@ -113,6 +114,7 @@ class Stack(ComponentResource):
|
|||
"""
|
||||
self.outputs[name] = value
|
||||
|
||||
|
||||
# Note: we use a List here instead of a set as many objects are unhashable. This is inefficient,
|
||||
# but python seems to offer no alternative.
|
||||
def massage(attr: Any, seen: List[Any]):
|
||||
|
@ -123,9 +125,9 @@ def massage(attr: Any, seen: List[Any]):
|
|||
lists or dictionaries as appropriate. In general, iterable things are turned into lists, and
|
||||
dictionary-like things are turned into dictionaries.
|
||||
"""
|
||||
from .. import Output # pylint: disable=import-outside-toplevel
|
||||
|
||||
# Basic primitive types (numbers, booleans, strings, etc.) don't need any special handling.
|
||||
|
||||
if is_primitive(attr):
|
||||
return attr
|
||||
|
||||
|
@ -208,6 +210,7 @@ def is_primitive(attr: Any) -> bool:
|
|||
|
||||
return True
|
||||
|
||||
|
||||
def register_stack_transformation(t: ResourceTransformation):
|
||||
"""
|
||||
Add a transformation to all future resources constructed in this Pulumi stack.
|
||||
|
|
|
@ -130,6 +130,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -314,12 +316,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
|
|
Loading…
Reference in a new issue