Add support for importing existing resources. (#2893)
A resource can be imported by setting the `import` property in the resource options bag when instantiating a resource. In order to successfully import a resource, its desired configuration (i.e. its inputs) must not differ from its actual configuration (i.e. its state) as calculated by the resource's provider. There are a few interesting state transitions hiding here when importing a resource: 1. No prior resource exists in the checkpoint file. In this case, the resource is simply imported. 2. An external resource exists in the checkpoint file. In this case, the resource is imported and the old external state is discarded. 3. A non-external resource exists in the checkpoint file and its ID is different from the ID to import. In this case, the new resource is imported and the old resource is deleted. 4. A non-external resource exists in the checkpoint file, but the ID is the same as the ID to import. In this case, the import ID is ignored and the resource is treated as it would be in all cases except for changes that would replace the resource. In that case, the step generator issues an error that indicates that the import ID should be removed: were we to move forward with the replace, the new state of the stack would fall under case (3), which is almost certainly not what the user intends. Fixes #1662.
This commit is contained in:
parent
611c616468
commit
e1a52693dc
|
@ -12,6 +12,10 @@ CHANGELOG
|
|||
[#2871](https://github.com/pulumi/pulumi/issues/2871))
|
||||
- Promises/Outputs can now be directly exported as the top-level (i.e. not-named) output of a Stack.
|
||||
(fixes [#2910](https://github.com/pulumi/pulumi/issues/2910))
|
||||
- Add support for importing existing resources to be managed using Pulumi. A resource can be imported
|
||||
by setting the `import` property in the resource options bag when instantiating a resource. In order to
|
||||
successfully import a resource, its desired configuration (i.e. its inputs) must not differ from its
|
||||
actual configuration (i.e. its state) as calculated by the resource's provider.
|
||||
|
||||
## 0.17.22 (2019-07-11)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ package display
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
|
@ -249,39 +250,49 @@ func renderDiffResourceOperationFailedEvent(
|
|||
return ""
|
||||
}
|
||||
|
||||
func renderDiff(
|
||||
out io.Writer,
|
||||
metadata engine.StepEventMetadata,
|
||||
planning, debug bool,
|
||||
seen map[resource.URN]engine.StepEventMetadata,
|
||||
opts Options) {
|
||||
|
||||
indent := engine.GetIndent(metadata, seen)
|
||||
summary := engine.GetResourcePropertiesSummary(metadata, indent)
|
||||
|
||||
var details string
|
||||
if metadata.DetailedDiff != nil {
|
||||
var buf bytes.Buffer
|
||||
if diff := translateDetailedDiff(metadata); diff != nil {
|
||||
engine.PrintObjectDiff(&buf, *diff, nil /*include*/, planning, indent+1, opts.SummaryDiff, debug)
|
||||
} else {
|
||||
engine.PrintObject(
|
||||
&buf, metadata.Old.Inputs, planning, indent+1, deploy.OpSame, true /*prefix*/, debug)
|
||||
}
|
||||
details = buf.String()
|
||||
} else {
|
||||
details = engine.GetResourcePropertiesDetails(
|
||||
metadata, indent, planning, opts.SummaryDiff, debug)
|
||||
}
|
||||
|
||||
fprintIgnoreError(out, opts.Color.Colorize(summary))
|
||||
fprintIgnoreError(out, opts.Color.Colorize(details))
|
||||
fprintIgnoreError(out, opts.Color.Colorize(colors.Reset))
|
||||
}
|
||||
|
||||
func renderDiffResourcePreEvent(
|
||||
payload engine.ResourcePreEventPayload,
|
||||
seen map[resource.URN]engine.StepEventMetadata,
|
||||
opts Options) string {
|
||||
|
||||
seen[payload.Metadata.URN] = payload.Metadata
|
||||
if payload.Metadata.Op == deploy.OpRefresh {
|
||||
if payload.Metadata.Op == deploy.OpRefresh || payload.Metadata.Op == deploy.OpImport {
|
||||
return ""
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
if shouldShow(payload.Metadata, opts) || isRootStack(payload.Metadata) {
|
||||
indent := engine.GetIndent(payload.Metadata, seen)
|
||||
summary := engine.GetResourcePropertiesSummary(payload.Metadata, indent)
|
||||
|
||||
var details string
|
||||
if payload.Metadata.DetailedDiff != nil {
|
||||
var buf bytes.Buffer
|
||||
if diff := translateDetailedDiff(payload.Metadata); diff != nil {
|
||||
engine.PrintObjectDiff(&buf, *diff, nil /*include*/, payload.Planning, indent+1, opts.SummaryDiff, payload.Debug)
|
||||
} else {
|
||||
engine.PrintObject(
|
||||
&buf, payload.Metadata.Old.Inputs, payload.Planning, indent+1, deploy.OpSame, true /*prefix*/, payload.Debug)
|
||||
}
|
||||
details = buf.String()
|
||||
} else {
|
||||
details = engine.GetResourcePropertiesDetails(
|
||||
payload.Metadata, indent, payload.Planning, opts.SummaryDiff, payload.Debug)
|
||||
}
|
||||
|
||||
fprintIgnoreError(out, opts.Color.Colorize(summary))
|
||||
fprintIgnoreError(out, opts.Color.Colorize(details))
|
||||
fprintIgnoreError(out, opts.Color.Colorize(colors.Reset))
|
||||
renderDiff(out, payload.Metadata, payload.Planning, payload.Debug, seen, opts)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
@ -293,6 +304,12 @@ func renderDiffResourceOutputsEvent(
|
|||
|
||||
out := &bytes.Buffer{}
|
||||
if shouldShow(payload.Metadata, opts) || isRootStack(payload.Metadata) {
|
||||
// If this is the output step for an import, we actually want to display the diff at this point.
|
||||
if payload.Metadata.Op == deploy.OpImport {
|
||||
renderDiff(out, payload.Metadata, payload.Planning, payload.Debug, seen, opts)
|
||||
return out.String()
|
||||
}
|
||||
|
||||
indent := engine.GetIndent(payload.Metadata, seen)
|
||||
|
||||
refresh := false // are these outputs from a refresh?
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// nolint: goconst
|
||||
package display
|
||||
|
||||
import (
|
||||
|
@ -1075,6 +1076,8 @@ func (display *ProgressDisplay) getStepDoneDescription(step engine.StepEventMeta
|
|||
return "refreshing failed"
|
||||
case deploy.OpReadDiscard, deploy.OpDiscardReplaced:
|
||||
return "discarding failed"
|
||||
case deploy.OpImport, deploy.OpImportReplacement:
|
||||
return "importing failed"
|
||||
}
|
||||
} else {
|
||||
switch op {
|
||||
|
@ -1103,6 +1106,10 @@ func (display *ProgressDisplay) getStepDoneDescription(step engine.StepEventMeta
|
|||
return "discarded"
|
||||
case deploy.OpDiscardReplaced:
|
||||
return "discarded original"
|
||||
case deploy.OpImport:
|
||||
return "imported"
|
||||
case deploy.OpImportReplacement:
|
||||
return "imported replacement"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1143,7 +1150,11 @@ func (display *ProgressDisplay) getPreviewText(step engine.StepEventMetadata) st
|
|||
case deploy.OpReadDiscard:
|
||||
return "discard"
|
||||
case deploy.OpDiscardReplaced:
|
||||
return "discard origina;"
|
||||
return "discard original"
|
||||
case deploy.OpImport:
|
||||
return "import"
|
||||
case deploy.OpImportReplacement:
|
||||
return "import replacement"
|
||||
}
|
||||
|
||||
contract.Failf("Unrecognized resource step op: %v", step.Op)
|
||||
|
@ -1172,6 +1183,8 @@ func (display *ProgressDisplay) getPreviewDoneText(step engine.StepEventMetadata
|
|||
return "refresh"
|
||||
case deploy.OpReadDiscard:
|
||||
return "discard"
|
||||
case deploy.OpImport, deploy.OpImportReplacement:
|
||||
return "import"
|
||||
}
|
||||
|
||||
contract.Failf("Unrecognized resource step op: %v", step.Op)
|
||||
|
@ -1246,6 +1259,10 @@ func (display *ProgressDisplay) getStepInProgressDescription(step engine.StepEve
|
|||
return "discarding"
|
||||
case deploy.OpDiscardReplaced:
|
||||
return "discarding original"
|
||||
case deploy.OpImport:
|
||||
return "importing"
|
||||
case deploy.OpImportReplacement:
|
||||
return "importing replacement"
|
||||
}
|
||||
|
||||
contract.Failf("Unrecognized resource step op: %v", op)
|
||||
|
|
|
@ -348,7 +348,8 @@ func (data *resourceRowData) ColorizedColumns() []string {
|
|||
|
||||
func (data *resourceRowData) getInfoColumn() string {
|
||||
step := data.step
|
||||
if step.Op == deploy.OpCreateReplacement || step.Op == deploy.OpDeleteReplaced {
|
||||
switch step.Op {
|
||||
case deploy.OpCreateReplacement, deploy.OpDeleteReplaced:
|
||||
// if we're doing a replacement, see if we can find a replace step that contains useful
|
||||
// information to display.
|
||||
for _, outputStep := range data.outputSteps {
|
||||
|
@ -356,6 +357,14 @@ func (data *resourceRowData) getInfoColumn() string {
|
|||
step = outputStep
|
||||
}
|
||||
}
|
||||
|
||||
case deploy.OpImport, deploy.OpImportReplacement:
|
||||
// If we're doing an import, see if we have the imported state to diff.
|
||||
for _, outputStep := range data.outputSteps {
|
||||
if outputStep.Op == step.Op {
|
||||
step = outputStep
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var diagMsg string
|
||||
|
|
|
@ -145,6 +145,8 @@ func (sm *SnapshotManager) BeginMutation(step deploy.Step) (engine.SnapshotMutat
|
|||
return &refreshSnapshotMutation{sm}, nil
|
||||
case deploy.OpRemovePendingReplace:
|
||||
return &removePendingReplaceSnapshotMutation{sm}, nil
|
||||
case deploy.OpImport, deploy.OpImportReplacement:
|
||||
return sm.doImport(step)
|
||||
}
|
||||
|
||||
contract.Failf("unknown StepOp: %s", step.Op())
|
||||
|
@ -427,6 +429,37 @@ 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.URN())
|
||||
err := sm.mutate(func() bool {
|
||||
sm.markOperationPending(step.New(), resource.OperationTypeImporting)
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &importSnapshotMutation{sm}, nil
|
||||
}
|
||||
|
||||
type importSnapshotMutation struct {
|
||||
manager *SnapshotManager
|
||||
}
|
||||
|
||||
func (ism *importSnapshotMutation) End(step deploy.Step, successful bool) error {
|
||||
contract.Require(step != nil, "step != nil")
|
||||
contract.Require(step.Op() == deploy.OpImport || step.Op() == deploy.OpImportReplacement,
|
||||
"step.Op() == deploy.OpImport || step.Op() == deploy.OpImportReplacement")
|
||||
|
||||
return ism.manager.mutate(func() bool {
|
||||
ism.manager.markOperationComplete(step.New())
|
||||
if successful {
|
||||
ism.manager.markNew(step.New())
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// markDone marks a resource as having been processed. Resources that have been marked
|
||||
// in this manner won't be persisted in the snapshot.
|
||||
func (sm *SnapshotManager) markDone(state *resource.State) {
|
||||
|
|
|
@ -202,7 +202,7 @@ func GetResourcePropertiesDetails(
|
|||
if !summary {
|
||||
PrintObject(&b, old.Inputs, planning, indent, step.Op, false, debug)
|
||||
}
|
||||
} else if len(new.Outputs) > 0 {
|
||||
} else if len(new.Outputs) > 0 && step.Op != deploy.OpImport && step.Op != deploy.OpImportReplacement {
|
||||
printOldNewDiffs(&b, old.Outputs, new.Outputs, nil, planning, indent, step.Op, summary, debug)
|
||||
} else {
|
||||
printOldNewDiffs(&b, old.Inputs, new.Inputs, step.Diffs, planning, indent, step.Op, summary, debug)
|
||||
|
@ -246,12 +246,17 @@ func GetResourceOutputsPropertiesString(
|
|||
// 1) not doing a preview
|
||||
// 2) doing a refresh
|
||||
// 3) doing a read
|
||||
// 4) doing an import
|
||||
//
|
||||
// Technically, 2 and 3 are the same, since they're both bottoming out at a provider's implementation of Read, but
|
||||
// Technically, 2-4 are the same, since they're all bottoming out at a provider's implementation of Read, but
|
||||
// the upshot is that either way we're ending up with outputs that are exactly accurate. If we are not sure that we
|
||||
// are in one of the above states, we shouldn't try to print outputs.
|
||||
if planning {
|
||||
printOutputDuringPlanning := refresh || step.Op == deploy.OpRead || step.Op == deploy.OpReadReplacement
|
||||
printOutputDuringPlanning := refresh ||
|
||||
step.Op == deploy.OpRead ||
|
||||
step.Op == deploy.OpReadReplacement ||
|
||||
step.Op == deploy.OpImport ||
|
||||
step.Op == deploy.OpImportReplacement
|
||||
if !printOutputDuringPlanning {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -131,11 +131,14 @@ func (j *Journal) Snap(base *deploy.Snapshot) *deploy.Snapshot {
|
|||
ops = append(ops, resource.NewOperation(e.Step.New(), resource.OperationTypeReading))
|
||||
case deploy.OpUpdate:
|
||||
ops = append(ops, resource.NewOperation(e.Step.New(), resource.OperationTypeUpdating))
|
||||
case deploy.OpImport, deploy.OpImportReplacement:
|
||||
ops = append(ops, resource.NewOperation(e.Step.New(), resource.OperationTypeImporting))
|
||||
}
|
||||
case JournalEntryFailure, JournalEntrySuccess:
|
||||
switch e.Step.Op() {
|
||||
// nolint: lll
|
||||
case deploy.OpCreate, deploy.OpCreateReplacement, deploy.OpRead, deploy.OpReadReplacement, deploy.OpUpdate:
|
||||
case deploy.OpCreate, deploy.OpCreateReplacement, deploy.OpRead, deploy.OpReadReplacement, deploy.OpUpdate,
|
||||
deploy.OpImport, deploy.OpImportReplacement:
|
||||
doneOps[e.Step.New()] = true
|
||||
case deploy.OpDelete, deploy.OpDeleteReplaced, deploy.OpReadDiscard, deploy.OpDiscardReplaced:
|
||||
doneOps[e.Step.Old()] = true
|
||||
|
@ -166,6 +169,9 @@ func (j *Journal) Snap(base *deploy.Snapshot) *deploy.Snapshot {
|
|||
}
|
||||
case deploy.OpRemovePendingReplace:
|
||||
dones[e.Step.Old()] = true
|
||||
case deploy.OpImport, deploy.OpImportReplacement:
|
||||
resources = append(resources, e.Step.New())
|
||||
dones[e.Step.New()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -567,7 +573,7 @@ func TestSingleResourceDefaultProviderLifecycle(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -589,7 +595,8 @@ func TestSingleResourceExplicitProviderLifecycle(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true, "",
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil, "",
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if provID == "" {
|
||||
|
@ -600,7 +607,7 @@ func TestSingleResourceExplicitProviderLifecycle(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, provRef.String(),
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
return nil
|
||||
|
@ -623,7 +630,7 @@ func TestSingleResourceDefaultProviderUpgrade(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -729,7 +736,7 @@ func TestSingleResourceDefaultProviderReplace(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -811,7 +818,7 @@ func TestSingleResourceExplicitProviderReplace(t *testing.T) {
|
|||
}
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true, "",
|
||||
false, nil, "", providerInputs, nil, false, "", nil, nil)
|
||||
false, nil, "", providerInputs, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
if provID == "" {
|
||||
|
@ -822,7 +829,7 @@ func TestSingleResourceExplicitProviderReplace(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, provRef.String(),
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
return nil
|
||||
|
@ -901,7 +908,7 @@ func TestSingleResourceExplicitProviderDeleteBeforeReplace(t *testing.T) {
|
|||
}
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
provURN, provID, _, err := monitor.RegisterResource(providers.MakeProviderType("pkgA"), "provA", true, "",
|
||||
false, nil, "", providerInputs, nil, false, "", nil, nil)
|
||||
false, nil, "", providerInputs, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
if provID == "" {
|
||||
|
@ -912,7 +919,7 @@ func TestSingleResourceExplicitProviderDeleteBeforeReplace(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, provRef.String(),
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
return nil
|
||||
|
@ -1005,7 +1012,7 @@ func TestSingleResourceDiffUnavailable(t *testing.T) {
|
|||
inputs := resource.PropertyMap{}
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource(
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil)
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -1204,19 +1211,19 @@ func TestParallelRefresh(t *testing.T) {
|
|||
// it.
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
resA, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
resB, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resB", true, "", false, []resource.URN{resA}, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
resC, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resC", true, "", false, []resource.URN{resB}, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resD", true, "", false, []resource.URN{resC}, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
return nil
|
||||
|
@ -1330,7 +1337,7 @@ func TestRefreshInitFailure(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -1419,7 +1426,7 @@ func TestCheckFailureRecord(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
nil, nil, false, "", nil, nil)
|
||||
nil, nil, false, "", nil, nil, "")
|
||||
assert.Error(t, err)
|
||||
return err
|
||||
})
|
||||
|
@ -1470,7 +1477,7 @@ func TestCheckFailureInvalidPropertyRecord(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
nil, nil, false, "", nil, nil)
|
||||
nil, nil, false, "", nil, nil, "")
|
||||
assert.Error(t, err)
|
||||
return err
|
||||
})
|
||||
|
@ -1528,7 +1535,7 @@ func TestRefreshWithDelete(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource(
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", nil, nil, false, "", nil, nil)
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", nil, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return err
|
||||
})
|
||||
|
@ -2035,7 +2042,7 @@ func TestBadResourceType(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, mon *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := mon.RegisterResource(
|
||||
"very:bad", "resA", true, "", false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
"very:bad", "resA", true, "", false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.Error(t, err)
|
||||
rpcerr, ok := rpcerror.FromError(err)
|
||||
assert.True(t, ok)
|
||||
|
@ -2051,11 +2058,11 @@ func TestBadResourceType(t *testing.T) {
|
|||
|
||||
// Component resources may have any format type.
|
||||
_, _, _, noErr := mon.RegisterResource("a:component", "resB", false /* custom */, "",
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, noErr)
|
||||
|
||||
_, _, _, noErr = mon.RegisterResource("singlename", "resC", false /* custom */, "",
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, noErr)
|
||||
|
||||
return err
|
||||
|
@ -2119,7 +2126,7 @@ func TestProviderCancellation(t *testing.T) {
|
|||
for i := 0; i < resourceCount; i++ {
|
||||
go func(idx int) {
|
||||
_, _, _, errors[idx] = monitor.RegisterResource("pkgA:m:typA", fmt.Sprintf("res%d", idx), true, "",
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
false, nil, "", resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
resources.Done()
|
||||
}(i)
|
||||
}
|
||||
|
@ -2184,7 +2191,7 @@ func TestPreviewWithPendingOperations(t *testing.T) {
|
|||
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil)
|
||||
resource.PropertyMap{}, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -2230,7 +2237,7 @@ func TestUpdatePartialFailure(t *testing.T) {
|
|||
_, _, _, err := mon.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"input_prop": "new inputs",
|
||||
}), nil, false, "", nil, nil)
|
||||
}), nil, false, "", nil, nil, "")
|
||||
|
||||
return err
|
||||
})
|
||||
|
@ -2302,7 +2309,7 @@ func TestStackReference(t *testing.T) {
|
|||
_, _, state, err := mon.RegisterResource("pulumi:pulumi:StackReference", "other", true, "", false, nil, "",
|
||||
resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"name": "other",
|
||||
}), nil, false, "", nil, nil)
|
||||
}), nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
if !info.DryRun {
|
||||
assert.Equal(t, "bar", state["outputs"].ObjectValue()["foo"].StringValue())
|
||||
|
@ -2375,7 +2382,7 @@ func TestStackReference(t *testing.T) {
|
|||
_, _, _, err := mon.RegisterResource("pulumi:pulumi:StackReference", "other", true, "", false, nil, "",
|
||||
resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"name": "rehto",
|
||||
}), nil, false, "", nil, nil)
|
||||
}), nil, false, "", nil, nil, "")
|
||||
assert.Error(t, err)
|
||||
return err
|
||||
})
|
||||
|
@ -2393,7 +2400,7 @@ func TestStackReference(t *testing.T) {
|
|||
resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"name": "other",
|
||||
"foo": "bar",
|
||||
}), nil, false, "", nil, nil)
|
||||
}), nil, false, "", nil, nil, "")
|
||||
assert.Error(t, err)
|
||||
return err
|
||||
})
|
||||
|
@ -2625,7 +2632,7 @@ func TestDeleteBeforeReplace(t *testing.T) {
|
|||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
register := func(urn resource.URN, provider string, inputs resource.PropertyMap) resource.ID {
|
||||
_, id, _, err := monitor.RegisterResource(urn.Type(), string(urn.Name()), true, "", false, nil, provider,
|
||||
inputs, nil, false, "", nil, nil)
|
||||
inputs, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return id
|
||||
}
|
||||
|
@ -2700,7 +2707,7 @@ func TestPropertyDependenciesAdapter(t *testing.T) {
|
|||
dependencies []resource.URN) resource.URN {
|
||||
|
||||
urn, _, _, err := monitor.RegisterResource(resType, name, true, "", false, dependencies, "", inputs,
|
||||
inputDeps, false, "", nil, nil)
|
||||
inputDeps, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
return urn
|
||||
|
@ -2779,7 +2786,7 @@ func TestExplicitDeleteBeforeReplace(t *testing.T) {
|
|||
var err error
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
provURN, provID, _, err = monitor.RegisterResource(
|
||||
providers.MakeProviderType("pkgA"), "provA", true, "", false, nil, "", nil, nil, false, "", nil, nil)
|
||||
providers.MakeProviderType("pkgA"), "provA", true, "", false, nil, "", nil, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
if provID == "" {
|
||||
|
@ -2790,12 +2797,12 @@ func TestExplicitDeleteBeforeReplace(t *testing.T) {
|
|||
provA := provRef.String()
|
||||
|
||||
urnA, _, _, err = monitor.RegisterResource(resType, "resA", true, "", false, nil, provA, inputsA,
|
||||
nil, dbrA, "", nil, nil)
|
||||
nil, dbrA, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
inputDepsB := map[resource.PropertyKey][]resource.URN{"A": {urnA}}
|
||||
urnB, _, _, err = monitor.RegisterResource(resType, "resB", true, "", false, []resource.URN{urnA}, provA,
|
||||
inputsB, inputDepsB, false, "", nil, nil)
|
||||
inputsB, inputDepsB, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
return nil
|
||||
|
@ -2913,7 +2920,7 @@ func TestSingleResourceIgnoreChanges(t *testing.T) {
|
|||
allowedOps []deploy.StepOp) *deploy.Snapshot {
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, "", false, nil, "",
|
||||
props, nil, false, "", ignoreChanges, nil)
|
||||
props, nil, false, "", ignoreChanges, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -2989,10 +2996,10 @@ func TestDefaultProviderDiff(t *testing.T) {
|
|||
runProgram := func(base *deploy.Snapshot, versionA, versionB string, expectedStep deploy.StepOp) *deploy.Snapshot {
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", resName, true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, versionA, nil, nil)
|
||||
resource.PropertyMap{}, nil, false, versionA, nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
_, _, _, err = monitor.RegisterResource("pkgA:m:typA", resBName, true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, versionB, nil, nil)
|
||||
resource.PropertyMap{}, nil, false, versionB, nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -3106,10 +3113,10 @@ func TestDefaultProviderDiffReplacement(t *testing.T) {
|
|||
runProgram := func(base *deploy.Snapshot, versionA, versionB string, expectedSteps ...deploy.StepOp) *deploy.Snapshot {
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource("pkgA:m:typA", resName, true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, versionA, nil, nil)
|
||||
resource.PropertyMap{}, nil, false, versionA, nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
_, _, _, err = monitor.RegisterResource("pkgA:m:typA", resBName, true, "", false, nil, "",
|
||||
resource.PropertyMap{}, nil, false, versionB, nil, nil)
|
||||
resource.PropertyMap{}, nil, false, versionB, nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -3191,7 +3198,7 @@ type Resource struct {
|
|||
func registerResources(t *testing.T, monitor *deploytest.ResourceMonitor, resources []Resource) error {
|
||||
for _, r := range resources {
|
||||
_, _, _, err := monitor.RegisterResource(r.t, r.name, true, r.parent, false, r.dependencies, "",
|
||||
r.props, nil, r.deleteBeforeReplace, "", nil, r.aliases)
|
||||
r.props, nil, r.deleteBeforeReplace, "", nil, r.aliases, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -3482,7 +3489,7 @@ func TestPersistentDiff(t *testing.T) {
|
|||
inputs := resource.PropertyMap{}
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource(
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil)
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -3562,7 +3569,7 @@ func TestDetailedDiffReplace(t *testing.T) {
|
|||
inputs := resource.PropertyMap{}
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
_, _, _, err := monitor.RegisterResource(
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil)
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil, "")
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
|
@ -3598,3 +3605,249 @@ func TestDetailedDiffReplace(t *testing.T) {
|
|||
})
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
||||
func TestImport(t *testing.T) {
|
||||
loaders := []*deploytest.ProviderLoader{
|
||||
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
||||
return &deploytest.Provider{
|
||||
DiffF: func(urn resource.URN, id resource.ID,
|
||||
olds, news resource.PropertyMap) (plugin.DiffResult, error) {
|
||||
|
||||
if olds["foo"].DeepEquals(news["foo"]) {
|
||||
return plugin.DiffResult{Changes: plugin.DiffNone}, nil
|
||||
}
|
||||
|
||||
diffKind := plugin.DiffUpdate
|
||||
if news["foo"].IsString() && news["foo"].StringValue() == "replace" {
|
||||
diffKind = plugin.DiffUpdateReplace
|
||||
}
|
||||
|
||||
return plugin.DiffResult{
|
||||
Changes: plugin.DiffSome,
|
||||
DetailedDiff: map[string]plugin.PropertyDiff{
|
||||
"foo": {Kind: diffKind},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
CreateF: func(urn resource.URN,
|
||||
news resource.PropertyMap) (resource.ID, resource.PropertyMap, resource.Status, error) {
|
||||
|
||||
return "created-id", news, resource.StatusOK, nil
|
||||
},
|
||||
ReadF: func(urn resource.URN, id resource.ID,
|
||||
inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
|
||||
|
||||
return plugin.ReadResult{
|
||||
Inputs: resource.PropertyMap{
|
||||
"foo": resource.NewStringProperty("bar"),
|
||||
},
|
||||
Outputs: resource.PropertyMap{
|
||||
"foo": resource.NewStringProperty("bar"),
|
||||
},
|
||||
}, resource.StatusOK, nil
|
||||
},
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
|
||||
readID, importID, inputs := resource.ID(""), resource.ID("id"), resource.PropertyMap{}
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
var err error
|
||||
if readID != "" {
|
||||
_, _, err = monitor.ReadResource("pkgA:m:typA", "resA", readID, "", resource.PropertyMap{}, "", "")
|
||||
} else {
|
||||
_, _, _, err = monitor.RegisterResource(
|
||||
"pkgA:m:typA", "resA", true, "", false, nil, "", inputs, nil, false, "", nil, nil, importID)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
host := deploytest.NewPluginHost(nil, nil, program, loaders...)
|
||||
|
||||
p := &TestPlan{
|
||||
Options: UpdateOptions{host: host},
|
||||
}
|
||||
provURN := p.NewProviderURN("pkgA", "default", "")
|
||||
resURN := p.NewURN("pkgA:m:typA", "resA", "")
|
||||
|
||||
// Run the initial update. The import should fail due to a mismatch in inputs between the program and the
|
||||
// actual resource state.
|
||||
project := p.GetProject()
|
||||
_, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient, nil)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
// Run a second update after fixing the inputs. The import should succeed.
|
||||
inputs["foo"] = resource.NewStringProperty("bar")
|
||||
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.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
case resURN:
|
||||
assert.Equal(t, deploy.OpImport, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
assert.Len(t, snap.Resources, 2)
|
||||
|
||||
// Now, run another update. The update should succeed and there should be no diffs.
|
||||
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.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Change a property value and run a third update. The update should succeed.
|
||||
inputs["foo"] = resource.NewStringProperty("rab")
|
||||
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.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
case resURN:
|
||||
assert.Equal(t, deploy.OpUpdate, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Change the property value s.t. the resource requires replacement. The update should fail.
|
||||
inputs["foo"] = resource.NewStringProperty("replace")
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient, nil)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
// Finally, destroy the stack. The `Delete` function should be called.
|
||||
_, 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.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpDelete, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Now clear the ID to import and run an initial update to create a resource that we will import-replace.
|
||||
importID, inputs["foo"] = "", resource.NewStringProperty("bar")
|
||||
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.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
assert.Len(t, snap.Resources, 2)
|
||||
|
||||
// Set the import ID to the same ID as the existing resource and run an update. This should produce no changes.
|
||||
for _, r := range snap.Resources {
|
||||
if r.URN == resURN {
|
||||
importID = r.ID
|
||||
}
|
||||
}
|
||||
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.URN(); urn {
|
||||
case provURN, resURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Then set the import ID and run another update. The update should succeed and should show an import-replace and
|
||||
// a delete-replaced.
|
||||
importID = "id"
|
||||
_, 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.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
case resURN:
|
||||
switch entry.Step.Op() {
|
||||
case deploy.OpReplace, deploy.OpImportReplacement:
|
||||
assert.Equal(t, importID, entry.Step.New().ID)
|
||||
case deploy.OpDeleteReplaced:
|
||||
assert.NotEqual(t, importID, entry.Step.Old().ID)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Change the program to read a resource rather than creating one.
|
||||
readID = "id"
|
||||
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.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpCreate, entry.Step.Op())
|
||||
case resURN:
|
||||
assert.Equal(t, deploy.OpRead, entry.Step.Op())
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
assert.Len(t, snap.Resources, 2)
|
||||
|
||||
// Now have the program import the resource. We should see an import-replace and a read-discard.
|
||||
readID, importID = "", readID
|
||||
_, 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.URN(); urn {
|
||||
case provURN:
|
||||
assert.Equal(t, deploy.OpSame, entry.Step.Op())
|
||||
case resURN:
|
||||
switch entry.Step.Op() {
|
||||
case deploy.OpReplace, deploy.OpImportReplacement:
|
||||
assert.Equal(t, importID, entry.Step.New().ID)
|
||||
case deploy.OpDiscardReplaced:
|
||||
assert.Equal(t, importID, entry.Step.Old().ID)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected resource %v", urn)
|
||||
}
|
||||
}
|
||||
return res
|
||||
})
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func (rm *ResourceMonitor) RegisterResource(t tokens.Type, name string, custom b
|
|||
dependencies []resource.URN, provider string, inputs resource.PropertyMap,
|
||||
propertyDeps map[resource.PropertyKey][]resource.URN, deleteBeforeReplace bool,
|
||||
version string, ignoreChanges []string,
|
||||
aliases []resource.URN) (resource.URN, resource.ID, resource.PropertyMap, error) {
|
||||
aliases []resource.URN, importID resource.ID) (resource.URN, resource.ID, resource.PropertyMap, error) {
|
||||
|
||||
// marshal inputs
|
||||
ins, err := plugin.MarshalProperties(inputs, plugin.MarshalOptions{KeepUnknowns: true})
|
||||
|
@ -77,6 +77,7 @@ func (rm *ResourceMonitor) RegisterResource(t tokens.Type, name string, custom b
|
|||
IgnoreChanges: ignoreChanges,
|
||||
Version: version,
|
||||
Aliases: aliasStrings,
|
||||
ImportId: string(importID),
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
|
@ -104,6 +105,7 @@ func (rm *ResourceMonitor) ReadResource(t tokens.Type, name string, id resource.
|
|||
resp, err := rm.resmon.ReadResource(context.Background(), &pulumirpc.ReadResourceRequest{
|
||||
Type: string(t),
|
||||
Name: name,
|
||||
Id: string(id),
|
||||
Parent: string(parent),
|
||||
Provider: provider,
|
||||
Properties: ins,
|
||||
|
|
|
@ -298,7 +298,7 @@ func (d *defaultProviders) newRegisterDefaultProviderEvent(
|
|||
event := ®isterResourceEvent{
|
||||
goal: resource.NewGoal(
|
||||
providers.MakeProviderType(req.Package()),
|
||||
req.Name(), true, inputs, "", false, nil, "", nil, nil, false, nil, nil, nil),
|
||||
req.Name(), true, inputs, "", false, nil, "", nil, nil, false, nil, nil, nil, ""),
|
||||
done: done,
|
||||
}
|
||||
return event, done, nil
|
||||
|
@ -674,6 +674,7 @@ func (rm *resmon) RegisterResource(ctx context.Context,
|
|||
protect := req.GetProtect()
|
||||
deleteBeforeReplace := req.GetDeleteBeforeReplace()
|
||||
ignoreChanges := req.GetIgnoreChanges()
|
||||
id := resource.ID(req.GetImportId())
|
||||
var t tokens.Type
|
||||
|
||||
// Custom resources must have a three-part type so that we can 1) identify if they are providers and 2) retrieve the
|
||||
|
@ -755,7 +756,7 @@ func (rm *resmon) RegisterResource(ctx context.Context,
|
|||
// Send the goal state to the engine.
|
||||
step := ®isterResourceEvent{
|
||||
goal: resource.NewGoal(t, name, custom, props, parent, protect, dependencies, provider, nil,
|
||||
propertyDependencies, deleteBeforeReplace, ignoreChanges, additionalSecretOutputs, aliases),
|
||||
propertyDependencies, deleteBeforeReplace, ignoreChanges, additionalSecretOutputs, aliases, id),
|
||||
done: make(chan *RegisterResult),
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ func fixedProgram(steps []RegisterResourceEvent) deploytest.ProgramFunc {
|
|||
for _, s := range steps {
|
||||
g := s.Goal()
|
||||
urn, id, outs, err := resmon.RegisterResource(g.Type, string(g.Name), g.Custom, g.Parent, g.Protect,
|
||||
g.Dependencies, g.Provider, g.Properties, g.PropertyDependencies, false, "", nil, nil)
|
||||
g.Dependencies, g.Provider, g.Properties, g.PropertyDependencies, false, "", nil, nil, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -143,16 +143,16 @@ func TestRegisterNoDefaultProviders(t *testing.T) {
|
|||
// Register a component resource.
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal(componentURN.Type(), componentURN.Name(), false, resource.PropertyMap{}, "", false,
|
||||
nil, "", []string{}, nil, false, nil, nil, nil),
|
||||
nil, "", []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
// Register a couple resources using provider A.
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgA:index:typA", "res1", true, resource.PropertyMap{}, componentURN, false, nil,
|
||||
providerARef.String(), []string{}, nil, false, nil, nil, nil),
|
||||
providerARef.String(), []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgA:index:typA", "res2", true, resource.PropertyMap{}, componentURN, false, nil,
|
||||
providerARef.String(), []string{}, nil, false, nil, nil, nil),
|
||||
providerARef.String(), []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
// Register two more providers.
|
||||
newProviderEvent("pkgA", "providerB", nil, ""),
|
||||
|
@ -160,11 +160,11 @@ func TestRegisterNoDefaultProviders(t *testing.T) {
|
|||
// Register a few resources that use the new providers.
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgB:index:typB", "res3", true, resource.PropertyMap{}, "", false, nil,
|
||||
providerBRef.String(), []string{}, nil, false, nil, nil, nil),
|
||||
providerBRef.String(), []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgB:index:typC", "res4", true, resource.PropertyMap{}, "", false, nil,
|
||||
providerCRef.String(), []string{}, nil, false, nil, nil, nil),
|
||||
providerCRef.String(), []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -227,25 +227,25 @@ func TestRegisterDefaultProviders(t *testing.T) {
|
|||
// Register a component resource.
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal(componentURN.Type(), componentURN.Name(), false, resource.PropertyMap{}, "", false,
|
||||
nil, "", []string{}, nil, false, nil, nil, nil),
|
||||
nil, "", []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
// Register a couple resources from package A.
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgA:m:typA", "res1", true, resource.PropertyMap{},
|
||||
componentURN, false, nil, "", []string{}, nil, false, nil, nil, nil),
|
||||
componentURN, false, nil, "", []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgA:m:typA", "res2", true, resource.PropertyMap{},
|
||||
componentURN, false, nil, "", []string{}, nil, false, nil, nil, nil),
|
||||
componentURN, false, nil, "", []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
// Register a few resources from other packages.
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgB:m:typB", "res3", true, resource.PropertyMap{}, "", false,
|
||||
nil, "", []string{}, nil, false, nil, nil, nil),
|
||||
nil, "", []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
&testRegEvent{
|
||||
goal: resource.NewGoal("pkgB:m:typC", "res4", true, resource.PropertyMap{}, "", false,
|
||||
nil, "", []string{}, nil, false, nil, nil, nil),
|
||||
nil, "", []string{}, nil, false, nil, nil, nil, ""),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/pulumi/pkg/diag"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/diag"
|
||||
"github.com/pulumi/pulumi/pkg/diag/colors"
|
||||
"github.com/pulumi/pulumi/pkg/resource"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy/providers"
|
||||
|
@ -709,6 +709,137 @@ func (s *RefreshStep) Apply(preview bool) (resource.Status, StepCompleteFunc, er
|
|||
return rst, complete, err
|
||||
}
|
||||
|
||||
type ImportStep struct {
|
||||
plan *Plan // the current plan.
|
||||
reg RegisterResourceEvent // the registration intent to convey a URN back to.
|
||||
original *resource.State // the original resource, if this is an import-replace.
|
||||
old *resource.State // the state of the resource fetched from the provider.
|
||||
new *resource.State // the newly computed state of the resource after importing.
|
||||
replacing bool // true if we are replacing a Pulumi-managed resource.
|
||||
diffs []resource.PropertyKey // any keys that differed between the user's program and the actual state.
|
||||
detailedDiff map[string]plugin.PropertyDiff // the structured property diff.
|
||||
}
|
||||
|
||||
func NewImportStep(plan *Plan, reg RegisterResourceEvent, new *resource.State) Step {
|
||||
contract.Assert(new != nil)
|
||||
contract.Assert(new.URN != "")
|
||||
contract.Assert(new.ID != "")
|
||||
contract.Assert(new.Custom)
|
||||
contract.Assert(!new.Delete)
|
||||
contract.Assert(!new.External)
|
||||
|
||||
return &ImportStep{
|
||||
plan: plan,
|
||||
reg: reg,
|
||||
new: new,
|
||||
}
|
||||
}
|
||||
|
||||
func NewImportReplacementStep(plan *Plan, reg RegisterResourceEvent, original, new *resource.State) Step {
|
||||
contract.Assert(original != nil)
|
||||
contract.Assert(new != nil)
|
||||
contract.Assert(new.URN != "")
|
||||
contract.Assert(new.ID != "")
|
||||
contract.Assert(new.Custom)
|
||||
contract.Assert(!new.Delete)
|
||||
contract.Assert(!new.External)
|
||||
|
||||
return &ImportStep{
|
||||
plan: plan,
|
||||
reg: reg,
|
||||
original: original,
|
||||
new: new,
|
||||
replacing: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ImportStep) Op() StepOp {
|
||||
if s.replacing {
|
||||
return OpImportReplacement
|
||||
}
|
||||
return OpImport
|
||||
}
|
||||
|
||||
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 }
|
||||
func (s *ImportStep) Logical() bool { return !s.replacing }
|
||||
func (s *ImportStep) Diffs() []resource.PropertyKey { return s.diffs }
|
||||
func (s *ImportStep) DetailedDiff() map[string]plugin.PropertyDiff { return s.detailedDiff }
|
||||
|
||||
func (s *ImportStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
|
||||
complete := func() { s.reg.Done(&RegisterResult{State: s.new}) }
|
||||
|
||||
// Read the current state of the resource to import. If the provider does not hand us back any inputs for the
|
||||
// resource, it probably needs to be updated. If the resource does not exist at all, fail the import.
|
||||
prov, err := getProvider(s)
|
||||
if err != nil {
|
||||
return resource.StatusOK, nil, err
|
||||
}
|
||||
read, rst, err := prov.Read(s.new.URN, s.new.ID, nil, nil)
|
||||
if err != nil {
|
||||
if initErr, isInitErr := err.(*plugin.InitError); isInitErr {
|
||||
s.new.InitErrors = initErr.Reasons
|
||||
} else {
|
||||
return rst, nil, err
|
||||
}
|
||||
}
|
||||
if read.Outputs == nil {
|
||||
return rst, nil, errors.Errorf("resource '%v' does not exist", s.new.ID)
|
||||
}
|
||||
if read.Inputs == nil {
|
||||
return resource.StatusOK, nil, errors.Errorf(
|
||||
"provider does not support importing resources; please try updating the '%v' plugin",
|
||||
s.new.URN.Type().Package())
|
||||
}
|
||||
s.new.Outputs = read.Outputs
|
||||
|
||||
// Magic up an old state so the frontend can display a proper diff. This state is the output of the just-executed
|
||||
// `Read` combined with the resource identity and metadata from the desired state. This ensures that the only
|
||||
// differences between the old and new states are between the inputs and outputs.
|
||||
s.old = resource.NewState(s.new.Type, s.new.URN, s.new.Custom, false, s.new.ID, read.Inputs, read.Outputs,
|
||||
s.new.Parent, s.new.Protect, false, s.new.Dependencies, s.new.InitErrors, s.new.Provider,
|
||||
s.new.PropertyDependencies, false, nil, nil)
|
||||
|
||||
// Check the user inputs using the provider inputs for defaults.
|
||||
inputs, failures, err := prov.Check(s.new.URN, s.old.Inputs, s.new.Inputs, preview)
|
||||
if err != nil {
|
||||
return rst, nil, err
|
||||
}
|
||||
if issueCheckErrors(s.plan, s.new, s.new.URN, failures) {
|
||||
return rst, nil, errors.New("one or more inputs failed to validate")
|
||||
}
|
||||
s.new.Inputs = inputs
|
||||
|
||||
// Diff the user inputs against the provider inputs. If there are any differences, fail the import.
|
||||
diff, err := diffResource(s.new.URN, s.new.ID, s.old.Inputs, s.old.Outputs, s.new.Inputs, prov, preview)
|
||||
if err != nil {
|
||||
return rst, nil, err
|
||||
}
|
||||
s.diffs, s.detailedDiff = diff.ChangedKeys, diff.DetailedDiff
|
||||
|
||||
if diff.Changes != plugin.DiffNone {
|
||||
const message = "inputs to import do not match the existing resource"
|
||||
|
||||
if preview {
|
||||
s.plan.ctx.Diag.Warningf(diag.StreamMessage(s.new.URN, message+"; importing this resource will fail", 0))
|
||||
} else {
|
||||
err = errors.New(message)
|
||||
}
|
||||
}
|
||||
|
||||
// If we were asked to replace an existing, non-External resource, pend the deletion here.
|
||||
if err == nil && s.replacing {
|
||||
s.original.Delete = true
|
||||
}
|
||||
|
||||
return rst, complete, err
|
||||
}
|
||||
|
||||
// StepOp represents the kind of operation performed by a step. It evaluates to its string label.
|
||||
type StepOp string
|
||||
|
||||
|
@ -726,6 +857,8 @@ const (
|
|||
OpReadDiscard StepOp = "discard" // removing a resource that was read.
|
||||
OpDiscardReplaced StepOp = "discard-replaced" // discarding a read resource that was replaced.
|
||||
OpRemovePendingReplace StepOp = "remove-pending-replace" // removing a pending replace resource.
|
||||
OpImport StepOp = "import" // import an existing resource.
|
||||
OpImportReplacement StepOp = "import-replacement" // replace an existing resource with an imported resource.
|
||||
)
|
||||
|
||||
// StepOps contains the full set of step operation types.
|
||||
|
@ -743,6 +876,8 @@ var StepOps = []StepOp{
|
|||
OpReadDiscard,
|
||||
OpDiscardReplaced,
|
||||
OpRemovePendingReplace,
|
||||
OpImport,
|
||||
OpImportReplacement,
|
||||
}
|
||||
|
||||
// Color returns a suggested color for lines of this op type.
|
||||
|
@ -750,7 +885,7 @@ func (op StepOp) Color() string {
|
|||
switch op {
|
||||
case OpSame:
|
||||
return colors.SpecUnimportant
|
||||
case OpCreate:
|
||||
case OpCreate, OpImport:
|
||||
return colors.SpecCreate
|
||||
case OpDelete:
|
||||
return colors.SpecDelete
|
||||
|
@ -764,7 +899,7 @@ func (op StepOp) Color() string {
|
|||
return colors.SpecDeleteReplaced
|
||||
case OpRead:
|
||||
return colors.SpecCreate
|
||||
case OpReadReplacement:
|
||||
case OpReadReplacement, OpImportReplacement:
|
||||
return colors.SpecReplace
|
||||
case OpRefresh:
|
||||
return colors.SpecUpdate
|
||||
|
@ -808,6 +943,10 @@ func (op StepOp) RawPrefix() string {
|
|||
return "< "
|
||||
case OpDiscardReplaced:
|
||||
return "<<"
|
||||
case OpImport:
|
||||
return "= "
|
||||
case OpImportReplacement:
|
||||
return "=>"
|
||||
default:
|
||||
contract.Failf("Unrecognized resource step op: %v", op)
|
||||
return ""
|
||||
|
@ -824,6 +963,8 @@ func (op StepOp) PastTense() string {
|
|||
return "read"
|
||||
case OpReadDiscard, OpDiscardReplaced:
|
||||
return "discarded"
|
||||
case OpImport, OpImportReplacement:
|
||||
return "imported"
|
||||
default:
|
||||
contract.Failf("Unexpected resource step op: %v", op)
|
||||
return ""
|
||||
|
@ -832,7 +973,8 @@ func (op StepOp) PastTense() string {
|
|||
|
||||
// Suffix returns a suggested suffix for lines of this op type.
|
||||
func (op StepOp) Suffix() string {
|
||||
if op == OpCreateReplacement || op == OpUpdate || op == OpReplace || op == OpReadReplacement || op == OpRefresh {
|
||||
switch op {
|
||||
case OpCreateReplacement, OpUpdate, OpReplace, OpReadReplacement, OpRefresh, OpImportReplacement:
|
||||
return colors.Reset // updates and replacements colorize individual lines; get has none
|
||||
}
|
||||
return ""
|
||||
|
|
|
@ -186,6 +186,22 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
// We may be creating this resource if it previously existed in the snapshot as an External resource
|
||||
wasExternal := hasOld && old.External
|
||||
|
||||
// If the goal contains an ID, this may be an import. An import occurs if there is no old resource or if the old
|
||||
// resource's ID does not match the ID in the goal state.
|
||||
isImport := goal.Custom && goal.ID != "" && (!hasOld || old.External || old.ID != goal.ID)
|
||||
if isImport {
|
||||
// Write the ID of the resource to import into the new state and return an ImportStep or an
|
||||
// ImportReplacementStep
|
||||
new.ID = goal.ID
|
||||
if isReplace := hasOld && !recreating; isReplace {
|
||||
return []Step{
|
||||
NewImportReplacementStep(sg.plan, event, old, new),
|
||||
NewReplaceStep(sg.plan, old, new, nil, nil, nil, true),
|
||||
}, nil
|
||||
}
|
||||
return []Step{NewImportStep(sg.plan, event, new)}, nil
|
||||
}
|
||||
|
||||
// Ensure the provider is okay with this resource and fetch the inputs to pass to subsequent methods.
|
||||
var err error
|
||||
if prov != nil {
|
||||
|
@ -202,7 +218,7 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
} else if sg.issueCheckErrors(new, urn, failures) {
|
||||
} else if issueCheckErrors(sg.plan, new, urn, failures) {
|
||||
invalid = true
|
||||
}
|
||||
new.Inputs = inputs
|
||||
|
@ -307,7 +323,6 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
} else {
|
||||
return nil, result.FromError(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ensure that we received a sensible response.
|
||||
|
@ -319,6 +334,18 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
// If there were changes, check for a replacement vs. an in-place update.
|
||||
if diff.Changes == plugin.DiffSome {
|
||||
if diff.Replace() {
|
||||
// If the goal state specified an ID, issue an error: the replacement will change the ID, and is
|
||||
// therefore incompatible with the goal state.
|
||||
if goal.ID != "" {
|
||||
const message = "previously-imported resources that still specify an ID may not be replaced; " +
|
||||
"please remove the `import` declaration from your program"
|
||||
if sg.plan.preview {
|
||||
sg.plan.ctx.Diag.Warningf(diag.StreamMessage(urn, message, 0))
|
||||
} else {
|
||||
return nil, result.Errorf(message)
|
||||
}
|
||||
}
|
||||
|
||||
sg.replaces[urn] = true
|
||||
|
||||
// If we are going to perform a replacement, we need to recompute the default values. The above logic
|
||||
|
@ -328,7 +355,7 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
inputs, failures, err = prov.Check(urn, nil, goal.Properties, allowUnknowns)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
} else if sg.issueCheckErrors(new, urn, failures) {
|
||||
} else if issueCheckErrors(sg.plan, new, urn, failures) {
|
||||
return nil, result.Bail()
|
||||
}
|
||||
new.Inputs = inputs
|
||||
|
@ -709,9 +736,18 @@ func (sg *stepGenerator) diff(urn resource.URN, old, new *resource.State, oldInp
|
|||
return plugin.DiffResult{Changes: plugin.DiffSome}, nil
|
||||
}
|
||||
|
||||
return diffResource(urn, old.ID, oldInputs, oldOutputs, newInputs, prov, allowUnknowns)
|
||||
}
|
||||
|
||||
// diffResource invokes the Diff function for the given custom resource's provider and returns the result.
|
||||
func diffResource(urn resource.URN, id resource.ID, oldInputs, oldOutputs,
|
||||
newInputs resource.PropertyMap, prov plugin.Provider, allowUnknowns bool) (plugin.DiffResult, error) {
|
||||
|
||||
contract.Require(prov != nil, "prov != nil")
|
||||
|
||||
// Grab the diff from the provider. At this point we know that there were changes to the Pulumi inputs, so if the
|
||||
// provider returns an "unknown" diff result, pretend it returned "diffs exist".
|
||||
diff, err := prov.Diff(urn, old.ID, oldOutputs, newInputs, allowUnknowns)
|
||||
diff, err := prov.Diff(urn, id, oldOutputs, newInputs, allowUnknowns)
|
||||
if err != nil {
|
||||
return diff, err
|
||||
}
|
||||
|
@ -726,18 +762,17 @@ func (sg *stepGenerator) diff(urn resource.URN, old, new *resource.State, oldInp
|
|||
}
|
||||
|
||||
// issueCheckErrors prints any check errors to the diagnostics sink.
|
||||
func (sg *stepGenerator) issueCheckErrors(new *resource.State, urn resource.URN,
|
||||
failures []plugin.CheckFailure) bool {
|
||||
func issueCheckErrors(plan *Plan, new *resource.State, urn resource.URN, failures []plugin.CheckFailure) bool {
|
||||
if len(failures) == 0 {
|
||||
return false
|
||||
}
|
||||
inputs := new.Inputs
|
||||
for _, failure := range failures {
|
||||
if failure.Property != "" {
|
||||
sg.plan.Diag().Errorf(diag.GetResourcePropertyInvalidValueError(urn),
|
||||
plan.Diag().Errorf(diag.GetResourcePropertyInvalidValueError(urn),
|
||||
new.Type, urn.Name(), failure.Property, inputs[failure.Property], failure.Reason)
|
||||
} else {
|
||||
sg.plan.Diag().Errorf(
|
||||
plan.Diag().Errorf(
|
||||
diag.GetResourceInvalidError(urn), new.Type, urn.Name(), failure.Reason)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -565,8 +565,8 @@ func (p *provider) Diff(urn resource.URN, id resource.ID,
|
|||
|
||||
changes := resp.GetChanges()
|
||||
deleteBeforeReplace := resp.GetDeleteBeforeReplace()
|
||||
logging.V(7).Infof("%s success: changes=%d #replaces=%v #stables=%v delbefrepl=%v, diffs=#%v",
|
||||
label, changes, replaces, stables, deleteBeforeReplace, diffs)
|
||||
logging.V(7).Infof("%s success: changes=%d #replaces=%v #stables=%v delbefrepl=%v, diffs=#%v, detaileddiff=%v",
|
||||
label, changes, replaces, stables, deleteBeforeReplace, diffs, resp.GetDetailedDiff())
|
||||
|
||||
return DiffResult{
|
||||
Changes: DiffChanges(changes),
|
||||
|
|
|
@ -35,13 +35,14 @@ type Goal struct {
|
|||
IgnoreChanges []string // a list of property names to ignore during changes.
|
||||
AdditionalSecretOutputs []PropertyKey // outputs that should always be treated as secrets.
|
||||
Aliases []URN // additional URNs that should be aliased to this resource.
|
||||
ID ID // the expected ID of the resource, if any.
|
||||
}
|
||||
|
||||
// NewGoal allocates a new resource goal state.
|
||||
func NewGoal(t tokens.Type, name tokens.QName, custom bool, props PropertyMap,
|
||||
parent URN, protect bool, dependencies []URN, provider string, initErrors []string,
|
||||
propertyDependencies map[PropertyKey][]URN, deleteBeforeReplace bool, ignoreChanges []string,
|
||||
additionalSecretOutputs []PropertyKey, aliases []URN) *Goal {
|
||||
additionalSecretOutputs []PropertyKey, aliases []URN, id ID) *Goal {
|
||||
|
||||
return &Goal{
|
||||
Type: t,
|
||||
|
@ -58,5 +59,6 @@ func NewGoal(t tokens.Type, name tokens.QName, custom bool, props PropertyMap,
|
|||
IgnoreChanges: ignoreChanges,
|
||||
AdditionalSecretOutputs: additionalSecretOutputs,
|
||||
Aliases: aliases,
|
||||
ID: id,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ const (
|
|||
OperationTypeDeleting OperationType = "deleting"
|
||||
// OperationTypeReading is the state of resources that are being read.
|
||||
OperationTypeReading OperationType = "reading"
|
||||
// OperationTypeImporting is the state of resources that are being imported.
|
||||
OperationTypeImporting OperationType = "importing"
|
||||
)
|
||||
|
||||
// Operation represents an operation that the engine has initiated but has not yet completed. It is
|
||||
|
|
|
@ -293,6 +293,7 @@ func (ctx *Context) RegisterResource(
|
|||
Provider: inputs.provider,
|
||||
PropertyDependencies: inputs.rpcPropertyDeps,
|
||||
DeleteBeforeReplace: inputs.deleteBeforeReplace,
|
||||
ImportId: inputs.importID,
|
||||
})
|
||||
if err != nil {
|
||||
glog.V(9).Infof("RegisterResource(%s, %s): error: %v", t, name, err)
|
||||
|
@ -412,13 +413,14 @@ type resourceInputs struct {
|
|||
rpcProps *structpb.Struct
|
||||
rpcPropertyDeps map[string]*pulumirpc.RegisterResourceRequest_PropertyDependencies
|
||||
deleteBeforeReplace bool
|
||||
importID string
|
||||
}
|
||||
|
||||
// prepareResourceInputs prepares the inputs for a resource operation, shared between read and register.
|
||||
func (ctx *Context) prepareResourceInputs(props map[string]interface{}, opts ...ResourceOpt) (*resourceInputs, error) {
|
||||
// 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, err := ctx.getOpts(opts...)
|
||||
parent, optDeps, protect, provider, deleteBeforeReplace, importID, err := ctx.getOpts(opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "resolving options")
|
||||
}
|
||||
|
@ -466,6 +468,7 @@ func (ctx *Context) prepareResourceInputs(props map[string]interface{}, opts ...
|
|||
rpcProps: rpcProps,
|
||||
rpcPropertyDeps: rpcPropertyDeps,
|
||||
deleteBeforeReplace: deleteBeforeReplace,
|
||||
importID: string(importID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -477,12 +480,13 @@ type resourceOutput struct {
|
|||
|
||||
// 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(opts ...ResourceOpt) (URN, []URN, bool, string, bool, error) {
|
||||
func (ctx *Context) getOpts(opts ...ResourceOpt) (URN, []URN, bool, string, bool, ID, error) {
|
||||
var parent Resource
|
||||
var deps []Resource
|
||||
var protect bool
|
||||
var provider ProviderResource
|
||||
var deleteBeforeReplace bool
|
||||
var importID ID
|
||||
for _, opt := range opts {
|
||||
if parent == nil && opt.Parent != nil {
|
||||
parent = opt.Parent
|
||||
|
@ -499,6 +503,9 @@ func (ctx *Context) getOpts(opts ...ResourceOpt) (URN, []URN, bool, string, bool
|
|||
if !deleteBeforeReplace && opt.DeleteBeforeReplace {
|
||||
deleteBeforeReplace = true
|
||||
}
|
||||
if importID == "" && opt.Import != "" {
|
||||
importID = opt.Import
|
||||
}
|
||||
}
|
||||
|
||||
var parentURN URN
|
||||
|
@ -507,7 +514,7 @@ func (ctx *Context) getOpts(opts ...ResourceOpt) (URN, []URN, bool, string, bool
|
|||
} else {
|
||||
urn, err := parent.URN().Value()
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, err
|
||||
return "", nil, false, "", false, "", err
|
||||
}
|
||||
parentURN = urn
|
||||
}
|
||||
|
@ -518,7 +525,7 @@ func (ctx *Context) getOpts(opts ...ResourceOpt) (URN, []URN, bool, string, bool
|
|||
for i, r := range deps {
|
||||
urn, err := r.URN().Value()
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, err
|
||||
return "", nil, false, "", false, "", err
|
||||
}
|
||||
depURNs[i] = urn
|
||||
}
|
||||
|
@ -528,12 +535,12 @@ func (ctx *Context) getOpts(opts ...ResourceOpt) (URN, []URN, bool, string, bool
|
|||
if provider != nil {
|
||||
pr, err := ctx.resolveProviderReference(provider)
|
||||
if err != nil {
|
||||
return "", nil, false, "", false, err
|
||||
return "", nil, false, "", false, "", err
|
||||
}
|
||||
providerRef = pr
|
||||
}
|
||||
|
||||
return parentURN, depURNs, protect, providerRef, false, nil
|
||||
return parentURN, depURNs, protect, providerRef, false, importID, nil
|
||||
}
|
||||
|
||||
func (ctx *Context) resolveProviderReference(provider ProviderResource) (string, error) {
|
||||
|
|
|
@ -62,6 +62,11 @@ type ResourceOpt struct {
|
|||
Provider ProviderResource
|
||||
// DeleteBeforeReplace, when set to true, ensures that this resource is deleted prior to replacement.
|
||||
DeleteBeforeReplace bool
|
||||
// Import, when provided with a resource ID, indicates that this resource's provider should import its state from
|
||||
// the cloud resource with the given ID. The inputs to the resource's constructor must align with the resource's
|
||||
// current state. Once a resource has been imported, the import property must be removed from the resource's
|
||||
// options.
|
||||
Import ID
|
||||
}
|
||||
|
||||
// InvokeOpt contains optional settings that control an invoke's behavior.
|
||||
|
|
|
@ -1042,7 +1042,8 @@ proto.pulumirpc.RegisterResourceRequest.toObject = function(includeInstance, msg
|
|||
ignorechangesList: jspb.Message.getRepeatedField(msg, 12),
|
||||
acceptsecrets: jspb.Message.getFieldWithDefault(msg, 13, false),
|
||||
additionalsecretoutputsList: jspb.Message.getRepeatedField(msg, 14),
|
||||
aliasesList: jspb.Message.getRepeatedField(msg, 15)
|
||||
aliasesList: jspb.Message.getRepeatedField(msg, 15),
|
||||
importid: jspb.Message.getFieldWithDefault(msg, 16, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
|
@ -1142,6 +1143,10 @@ proto.pulumirpc.RegisterResourceRequest.deserializeBinaryFromReader = function(m
|
|||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addAliases(value);
|
||||
break;
|
||||
case 16:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setImportid(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
|
@ -1274,6 +1279,13 @@ proto.pulumirpc.RegisterResourceRequest.serializeBinaryToWriter = function(messa
|
|||
f
|
||||
);
|
||||
}
|
||||
f = message.getImportid();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
16,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -1747,6 +1759,21 @@ proto.pulumirpc.RegisterResourceRequest.prototype.clearAliasesList = function()
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string importId = 16;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.pulumirpc.RegisterResourceRequest.prototype.getImportid = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 16, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.pulumirpc.RegisterResourceRequest.prototype.setImportid = function(value) {
|
||||
jspb.Message.setProto3StringField(this, 16, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
|
|
|
@ -466,6 +466,14 @@ export interface CustomResourceOptions extends ResourceOptions {
|
|||
* to mark certain ouputs as a secrets on a per resource basis.
|
||||
*/
|
||||
additionalSecretOutputs?: string[];
|
||||
|
||||
/**
|
||||
* When provided with a resource ID, import indicates that this resource's provider should import its state from
|
||||
* the cloud resource with the given ID. The inputs to the resource's constructor must align with the resource's
|
||||
* current state. Once a resource has been imported, the import property must be removed from the resource's
|
||||
* options.
|
||||
*/
|
||||
import?: ID;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -76,6 +76,8 @@ interface ResourceResolverOperation {
|
|||
propertyToDirectDependencyURNs: Map<string, Set<URN>>;
|
||||
// A list of aliases applied to this resource.
|
||||
aliases: URN[];
|
||||
// An ID to import, if any.
|
||||
import: ID | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,6 +186,7 @@ export function registerResource(res: Resource, t: string, name: string, custom:
|
|||
req.setAcceptsecrets(true);
|
||||
req.setAdditionalsecretoutputsList((<any>opts).additionalSecretOutputs || []);
|
||||
req.setAliasesList(resop.aliases);
|
||||
req.setImportid(resop.import || "");
|
||||
|
||||
const propertyDependencies = req.getPropertydependenciesMap();
|
||||
for (const [key, resourceURNs] of resop.propertyToDirectDependencyURNs) {
|
||||
|
@ -301,11 +304,16 @@ async function prepareResource(label: string, res: Resource, custom: boolean,
|
|||
: await getRootResource();
|
||||
|
||||
let providerRef: string | undefined;
|
||||
if (custom && (<CustomResourceOptions>opts).provider) {
|
||||
const provider = (<CustomResourceOptions>opts).provider!;
|
||||
const providerURN = await provider.urn.promise();
|
||||
const providerID = await provider.id.promise() || unknownValue;
|
||||
providerRef = `${providerURN}::${providerID}`;
|
||||
let importID: ID | undefined;
|
||||
if (custom) {
|
||||
if ((<CustomResourceOptions>opts).provider !== undefined) {
|
||||
const provider = (<CustomResourceOptions>opts).provider!;
|
||||
const providerURN = await provider.urn.promise();
|
||||
const providerID = await provider.id.promise() || unknownValue;
|
||||
providerRef = `${providerURN}::${providerID}`;
|
||||
}
|
||||
|
||||
importID = (<CustomResourceOptions>opts).import;
|
||||
}
|
||||
|
||||
// Collect the URNs for explicit/implicit dependencies for the engine so that it can understand
|
||||
|
@ -348,6 +356,7 @@ async function prepareResource(label: string, res: Resource, custom: boolean,
|
|||
allDirectDependencyURNs: allDirectDependencyURNs,
|
||||
propertyToDirectDependencyURNs: propertyToDirectDependencyURNs,
|
||||
aliases: aliases,
|
||||
import: importID,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// This tests the basic creation of a single propertyless resource.
|
||||
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyResource extends pulumi.CustomResource {
|
||||
constructor(name, opts) {
|
||||
super("test:index:MyResource", name, {}, opts);
|
||||
}
|
||||
}
|
||||
|
||||
new MyResource("testResource1", { import: "testID" });
|
||||
|
|
@ -51,7 +51,7 @@ interface RunCase {
|
|||
urn: URN | undefined, props: any | undefined };
|
||||
registerResource?: (ctx: any, dryrun: boolean, t: string, name: string, res: any, dependencies?: string[],
|
||||
custom?: boolean, protect?: boolean, parent?: string, provider?: string,
|
||||
propertyDeps?: any, ignoreChanges?: string[], version?: string) => { urn: URN | undefined, id: ID | undefined, props: any | undefined };
|
||||
propertyDeps?: any, ignoreChanges?: string[], version?: string, importID?: string) => { urn: URN | undefined, id: ID | undefined, props: any | undefined };
|
||||
registerResourceOutputs?: (ctx: any, dryrun: boolean, urn: URN,
|
||||
t: string, name: string, res: any, outputs: any | undefined) => void;
|
||||
log?: (ctx: any, severity: any, message: string, urn: URN, streamId: number) => void;
|
||||
|
@ -826,6 +826,19 @@ describe("rpc", () => {
|
|||
};
|
||||
},
|
||||
},
|
||||
// A program that imports a single resource.
|
||||
"import_resource": {
|
||||
program: path.join(base, "030.import_resource"),
|
||||
expectResourceCount: 1,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[],
|
||||
custom: boolean, protect: boolean, parent: string, provider: string,
|
||||
propertyDeps: any, ignoreChanges: string[], version: string, importID: string) => {
|
||||
assert.strictEqual(t, "test:index:MyResource");
|
||||
assert.strictEqual(name, "testResource1");
|
||||
assert.strictEqual(importID, "testID");
|
||||
return { urn: makeUrn(t, name), id: importID, props: {} };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const casename of Object.keys(cases)) {
|
||||
|
@ -898,8 +911,9 @@ describe("rpc", () => {
|
|||
return { ...o, [key]: value.getUrnsList().sort() };
|
||||
}, {});
|
||||
const version: string = req.getVersion();
|
||||
const importID: string = req.getImportid();
|
||||
const { urn, id, props } = opts.registerResource(ctx, dryrun, t, name, res, deps,
|
||||
custom, protect, parent, provider, propertyDeps, ignoreChanges, version);
|
||||
custom, protect, parent, provider, propertyDeps, ignoreChanges, version, importID);
|
||||
resp.setUrn(urn);
|
||||
resp.setId(id);
|
||||
resp.setObject(gstruct.Struct.fromJavaScript(props));
|
||||
|
|
|
@ -38,7 +38,7 @@ func (m *SupportsFeatureRequest) Reset() { *m = SupportsFeatureRequest{}
|
|||
func (m *SupportsFeatureRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SupportsFeatureRequest) ProtoMessage() {}
|
||||
func (*SupportsFeatureRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{0}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{0}
|
||||
}
|
||||
func (m *SupportsFeatureRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SupportsFeatureRequest.Unmarshal(m, b)
|
||||
|
@ -76,7 +76,7 @@ func (m *SupportsFeatureResponse) Reset() { *m = SupportsFeatureResponse
|
|||
func (m *SupportsFeatureResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SupportsFeatureResponse) ProtoMessage() {}
|
||||
func (*SupportsFeatureResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{1}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{1}
|
||||
}
|
||||
func (m *SupportsFeatureResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SupportsFeatureResponse.Unmarshal(m, b)
|
||||
|
@ -125,7 +125,7 @@ func (m *ReadResourceRequest) Reset() { *m = ReadResourceRequest{} }
|
|||
func (m *ReadResourceRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ReadResourceRequest) ProtoMessage() {}
|
||||
func (*ReadResourceRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{2}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{2}
|
||||
}
|
||||
func (m *ReadResourceRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ReadResourceRequest.Unmarshal(m, b)
|
||||
|
@ -235,7 +235,7 @@ func (m *ReadResourceResponse) Reset() { *m = ReadResourceResponse{} }
|
|||
func (m *ReadResourceResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ReadResourceResponse) ProtoMessage() {}
|
||||
func (*ReadResourceResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{3}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{3}
|
||||
}
|
||||
func (m *ReadResourceResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ReadResourceResponse.Unmarshal(m, b)
|
||||
|
@ -286,6 +286,7 @@ type RegisterResourceRequest struct {
|
|||
AcceptSecrets bool `protobuf:"varint,13,opt,name=acceptSecrets" json:"acceptSecrets,omitempty"`
|
||||
AdditionalSecretOutputs []string `protobuf:"bytes,14,rep,name=additionalSecretOutputs" json:"additionalSecretOutputs,omitempty"`
|
||||
Aliases []string `protobuf:"bytes,15,rep,name=aliases" json:"aliases,omitempty"`
|
||||
ImportId string `protobuf:"bytes,16,opt,name=importId" json:"importId,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
|
@ -295,7 +296,7 @@ func (m *RegisterResourceRequest) Reset() { *m = RegisterResourceRequest
|
|||
func (m *RegisterResourceRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*RegisterResourceRequest) ProtoMessage() {}
|
||||
func (*RegisterResourceRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{4}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{4}
|
||||
}
|
||||
func (m *RegisterResourceRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_RegisterResourceRequest.Unmarshal(m, b)
|
||||
|
@ -420,6 +421,13 @@ func (m *RegisterResourceRequest) GetAliases() []string {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *RegisterResourceRequest) GetImportId() string {
|
||||
if m != nil {
|
||||
return m.ImportId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// PropertyDependencies describes the resources that a particular property depends on.
|
||||
type RegisterResourceRequest_PropertyDependencies struct {
|
||||
Urns []string `protobuf:"bytes,1,rep,name=urns" json:"urns,omitempty"`
|
||||
|
@ -436,7 +444,7 @@ func (m *RegisterResourceRequest_PropertyDependencies) String() string {
|
|||
}
|
||||
func (*RegisterResourceRequest_PropertyDependencies) ProtoMessage() {}
|
||||
func (*RegisterResourceRequest_PropertyDependencies) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{4, 0}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{4, 0}
|
||||
}
|
||||
func (m *RegisterResourceRequest_PropertyDependencies) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_RegisterResourceRequest_PropertyDependencies.Unmarshal(m, b)
|
||||
|
@ -480,7 +488,7 @@ func (m *RegisterResourceResponse) Reset() { *m = RegisterResourceRespon
|
|||
func (m *RegisterResourceResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*RegisterResourceResponse) ProtoMessage() {}
|
||||
func (*RegisterResourceResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{5}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{5}
|
||||
}
|
||||
func (m *RegisterResourceResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_RegisterResourceResponse.Unmarshal(m, b)
|
||||
|
@ -548,7 +556,7 @@ func (m *RegisterResourceOutputsRequest) Reset() { *m = RegisterResource
|
|||
func (m *RegisterResourceOutputsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*RegisterResourceOutputsRequest) ProtoMessage() {}
|
||||
func (*RegisterResourceOutputsRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_resource_968947468bf03677, []int{6}
|
||||
return fileDescriptor_resource_67f6d16eb5718708, []int{6}
|
||||
}
|
||||
func (m *RegisterResourceOutputsRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_RegisterResourceOutputsRequest.Unmarshal(m, b)
|
||||
|
@ -798,56 +806,57 @@ var _ResourceMonitor_serviceDesc = grpc.ServiceDesc{
|
|||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("resource.proto", fileDescriptor_resource_968947468bf03677) }
|
||||
func init() { proto.RegisterFile("resource.proto", fileDescriptor_resource_67f6d16eb5718708) }
|
||||
|
||||
var fileDescriptor_resource_968947468bf03677 = []byte{
|
||||
// 756 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6e, 0xd3, 0x48,
|
||||
0x14, 0x6e, 0x92, 0x36, 0x3f, 0x27, 0x6d, 0x5a, 0x4d, 0xab, 0xc6, 0xf5, 0xae, 0xba, 0x5d, 0xef,
|
||||
0x5e, 0x74, 0xf7, 0x22, 0xdd, 0x2d, 0x17, 0x2d, 0x08, 0x09, 0x09, 0x28, 0x12, 0x17, 0x15, 0xe0,
|
||||
0xde, 0x21, 0x81, 0xe4, 0xd8, 0xa7, 0xa9, 0xa9, 0xe3, 0x19, 0x66, 0xc6, 0x91, 0x72, 0xc7, 0x2d,
|
||||
0x4f, 0xc1, 0xab, 0xf0, 0x4c, 0x3c, 0x01, 0x33, 0x63, 0x3b, 0xc4, 0xb1, 0xd3, 0x14, 0xae, 0x72,
|
||||
0xfe, 0xe6, 0x78, 0xe6, 0xfb, 0xbe, 0x33, 0x13, 0xe8, 0x71, 0x14, 0x34, 0xe1, 0x3e, 0x0e, 0x18,
|
||||
0xa7, 0x92, 0x92, 0x0e, 0x4b, 0xa2, 0x64, 0x1c, 0x72, 0xe6, 0xdb, 0xbf, 0x8d, 0x28, 0x1d, 0x45,
|
||||
0x78, 0x62, 0x12, 0xc3, 0xe4, 0xfa, 0x04, 0xc7, 0x4c, 0x4e, 0xd3, 0x3a, 0xfb, 0xf7, 0xc5, 0xa4,
|
||||
0x90, 0x3c, 0xf1, 0x65, 0x96, 0xed, 0xa9, 0x9f, 0x49, 0x18, 0x20, 0x4f, 0x7d, 0xe7, 0x18, 0xf6,
|
||||
0xaf, 0x12, 0xc6, 0x28, 0x97, 0xe2, 0x05, 0x7a, 0x32, 0xe1, 0xe8, 0xe2, 0xc7, 0x04, 0x85, 0x24,
|
||||
0x3d, 0xa8, 0x87, 0x81, 0x55, 0x3b, 0xaa, 0x1d, 0x77, 0x5c, 0x65, 0x39, 0x0f, 0xa1, 0x5f, 0xaa,
|
||||
0x14, 0x8c, 0xc6, 0x02, 0xc9, 0x21, 0xc0, 0x8d, 0x27, 0xb2, 0xac, 0x59, 0xd2, 0x76, 0xe7, 0x22,
|
||||
0xce, 0xb7, 0x3a, 0xec, 0xba, 0xe8, 0x05, 0x6e, 0x76, 0xa2, 0x25, 0x9f, 0x20, 0x04, 0xd6, 0xe5,
|
||||
0x94, 0xa1, 0x55, 0x37, 0x11, 0x63, 0xeb, 0x58, 0xec, 0x8d, 0xd1, 0x6a, 0xa4, 0x31, 0x6d, 0x93,
|
||||
0x7d, 0x68, 0x32, 0x8f, 0x63, 0x2c, 0xad, 0x75, 0x13, 0xcd, 0x3c, 0x72, 0x06, 0xa0, 0x4e, 0xc5,
|
||||
0x90, 0xcb, 0x10, 0x85, 0xb5, 0xa1, 0x72, 0xdd, 0xd3, 0xfe, 0x20, 0xc5, 0x63, 0x90, 0xe3, 0x31,
|
||||
0xb8, 0x32, 0x78, 0xb8, 0x73, 0xa5, 0xc4, 0x81, 0xcd, 0x00, 0x19, 0xc6, 0x01, 0xc6, 0xbe, 0x5e,
|
||||
0xda, 0x3c, 0x6a, 0xa8, 0xb6, 0x85, 0x18, 0xb1, 0xa1, 0x9d, 0x63, 0x67, 0xb5, 0xcc, 0x67, 0x67,
|
||||
0x3e, 0xb1, 0xa0, 0x35, 0x41, 0x2e, 0x42, 0x1a, 0x5b, 0x6d, 0x93, 0xca, 0x5d, 0xf2, 0x37, 0x6c,
|
||||
0x79, 0xbe, 0x8f, 0x4c, 0x5e, 0xa1, 0xcf, 0x51, 0x0a, 0xab, 0x63, 0xd0, 0x29, 0x06, 0xc9, 0x39,
|
||||
0xf4, 0xbd, 0x20, 0x08, 0xa5, 0x5a, 0xe1, 0x45, 0x69, 0xf0, 0x55, 0x22, 0x59, 0xa2, 0xea, 0xc1,
|
||||
0x6c, 0x65, 0x59, 0x5a, 0x7f, 0xd9, 0x8b, 0x42, 0x4f, 0xa8, 0x4d, 0x77, 0x4d, 0x65, 0xee, 0x3a,
|
||||
0x1e, 0xec, 0x15, 0x31, 0xcf, 0xc8, 0xda, 0x81, 0x46, 0xc2, 0xe3, 0x0c, 0x75, 0x6d, 0x2e, 0xc0,
|
||||
0x56, 0xbf, 0x37, 0x6c, 0xce, 0xe7, 0x26, 0xf4, 0x5d, 0x1c, 0x85, 0x42, 0x22, 0x5f, 0xe4, 0x36,
|
||||
0xe7, 0xb2, 0x56, 0xc1, 0x65, 0xbd, 0x92, 0xcb, 0x46, 0x81, 0x4b, 0x15, 0xf7, 0x13, 0x21, 0xe9,
|
||||
0xd8, 0x70, 0xdc, 0x76, 0x33, 0x8f, 0x9c, 0x40, 0x93, 0x0e, 0x3f, 0xa0, 0x2f, 0x57, 0xf1, 0x9b,
|
||||
0x95, 0x69, 0x84, 0x74, 0x4a, 0xaf, 0x68, 0x9a, 0x4e, 0xb9, 0x5b, 0x62, 0xbd, 0xb5, 0x82, 0xf5,
|
||||
0xf6, 0x02, 0xeb, 0x0c, 0xf6, 0x32, 0x30, 0xa6, 0xcf, 0xe7, 0xfb, 0x74, 0x54, 0x9f, 0xee, 0xe9,
|
||||
0xe3, 0xc1, 0x6c, 0x60, 0x07, 0x4b, 0x40, 0x1a, 0xbc, 0xae, 0x58, 0x7e, 0x11, 0x4b, 0x3e, 0x75,
|
||||
0x2b, 0x3b, 0x93, 0xff, 0x60, 0x37, 0xc0, 0x08, 0x25, 0x3e, 0xc5, 0x6b, 0xaa, 0x07, 0x90, 0x45,
|
||||
0x9e, 0x8f, 0x4a, 0x23, 0xfa, 0x5c, 0x55, 0xa9, 0x79, 0x65, 0x76, 0x4b, 0xca, 0x0c, 0x47, 0xb1,
|
||||
0x2a, 0x7d, 0x76, 0xe3, 0xc5, 0x23, 0xb5, 0xed, 0x4d, 0x73, 0xfc, 0x62, 0xb0, 0xac, 0xdf, 0xad,
|
||||
0x9f, 0xd4, 0x6f, 0xef, 0xde, 0xfa, 0xdd, 0x2e, 0xe8, 0xd7, 0xfe, 0x17, 0xf6, 0xaa, 0xe0, 0xd1,
|
||||
0x22, 0x52, 0xa2, 0x15, 0x4a, 0x58, 0xba, 0xdc, 0xd8, 0xf6, 0xa7, 0x1a, 0x1c, 0x2c, 0xc5, 0x52,
|
||||
0x2b, 0xfe, 0x16, 0xa7, 0xb9, 0xe2, 0x95, 0x49, 0x2e, 0x61, 0x63, 0xe2, 0x45, 0x09, 0x66, 0x62,
|
||||
0x3f, 0xfb, 0x45, 0xaa, 0xdc, 0xb4, 0xcb, 0xa3, 0xfa, 0x79, 0xcd, 0xf9, 0x52, 0x03, 0xab, 0xbc,
|
||||
0x76, 0xe9, 0xcc, 0xa5, 0x57, 0x5f, 0x7d, 0x76, 0xf5, 0xfd, 0x90, 0x75, 0xe3, 0x7e, 0xb2, 0x56,
|
||||
0xf3, 0x21, 0xa4, 0x37, 0x8c, 0x30, 0x9f, 0x8f, 0xd4, 0xd3, 0x80, 0xa6, 0x96, 0xbe, 0x00, 0x0d,
|
||||
0xa0, 0x99, 0xeb, 0x20, 0x1c, 0x2e, 0x6e, 0x30, 0x63, 0x21, 0x9f, 0xd9, 0xf2, 0x36, 0xff, 0x87,
|
||||
0x16, 0xcd, 0x88, 0x5c, 0x71, 0x2f, 0xe4, 0x75, 0xa7, 0x5f, 0x1b, 0xb0, 0x9d, 0xf7, 0xbf, 0xa4,
|
||||
0x71, 0x28, 0x29, 0x27, 0x6f, 0x61, 0x7b, 0xe1, 0xed, 0x20, 0x7f, 0xce, 0x61, 0x5e, 0xfd, 0x02,
|
||||
0xd9, 0xce, 0x5d, 0x25, 0x29, 0xb2, 0xce, 0x1a, 0x79, 0x02, 0xcd, 0x97, 0xf1, 0x84, 0xde, 0xaa,
|
||||
0xa3, 0xcf, 0xd5, 0xa7, 0xa1, 0xbc, 0xd3, 0x41, 0x45, 0x66, 0xd6, 0xe0, 0x0d, 0x6c, 0xce, 0x5f,
|
||||
0x94, 0xe4, 0xb0, 0xa0, 0x86, 0xd2, 0xab, 0x65, 0xff, 0xb1, 0x34, 0x3f, 0x6b, 0xf9, 0x0e, 0x76,
|
||||
0x16, 0xa1, 0x26, 0xce, 0x6a, 0x91, 0xd9, 0x7f, 0xdd, 0x59, 0x33, 0x6b, 0xff, 0xbe, 0x7c, 0xed,
|
||||
0xe6, 0xf3, 0xf4, 0xcf, 0x1d, 0x1d, 0x8a, 0x6c, 0xdb, 0xfb, 0x25, 0x2a, 0x2f, 0xf4, 0xdf, 0x08,
|
||||
0x67, 0x6d, 0xd8, 0x34, 0x91, 0x07, 0xdf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x7d, 0xff, 0x47, 0xb3,
|
||||
0x83, 0x08, 0x00, 0x00,
|
||||
var fileDescriptor_resource_67f6d16eb5718708 = []byte{
|
||||
// 770 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x6e, 0xdb, 0x38,
|
||||
0x10, 0x8e, 0xed, 0xc4, 0x3f, 0xe3, 0xc4, 0x09, 0x98, 0x20, 0x56, 0xb4, 0x8b, 0x6c, 0x56, 0xbb,
|
||||
0x87, 0xec, 0x1e, 0x9c, 0xdd, 0xec, 0x21, 0xd9, 0xa2, 0x40, 0x81, 0xb6, 0x29, 0x90, 0x43, 0xd0,
|
||||
0x56, 0xb9, 0x15, 0x68, 0x01, 0x59, 0x9a, 0x38, 0x6a, 0x64, 0x91, 0x25, 0x29, 0x03, 0xbe, 0xf5,
|
||||
0x4d, 0x7a, 0xea, 0x7b, 0xf4, 0x99, 0xfa, 0x04, 0x25, 0x29, 0xc9, 0xb5, 0x2c, 0x39, 0x4e, 0x7b,
|
||||
0x32, 0xe7, 0x87, 0x23, 0xf2, 0xfb, 0xbe, 0x19, 0x1a, 0x7a, 0x1c, 0x05, 0x4d, 0xb8, 0x8f, 0x03,
|
||||
0xc6, 0xa9, 0xa4, 0xa4, 0xc3, 0x92, 0x28, 0x19, 0x87, 0x9c, 0xf9, 0xf6, 0x2f, 0x23, 0x4a, 0x47,
|
||||
0x11, 0x9e, 0x98, 0xc0, 0x30, 0xb9, 0x39, 0xc1, 0x31, 0x93, 0xd3, 0x34, 0xcf, 0xfe, 0x75, 0x31,
|
||||
0x28, 0x24, 0x4f, 0x7c, 0x99, 0x45, 0x7b, 0xea, 0x67, 0x12, 0x06, 0xc8, 0x53, 0xdb, 0x39, 0x86,
|
||||
0xfd, 0xeb, 0x84, 0x31, 0xca, 0xa5, 0x78, 0x81, 0x9e, 0x4c, 0x38, 0xba, 0xf8, 0x21, 0x41, 0x21,
|
||||
0x49, 0x0f, 0xea, 0x61, 0x60, 0xd5, 0x8e, 0x6a, 0xc7, 0x1d, 0x57, 0xad, 0x9c, 0xff, 0xa1, 0x5f,
|
||||
0xca, 0x14, 0x8c, 0xc6, 0x02, 0xc9, 0x21, 0xc0, 0xad, 0x27, 0xb2, 0xa8, 0xd9, 0xd2, 0x76, 0xe7,
|
||||
0x3c, 0xce, 0xd7, 0x3a, 0xec, 0xba, 0xe8, 0x05, 0x6e, 0x76, 0xa3, 0x25, 0x9f, 0x20, 0x04, 0xd6,
|
||||
0xe5, 0x94, 0xa1, 0x55, 0x37, 0x1e, 0xb3, 0xd6, 0xbe, 0xd8, 0x1b, 0xa3, 0xd5, 0x48, 0x7d, 0x7a,
|
||||
0x4d, 0xf6, 0xa1, 0xc9, 0x3c, 0x8e, 0xb1, 0xb4, 0xd6, 0x8d, 0x37, 0xb3, 0xc8, 0x19, 0x80, 0xba,
|
||||
0x15, 0x43, 0x2e, 0x43, 0x14, 0xd6, 0x86, 0x8a, 0x75, 0x4f, 0xfb, 0x83, 0x14, 0x8f, 0x41, 0x8e,
|
||||
0xc7, 0xe0, 0xda, 0xe0, 0xe1, 0xce, 0xa5, 0x12, 0x07, 0x36, 0x03, 0x64, 0x18, 0x07, 0x18, 0xfb,
|
||||
0x7a, 0x6b, 0xf3, 0xa8, 0xa1, 0xca, 0x16, 0x7c, 0xc4, 0x86, 0x76, 0x8e, 0x9d, 0xd5, 0x32, 0x9f,
|
||||
0x9d, 0xd9, 0xc4, 0x82, 0xd6, 0x04, 0xb9, 0x08, 0x69, 0x6c, 0xb5, 0x4d, 0x28, 0x37, 0xc9, 0x9f,
|
||||
0xb0, 0xe5, 0xf9, 0x3e, 0x32, 0x79, 0x8d, 0x3e, 0x47, 0x29, 0xac, 0x8e, 0x41, 0xa7, 0xe8, 0x24,
|
||||
0xe7, 0xd0, 0xf7, 0x82, 0x20, 0x94, 0x6a, 0x87, 0x17, 0xa5, 0xce, 0x97, 0x89, 0x64, 0x89, 0xca,
|
||||
0x07, 0x73, 0x94, 0x65, 0x61, 0xfd, 0x65, 0x2f, 0x0a, 0x3d, 0xa1, 0x0e, 0xdd, 0x35, 0x99, 0xb9,
|
||||
0xe9, 0x78, 0xb0, 0x57, 0xc4, 0x3c, 0x23, 0x6b, 0x07, 0x1a, 0x09, 0x8f, 0x33, 0xd4, 0xf5, 0x72,
|
||||
0x01, 0xb6, 0xfa, 0x83, 0x61, 0x73, 0x3e, 0x37, 0xa1, 0xef, 0xe2, 0x28, 0x14, 0x12, 0xf9, 0x22,
|
||||
0xb7, 0x39, 0x97, 0xb5, 0x0a, 0x2e, 0xeb, 0x95, 0x5c, 0x36, 0x0a, 0x5c, 0x2a, 0xbf, 0x9f, 0x08,
|
||||
0x49, 0xc7, 0x86, 0xe3, 0xb6, 0x9b, 0x59, 0xe4, 0x04, 0x9a, 0x74, 0xf8, 0x1e, 0x7d, 0xb9, 0x8a,
|
||||
0xdf, 0x2c, 0x4d, 0x23, 0xa4, 0x43, 0x7a, 0x47, 0xd3, 0x54, 0xca, 0xcd, 0x12, 0xeb, 0xad, 0x15,
|
||||
0xac, 0xb7, 0x17, 0x58, 0x67, 0xb0, 0x97, 0x81, 0x31, 0x7d, 0x3e, 0x5f, 0xa7, 0xa3, 0xea, 0x74,
|
||||
0x4f, 0x1f, 0x0f, 0x66, 0x0d, 0x3b, 0x58, 0x02, 0xd2, 0xe0, 0x55, 0xc5, 0xf6, 0x8b, 0x58, 0xf2,
|
||||
0xa9, 0x5b, 0x59, 0x99, 0xfc, 0x03, 0xbb, 0x01, 0x46, 0x28, 0xf1, 0x29, 0xde, 0x50, 0xdd, 0x80,
|
||||
0x2c, 0xf2, 0x7c, 0x54, 0x1a, 0xd1, 0xf7, 0xaa, 0x0a, 0xcd, 0x2b, 0xb3, 0x5b, 0x52, 0x66, 0x38,
|
||||
0x8a, 0x55, 0xea, 0xb3, 0x5b, 0x2f, 0x1e, 0xa9, 0x63, 0x6f, 0x9a, 0xeb, 0x17, 0x9d, 0x65, 0xfd,
|
||||
0x6e, 0xfd, 0xa0, 0x7e, 0x7b, 0x0f, 0xd6, 0xef, 0x76, 0x41, 0xbf, 0x1a, 0xf9, 0x70, 0xac, 0xc7,
|
||||
0xc7, 0x65, 0x60, 0xed, 0xa4, 0xc8, 0xe7, 0xb6, 0xfd, 0x37, 0xec, 0x55, 0x41, 0xa7, 0x05, 0xa6,
|
||||
0x04, 0x2d, 0x94, 0xe8, 0x74, 0x29, 0xb3, 0xb6, 0x3f, 0xd6, 0xe0, 0x60, 0x29, 0xce, 0xba, 0x1b,
|
||||
0xee, 0x70, 0x9a, 0x77, 0x83, 0x5a, 0x92, 0x2b, 0xd8, 0x98, 0x78, 0x51, 0x82, 0x59, 0x23, 0x9c,
|
||||
0xfd, 0x24, 0x8d, 0x6e, 0x5a, 0xe5, 0x51, 0xfd, 0xbc, 0xe6, 0x7c, 0xaa, 0x81, 0x55, 0xde, 0xbb,
|
||||
0xb4, 0x1f, 0xd3, 0xb1, 0x58, 0x9f, 0x8d, 0xc5, 0xef, 0x92, 0x6f, 0x3c, 0x4c, 0xf2, 0xaa, 0x77,
|
||||
0x84, 0xf4, 0x86, 0x11, 0xe6, 0xbd, 0x93, 0x5a, 0x1a, 0xec, 0x74, 0xa5, 0x87, 0xa3, 0x01, 0x3b,
|
||||
0x33, 0x1d, 0x84, 0xc3, 0xc5, 0x03, 0x66, 0x0c, 0xe5, 0xfd, 0x5c, 0x3e, 0xe6, 0xbf, 0xd0, 0xa2,
|
||||
0x19, 0xc9, 0x2b, 0x66, 0x46, 0x9e, 0x77, 0xfa, 0xa5, 0x01, 0xdb, 0x79, 0xfd, 0x2b, 0x1a, 0x87,
|
||||
0x92, 0x72, 0xf2, 0x06, 0xb6, 0x17, 0xde, 0x15, 0xf2, 0xfb, 0x1c, 0xe6, 0xd5, 0xaf, 0x93, 0xed,
|
||||
0xdc, 0x97, 0x92, 0x22, 0xeb, 0xac, 0x91, 0x27, 0xd0, 0xbc, 0x8c, 0x27, 0xf4, 0x4e, 0x5d, 0x7d,
|
||||
0x2e, 0x3f, 0x75, 0xe5, 0x95, 0x0e, 0x2a, 0x22, 0xb3, 0x02, 0xaf, 0x61, 0x73, 0x7e, 0x88, 0x92,
|
||||
0xc3, 0x82, 0x1a, 0x4a, 0x2f, 0x9a, 0xfd, 0xdb, 0xd2, 0xf8, 0xac, 0xe4, 0x5b, 0xd8, 0x59, 0x84,
|
||||
0x9a, 0x38, 0xab, 0x45, 0x66, 0xff, 0x71, 0x6f, 0xce, 0xac, 0xfc, 0xbb, 0xf2, 0x48, 0xce, 0x7b,
|
||||
0xed, 0xaf, 0x7b, 0x2a, 0x14, 0xd9, 0xb6, 0xf7, 0x4b, 0x54, 0x5e, 0xe8, 0xbf, 0x18, 0xce, 0xda,
|
||||
0xb0, 0x69, 0x3c, 0xff, 0x7d, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x41, 0xf6, 0x91, 0x9f, 0x08,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ message RegisterResourceRequest {
|
|||
bool acceptSecrets = 13; // when true operations should return secrets as strongly typed.
|
||||
repeated string additionalSecretOutputs = 14; // a list of output properties that should also be treated as secret, in addition to ones we detect.
|
||||
repeated string aliases = 15; // a list of additional URNs that shoud be considered the same.
|
||||
string importId = 16; // if set, this resource's state should be imported from the given ID.
|
||||
}
|
||||
|
||||
// RegisterResourceResponse is returned by the engine after a resource has finished being initialized. It includes the
|
||||
|
|
|
@ -84,6 +84,13 @@ class ResourceOptions:
|
|||
An optional existing ID to load, rather than create.
|
||||
"""
|
||||
|
||||
import_: Optional[str]
|
||||
"""
|
||||
When provided with a resource ID, import indicates that this resource's provider should import its state from the
|
||||
cloud resource with the given ID. The inputs to the resource's constructor must align with the resource's current
|
||||
state. Once a resource has been imported, the import property must be removed from the resource's options.
|
||||
"""
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
def __init__(self,
|
||||
parent: Optional['Resource'] = None,
|
||||
|
@ -95,7 +102,8 @@ class ResourceOptions:
|
|||
ignore_changes: Optional[List[str]] = None,
|
||||
version: Optional[str] = None,
|
||||
additional_secret_outputs: Optional[List[str]] = None,
|
||||
id: Optional[str] = None) -> None:
|
||||
id: Optional[str] = None,
|
||||
import_: Optional[str] = None) -> None:
|
||||
"""
|
||||
:param Optional[Resource] parent: If provided, the currently-constructing resource should be the child of
|
||||
the provided parent resource.
|
||||
|
@ -113,6 +121,10 @@ class ResourceOptions:
|
|||
:param Optional[List[string]] additional_secret_outputs: If provided, a list of output property names that should
|
||||
also be treated as secret.
|
||||
:param Optional[str] id: If provided, an existing resource ID to read, rather than create.
|
||||
:param Optional[str] import_: When provided with a resource ID, import indicates that this resource's provider should
|
||||
import its state from the cloud resource with the given ID. The inputs to the resource's constructor must align
|
||||
with the resource's current state. Once a resource has been imported, the import property must be removed from
|
||||
the resource's options.
|
||||
"""
|
||||
self.parent = parent
|
||||
self.depends_on = depends_on
|
||||
|
@ -124,6 +136,7 @@ class ResourceOptions:
|
|||
self.version = version
|
||||
self.additional_secret_outputs = additional_secret_outputs
|
||||
self.id = id
|
||||
self.import_ = import_
|
||||
|
||||
if depends_on is not None:
|
||||
for dep in depends_on:
|
||||
|
|
|
@ -22,7 +22,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
|
|||
package='pulumirpc',
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
serialized_pb=_b('\n\x0eresource.proto\x12\tpulumirpc\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x0eprovider.proto\"$\n\x16SupportsFeatureRequest\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x17SupportsFeatureResponse\x12\x12\n\nhasSupport\x18\x01 \x01(\x08\"\xfc\x01\n\x13ReadResourceRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06parent\x18\x04 \x01(\t\x12+\n\nproperties\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x14\n\x0c\x64\x65pendencies\x18\x06 \x03(\t\x12\x10\n\x08provider\x18\x07 \x01(\t\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\racceptSecrets\x18\t \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\n \x03(\t\x12\x0f\n\x07\x61liases\x18\x0b \x03(\t\"P\n\x14ReadResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12+\n\nproperties\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xbd\x04\n\x17RegisterResourceRequest\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06parent\x18\x03 \x01(\t\x12\x0e\n\x06\x63ustom\x18\x04 \x01(\x08\x12\'\n\x06object\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0f\n\x07protect\x18\x06 \x01(\x08\x12\x14\n\x0c\x64\x65pendencies\x18\x07 \x03(\t\x12\x10\n\x08provider\x18\x08 \x01(\t\x12Z\n\x14propertyDependencies\x18\t \x03(\x0b\x32<.pulumirpc.RegisterResourceRequest.PropertyDependenciesEntry\x12\x1b\n\x13\x64\x65leteBeforeReplace\x18\n \x01(\x08\x12\x0f\n\x07version\x18\x0b \x01(\t\x12\x15\n\rignoreChanges\x18\x0c \x03(\t\x12\x15\n\racceptSecrets\x18\r \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\x0e \x03(\t\x12\x0f\n\x07\x61liases\x18\x0f \x03(\t\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1at\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x46\n\x05value\x18\x02 \x01(\x0b\x32\x37.pulumirpc.RegisterResourceRequest.PropertyDependencies:\x02\x38\x01\"}\n\x18RegisterResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\'\n\x06object\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06stable\x18\x04 \x01(\x08\x12\x0f\n\x07stables\x18\x05 \x03(\t\"W\n\x1eRegisterResourceOutputsRequest\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12(\n\x07outputs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct2\xc0\x03\n\x0fResourceMonitor\x12Z\n\x0fSupportsFeature\x12!.pulumirpc.SupportsFeatureRequest\x1a\".pulumirpc.SupportsFeatureResponse\"\x00\x12?\n\x06Invoke\x12\x18.pulumirpc.InvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x12Q\n\x0cReadResource\x12\x1e.pulumirpc.ReadResourceRequest\x1a\x1f.pulumirpc.ReadResourceResponse\"\x00\x12]\n\x10RegisterResource\x12\".pulumirpc.RegisterResourceRequest\x1a#.pulumirpc.RegisterResourceResponse\"\x00\x12^\n\x17RegisterResourceOutputs\x12).pulumirpc.RegisterResourceOutputsRequest\x1a\x16.google.protobuf.Empty\"\x00\x62\x06proto3')
|
||||
serialized_pb=_b('\n\x0eresource.proto\x12\tpulumirpc\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x0eprovider.proto\"$\n\x16SupportsFeatureRequest\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x17SupportsFeatureResponse\x12\x12\n\nhasSupport\x18\x01 \x01(\x08\"\xfc\x01\n\x13ReadResourceRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06parent\x18\x04 \x01(\t\x12+\n\nproperties\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x14\n\x0c\x64\x65pendencies\x18\x06 \x03(\t\x12\x10\n\x08provider\x18\x07 \x01(\t\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\racceptSecrets\x18\t \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\n \x03(\t\x12\x0f\n\x07\x61liases\x18\x0b \x03(\t\"P\n\x14ReadResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12+\n\nproperties\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xcf\x04\n\x17RegisterResourceRequest\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06parent\x18\x03 \x01(\t\x12\x0e\n\x06\x63ustom\x18\x04 \x01(\x08\x12\'\n\x06object\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0f\n\x07protect\x18\x06 \x01(\x08\x12\x14\n\x0c\x64\x65pendencies\x18\x07 \x03(\t\x12\x10\n\x08provider\x18\x08 \x01(\t\x12Z\n\x14propertyDependencies\x18\t \x03(\x0b\x32<.pulumirpc.RegisterResourceRequest.PropertyDependenciesEntry\x12\x1b\n\x13\x64\x65leteBeforeReplace\x18\n \x01(\x08\x12\x0f\n\x07version\x18\x0b \x01(\t\x12\x15\n\rignoreChanges\x18\x0c \x03(\t\x12\x15\n\racceptSecrets\x18\r \x01(\x08\x12\x1f\n\x17\x61\x64\x64itionalSecretOutputs\x18\x0e \x03(\t\x12\x0f\n\x07\x61liases\x18\x0f \x03(\t\x12\x10\n\x08importId\x18\x10 \x01(\t\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1at\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x46\n\x05value\x18\x02 \x01(\x0b\x32\x37.pulumirpc.RegisterResourceRequest.PropertyDependencies:\x02\x38\x01\"}\n\x18RegisterResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\'\n\x06object\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06stable\x18\x04 \x01(\x08\x12\x0f\n\x07stables\x18\x05 \x03(\t\"W\n\x1eRegisterResourceOutputsRequest\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12(\n\x07outputs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct2\xc0\x03\n\x0fResourceMonitor\x12Z\n\x0fSupportsFeature\x12!.pulumirpc.SupportsFeatureRequest\x1a\".pulumirpc.SupportsFeatureResponse\"\x00\x12?\n\x06Invoke\x12\x18.pulumirpc.InvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x12Q\n\x0cReadResource\x12\x1e.pulumirpc.ReadResourceRequest\x1a\x1f.pulumirpc.ReadResourceResponse\"\x00\x12]\n\x10RegisterResource\x12\".pulumirpc.RegisterResourceRequest\x1a#.pulumirpc.RegisterResourceResponse\"\x00\x12^\n\x17RegisterResourceOutputs\x12).pulumirpc.RegisterResourceOutputsRequest\x1a\x16.google.protobuf.Empty\"\x00\x62\x06proto3')
|
||||
,
|
||||
dependencies=[google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,provider__pb2.DESCRIPTOR,])
|
||||
|
||||
|
@ -256,8 +256,8 @@ _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIES = _descriptor.Descriptor(
|
|||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=946,
|
||||
serialized_end=982,
|
||||
serialized_start=964,
|
||||
serialized_end=1000,
|
||||
)
|
||||
|
||||
_REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY = _descriptor.Descriptor(
|
||||
|
@ -293,8 +293,8 @@ _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY = _descriptor.Descriptor(
|
|||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=984,
|
||||
serialized_end=1100,
|
||||
serialized_start=1002,
|
||||
serialized_end=1118,
|
||||
)
|
||||
|
||||
_REGISTERRESOURCEREQUEST = _descriptor.Descriptor(
|
||||
|
@ -409,6 +409,13 @@ _REGISTERRESOURCEREQUEST = _descriptor.Descriptor(
|
|||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='importId', full_name='pulumirpc.RegisterResourceRequest.importId', index=15,
|
||||
number=16, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
|
@ -422,7 +429,7 @@ _REGISTERRESOURCEREQUEST = _descriptor.Descriptor(
|
|||
oneofs=[
|
||||
],
|
||||
serialized_start=527,
|
||||
serialized_end=1100,
|
||||
serialized_end=1118,
|
||||
)
|
||||
|
||||
|
||||
|
@ -480,8 +487,8 @@ _REGISTERRESOURCERESPONSE = _descriptor.Descriptor(
|
|||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1102,
|
||||
serialized_end=1227,
|
||||
serialized_start=1120,
|
||||
serialized_end=1245,
|
||||
)
|
||||
|
||||
|
||||
|
@ -518,8 +525,8 @@ _REGISTERRESOURCEOUTPUTSREQUEST = _descriptor.Descriptor(
|
|||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1229,
|
||||
serialized_end=1316,
|
||||
serialized_start=1247,
|
||||
serialized_end=1334,
|
||||
)
|
||||
|
||||
_READRESOURCEREQUEST.fields_by_name['properties'].message_type = google_dot_protobuf_dot_struct__pb2._STRUCT
|
||||
|
@ -614,8 +621,8 @@ _RESOURCEMONITOR = _descriptor.ServiceDescriptor(
|
|||
file=DESCRIPTOR,
|
||||
index=0,
|
||||
serialized_options=None,
|
||||
serialized_start=1319,
|
||||
serialized_end=1767,
|
||||
serialized_start=1337,
|
||||
serialized_end=1785,
|
||||
methods=[
|
||||
_descriptor.MethodDescriptor(
|
||||
name='SupportsFeature',
|
||||
|
|
|
@ -316,7 +316,8 @@ def register_resource(res: 'Resource', ty: str, name: str, custom: bool, props:
|
|||
version=opts.version or "",
|
||||
acceptSecrets=True,
|
||||
additionalSecretOutputs=additional_secret_outputs,
|
||||
aliases=[]
|
||||
aliases=[],
|
||||
importId=opts.import_
|
||||
)
|
||||
|
||||
def do_rpc_call():
|
||||
|
|
|
@ -94,6 +94,7 @@ class LanghostMockResourceMonitor(proto.ResourceMonitorServicer):
|
|||
delete_before_replace = request.deleteBeforeReplace
|
||||
ignore_changes = sorted(list(request.ignoreChanges))
|
||||
version = request.version
|
||||
import_ = request.importId
|
||||
|
||||
property_dependencies = {}
|
||||
for key, value in request.propertyDependencies.items():
|
||||
|
@ -101,9 +102,9 @@ class LanghostMockResourceMonitor(proto.ResourceMonitorServicer):
|
|||
|
||||
outs = {}
|
||||
if type_ != "pulumi:pulumi:Stack":
|
||||
outs = self.langhost_test.register_resource(
|
||||
context, self.dryrun, type_, name, props, deps, parent, custom, protect, provider,
|
||||
property_dependencies, delete_before_replace, ignore_changes, version)
|
||||
rrsig = signature(self.langhost_test.register_resource)
|
||||
args = [context, self.dryrun, type_, name, props, deps, parent, custom, protect, provider, property_dependencies, delete_before_replace, ignore_changes, version, import_]
|
||||
outs = self.langhost_test.register_resource(*args[0:len(rrsig.parameters)])
|
||||
if outs.get("urn"):
|
||||
urn = outs["urn"]
|
||||
self.registrations[urn] = {
|
||||
|
@ -262,7 +263,7 @@ class LanghostTest(unittest.TestCase):
|
|||
return {}
|
||||
|
||||
def register_resource(self, _ctx, _dry_run, _type, _name, _resource,
|
||||
_dependencies):
|
||||
_dependencies, _parent, _custom, _provider, _property_deps, _delete_before_replace, _import):
|
||||
"""
|
||||
Method corresponding to the `RegisterResource` resource monitor RPC call.
|
||||
Override for custom behavior or assertions.
|
||||
|
|
26
tests/integration/read/import_acquire/import_acquire_test.go
Normal file
26
tests/integration/read/import_acquire/import_acquire_test.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package ints
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/testing/integration"
|
||||
)
|
||||
|
||||
// Test that the engine is capable of assuming control of a resource that was external.
|
||||
func TestImportAcquire(t *testing.T) {
|
||||
t.Skipf("import does not yet work with dynamic providers")
|
||||
|
||||
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
||||
Dir: "step1",
|
||||
Dependencies: []string{"@pulumi/pulumi"},
|
||||
Quick: true,
|
||||
EditDirs: []integration.EditDir{
|
||||
{
|
||||
Dir: "step2",
|
||||
Additive: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
3
tests/integration/read/import_acquire/step1/Pulumi.yaml
Normal file
3
tests/integration/read/import_acquire/step1/Pulumi.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
name: import_acquire
|
||||
description: A program that adopts an external resource
|
||||
runtime: nodejs
|
18
tests/integration/read/import_acquire/step1/index.ts
Normal file
18
tests/integration/read/import_acquire/step1/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2016-2018, 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.
|
||||
|
||||
import { Resource } from "./resource";
|
||||
|
||||
// Setup: "a" is an external resource.
|
||||
const a = new Resource("a", { state: 42 }, { id: "0" });
|
14
tests/integration/read/import_acquire/step1/package.json
Normal file
14
tests/integration/read/import_acquire/step1/package.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "read_relinquish",
|
||||
"main": "bin/index.js",
|
||||
"typings": "bin/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pulumi/pulumi": "latest"
|
||||
}
|
||||
}
|
67
tests/integration/read/import_acquire/step1/resource.ts
Normal file
67
tests/integration/read/import_acquire/step1/resource.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2016-2018, 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.
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as dynamic from "@pulumi/pulumi/dynamic";
|
||||
|
||||
export class Provider implements dynamic.ResourceProvider {
|
||||
public static readonly instance = new Provider();
|
||||
|
||||
private id: number = 0;
|
||||
|
||||
public async check(olds: any, news: any): Promise<dynamic.CheckResult> {
|
||||
return {
|
||||
inputs: news,
|
||||
}
|
||||
}
|
||||
|
||||
public async diff(id: pulumi.ID, olds: any, news: any): Promise<dynamic.DiffResult> {
|
||||
if (news.state !== olds.state) {
|
||||
return {
|
||||
changes: true,
|
||||
replaces: ["state"],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
changes: false,
|
||||
}
|
||||
}
|
||||
|
||||
public async create(inputs: any): Promise<dynamic.CreateResult> {
|
||||
return {
|
||||
id: (this.id++).toString(),
|
||||
outs: inputs,
|
||||
}
|
||||
}
|
||||
|
||||
public async update(id: string, olds: any, news: any): Promise<dynamic.UpdateResult> {
|
||||
throw Error("this resource is replace-only and can't be updated");
|
||||
}
|
||||
|
||||
public async read(id: pulumi.ID, props: any): Promise<dynamic.ReadResult> {
|
||||
return {
|
||||
id: id,
|
||||
props: props,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Resource extends pulumi.dynamic.Resource {
|
||||
public readonly state: pulumi.Output<any>;
|
||||
|
||||
constructor(name: string, props: any, opts?: pulumi.CustomResourceOptions) {
|
||||
super(Provider.instance, name, props, opts);
|
||||
}
|
||||
}
|
21
tests/integration/read/import_acquire/step1/tsconfig.json
Normal file
21
tests/integration/read/import_acquire/step1/tsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "bin",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
|
18
tests/integration/read/import_acquire/step2/index.ts
Normal file
18
tests/integration/read/import_acquire/step2/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2016-2018, 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.
|
||||
|
||||
import { Resource } from "./resource";
|
||||
|
||||
// Step 2: the resource from the setup is imported, and is now managed by Pulumi.
|
||||
const a = new Resource("a", { state: 42 }, { import: "0" });
|
Loading…
Reference in a new issue