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:
Pat Gavlin 2019-07-12 11:12:01 -07:00 committed by GitHub
parent 611c616468
commit e1a52693dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 991 additions and 188 deletions

View file

@ -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)

View file

@ -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?

View file

@ -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)

View file

@ -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

View file

@ -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) {

View file

@ -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 ""
}

View file

@ -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)
}

View file

@ -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,

View file

@ -298,7 +298,7 @@ func (d *defaultProviders) newRegisterDefaultProviderEvent(
event := &registerResourceEvent{
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 := &registerResourceEvent{
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),
}

View file

@ -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, ""),
},
}

View file

@ -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 ""

View file

@ -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)
}
}

View file

@ -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),

View file

@ -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,
}
}

View file

@ -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

View file

@ -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) {

View file

@ -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.

View file

@ -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.

View file

@ -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;
}
/**

View file

@ -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,
};
}

View file

@ -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" });

View file

@ -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));

View file

@ -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,
}

View file

@ -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

View file

@ -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:

View file

@ -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',

View file

@ -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():

View file

@ -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.

View 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,
},
},
})
}

View file

@ -0,0 +1,3 @@
name: import_acquire
description: A program that adopts an external resource
runtime: nodejs

View 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" });

View 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"
}
}

View 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);
}
}

View 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"
]
}

View 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" });