diff --git a/changelog/pending/20221020--pkg--add-deletedwith-as-a-resource-option.yaml b/changelog/pending/20221020--pkg--add-deletedwith-as-a-resource-option.yaml new file mode 100644 index 000000000000..56eeea0f23c1 --- /dev/null +++ b/changelog/pending/20221020--pkg--add-deletedwith-as-a-resource-option.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: pkg + description: Add `DeletedWith` as a resource option. diff --git a/pkg/backend/display/json.go b/pkg/backend/display/json.go index f4403ee45066..95624526f0b5 100644 --- a/pkg/backend/display/json.go +++ b/pkg/backend/display/json.go @@ -85,7 +85,7 @@ func stateForJSONOutput(s *resource.State, opts Options) *resource.State { return resource.NewState(s.Type, s.URN, s.Custom, s.Delete, s.ID, inputs, outputs, s.Parent, s.Protect, s.External, s.Dependencies, s.InitErrors, s.Provider, s.PropertyDependencies, s.PendingReplacement, s.AdditionalSecretOutputs, s.Aliases, &s.CustomTimeouts, - s.ImportID, s.RetainOnDelete) + s.ImportID, s.RetainOnDelete, s.DeletedWith) } // ShowJSONEvents renders incremental engine events to stdout. diff --git a/pkg/backend/snapshot.go b/pkg/backend/snapshot.go index b1a2d5ef132d..4458b7be4862 100644 --- a/pkg/backend/snapshot.go +++ b/pkg/backend/snapshot.go @@ -231,6 +231,12 @@ func (ssm *sameSnapshotMutation) mustWrite(step *deploy.SameStep) bool { return true } + // If the DeletedWith attribute of this resource has changed, we must write the checkpoint. + if old.DeletedWith != new.DeletedWith { + logging.V(9).Infof("SnapshotManager: mustWrite() true because of DeletedWith") + return true + } + // If the protection attribute of this resource has changed, we must write the checkpoint. if old.Protect != new.Protect { logging.V(9).Infof("SnapshotManager: mustWrite() true because of Protect") diff --git a/pkg/backend/snapshot_test.go b/pkg/backend/snapshot_test.go index 0ee84587b349..93727192036b 100644 --- a/pkg/backend/snapshot_test.go +++ b/pkg/backend/snapshot_test.go @@ -603,7 +603,7 @@ func TestDeletion(t *testing.T) { }) manager, sp := MockSetup(t, snap) - step := deploy.NewDeleteStep(nil, resourceA) + step := deploy.NewDeleteStep(nil, map[resource.URN]bool{}, resourceA) mutation, err := manager.BeginMutation(step) if !assert.NoError(t, err) { t.FailNow() @@ -629,7 +629,7 @@ func TestFailedDelete(t *testing.T) { }) manager, sp := MockSetup(t, snap) - step := deploy.NewDeleteStep(nil, resourceA) + step := deploy.NewDeleteStep(nil, map[resource.URN]bool{}, resourceA) mutation, err := manager.BeginMutation(step) if !assert.NoError(t, err) { t.FailNow() @@ -802,7 +802,7 @@ func TestRecordingDeleteSuccess(t *testing.T) { resourceA, }) manager, sp := MockSetup(t, snap) - step := deploy.NewDeleteStep(nil, resourceA) + step := deploy.NewDeleteStep(nil, map[resource.URN]bool{}, resourceA) mutation, err := manager.BeginMutation(step) if !assert.NoError(t, err) { t.FailNow() @@ -834,7 +834,7 @@ func TestRecordingDeleteFailure(t *testing.T) { resourceA, }) manager, sp := MockSetup(t, snap) - step := deploy.NewDeleteStep(nil, resourceA) + step := deploy.NewDeleteStep(nil, map[resource.URN]bool{}, resourceA) mutation, err := manager.BeginMutation(step) if !assert.NoError(t, err) { t.FailNow() diff --git a/pkg/engine/lifecycletest/pulumi_test.go b/pkg/engine/lifecycletest/pulumi_test.go index bb0700c68e0e..156f494fdbd2 100644 --- a/pkg/engine/lifecycletest/pulumi_test.go +++ b/pkg/engine/lifecycletest/pulumi_test.go @@ -3702,6 +3702,215 @@ func TestRetainOnDelete(t *testing.T) { assert.Len(t, snap.Resources, 0) } +func TestDeletedWith(t *testing.T) { + t.Parallel() + + idCounter := 0 + + topURN := resource.URN("") + + 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, + ignoreChanges []string) (plugin.DiffResult, error) { + if !olds["foo"].DeepEquals(news["foo"]) { + // If foo changes do a replace, we use this to check we don't delete on replace + return plugin.DiffResult{ + Changes: plugin.DiffSome, + ReplaceKeys: []resource.PropertyKey{"foo"}, + }, nil + } + return plugin.DiffResult{}, nil + }, + CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64, + preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) { + resourceID := resource.ID(fmt.Sprintf("created-id-%d", idCounter)) + idCounter = idCounter + 1 + return resourceID, news, resource.StatusOK, nil + }, + DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap, + timeout float64) (resource.Status, error) { + if urn != topURN { + // Only topURN (aURN) should be actually deleted + assert.Fail(t, "Delete was called") + } + return resource.StatusOK, nil + }, + }, nil + }, deploytest.WithoutGrpc), + } + + ins := resource.NewPropertyMapFromMap(map[string]interface{}{ + "foo": "bar", + }) + + createResource := true + + program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error { + + if createResource { + aURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{ + Inputs: ins, + }) + assert.NoError(t, err) + topURN = aURN + + bURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resB", true, deploytest.ResourceOptions{ + Inputs: ins, + DeletedWith: aURN, + }) + assert.NoError(t, err) + + _, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resC", true, deploytest.ResourceOptions{ + Inputs: ins, + DeletedWith: bURN, + }) + assert.NoError(t, err) + } + + return nil + }) + host := deploytest.NewPluginHost(nil, nil, program, loaders...) + + p := &TestPlan{ + Options: UpdateOptions{Host: host}, + } + + project := p.GetProject() + + // Run an update to create the resource + snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil) + assert.Nil(t, res) + assert.NotNil(t, snap) + assert.Len(t, snap.Resources, 4) + assert.Equal(t, "created-id-0", snap.Resources[1].ID.String()) + assert.Equal(t, "created-id-1", snap.Resources[2].ID.String()) + assert.Equal(t, "created-id-2", snap.Resources[3].ID.String()) + + // Run a new update which will cause a replace, we should only see a provider delete for aURN but should + // get a new id for everything + ins = resource.NewPropertyMapFromMap(map[string]interface{}{ + "foo": "baz", + }) + snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil) + assert.Nil(t, res) + assert.NotNil(t, snap) + assert.Len(t, snap.Resources, 4) + assert.Equal(t, "created-id-3", snap.Resources[1].ID.String()) + assert.Equal(t, "created-id-4", snap.Resources[2].ID.String()) + assert.Equal(t, "created-id-5", snap.Resources[3].ID.String()) + + // Run a new update which will cause a delete, we still shouldn't see a provider delete for anything but aURN + createResource = false + snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil) + assert.Nil(t, res) + assert.NotNil(t, snap) + assert.Len(t, snap.Resources, 0) +} + +func TestDeletedWithCircularDependency(t *testing.T) { + // This test should be removed if DeletedWith circular dependency is taken care of. + // At the mean time, if there is a circular dependency - none shall be deleted. + t.Parallel() + + idCounter := 0 + + 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, + ignoreChanges []string) (plugin.DiffResult, error) { + return plugin.DiffResult{}, nil + }, + CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64, + preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) { + resourceID := resource.ID(fmt.Sprintf("created-id-%d", idCounter)) + idCounter = idCounter + 1 + return resourceID, news, resource.StatusOK, nil + }, + DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap, + timeout float64) (resource.Status, error) { + + assert.Fail(t, "Delete was called") + + return resource.StatusOK, nil + }, + }, nil + }, deploytest.WithoutGrpc), + } + + ins := resource.NewPropertyMapFromMap(map[string]interface{}{ + "foo": "bar", + }) + + createResource := true + cURN := resource.URN("") + + program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error { + + if createResource { + aURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{ + Inputs: ins, + DeletedWith: cURN, + }) + assert.NoError(t, err) + + bURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resB", true, deploytest.ResourceOptions{ + Inputs: ins, + DeletedWith: aURN, + }) + assert.NoError(t, err) + + cURN, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resC", true, deploytest.ResourceOptions{ + Inputs: ins, + DeletedWith: bURN, + }) + assert.NoError(t, err) + } + + return nil + }) + host := deploytest.NewPluginHost(nil, nil, program, loaders...) + + p := &TestPlan{ + Options: UpdateOptions{Host: host}, + } + + project := p.GetProject() + + // Run an update to create the resource + snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil) + assert.Nil(t, res) + assert.NotNil(t, snap) + assert.Len(t, snap.Resources, 4) + assert.Equal(t, "created-id-0", snap.Resources[1].ID.String()) + assert.Equal(t, "created-id-1", snap.Resources[2].ID.String()) + assert.Equal(t, "created-id-2", snap.Resources[3].ID.String()) + + // Run again to update DeleteWith for resA + snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil) + assert.Nil(t, res) + assert.NotNil(t, snap) + assert.Len(t, snap.Resources, 4) + assert.Equal(t, "created-id-0", snap.Resources[1].ID.String()) + assert.Equal(t, "created-id-1", snap.Resources[2].ID.String()) + assert.Equal(t, "created-id-2", snap.Resources[3].ID.String()) + + // Run a new update which will cause a delete, we still shouldn't see a provider delete + createResource = false + snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil) + assert.Nil(t, res) + assert.NotNil(t, snap) + assert.Len(t, snap.Resources, 0) +} + func TestInvalidGetIDReportsUserError(t *testing.T) { t.Parallel() diff --git a/pkg/resource/deploy/deploytest/resourcemonitor.go b/pkg/resource/deploy/deploytest/resourcemonitor.go index a7b3162d86e9..4434083e2933 100644 --- a/pkg/resource/deploy/deploytest/resourcemonitor.go +++ b/pkg/resource/deploy/deploytest/resourcemonitor.go @@ -101,6 +101,7 @@ type ResourceOptions struct { ImportID resource.ID CustomTimeouts *resource.CustomTimeouts RetainOnDelete bool + DeletedWith resource.URN SupportsPartialValues *bool Remote bool Providers map[string]string @@ -222,6 +223,7 @@ func (rm *ResourceMonitor) RegisterResource(t tokens.Type, name string, custom b RetainOnDelete: opts.RetainOnDelete, AdditionalSecretOutputs: additionalSecretOutputs, Aliases: aliasObjects, + DeletedWith: string(opts.DeletedWith), } // submit request diff --git a/pkg/resource/deploy/import.go b/pkg/resource/deploy/import.go index bb4a7413a7b8..3724363c2ace 100644 --- a/pkg/resource/deploy/import.go +++ b/pkg/resource/deploy/import.go @@ -169,7 +169,7 @@ func (i *importer) getOrCreateStackResource(ctx context.Context) (resource.URN, typ, name := resource.RootStackType, fmt.Sprintf("%s-%s", projectName, stackName) urn := resource.NewURN(stackName.Q(), projectName, "", typ, tokens.QName(name)) state := resource.NewState(typ, urn, false, false, "", resource.PropertyMap{}, nil, "", false, false, nil, nil, "", - nil, false, nil, nil, nil, "", false) + nil, false, nil, nil, nil, "", false, "") // TODO(seqnum) should stacks be created with 1? When do they ever get recreated/replaced? if !i.executeSerial(ctx, NewCreateStep(i.deployment, noopEvent(0), state)) { return "", false, false @@ -255,7 +255,7 @@ func (i *importer) registerProviders(ctx context.Context) (map[resource.URN]stri } state := resource.NewState(typ, urn, true, false, "", inputs, nil, "", false, false, nil, nil, "", nil, false, - nil, nil, nil, "", false) + nil, nil, nil, "", false, "") // TODO(seqnum) should default providers be created with 1? When do they ever get recreated/replaced? if issueCheckErrors(i.deployment, state, urn, failures) { return nil, nil, false @@ -355,7 +355,7 @@ func (i *importer) importResources(ctx context.Context) result.Result { // Create the new desired state. Note that the resource is protected. new := resource.NewState(urn.Type(), urn, true, false, imp.ID, resource.PropertyMap{}, nil, parent, imp.Protect, - false, nil, nil, provider, nil, false, nil, nil, nil, "", false) + false, nil, nil, provider, nil, false, nil, nil, nil, "", false, "") steps = append(steps, newImportDeploymentStep(i.deployment, new, randomSeed)) } diff --git a/pkg/resource/deploy/source_eval.go b/pkg/resource/deploy/source_eval.go index fcf806422809..00b70e9e23a6 100644 --- a/pkg/resource/deploy/source_eval.go +++ b/pkg/resource/deploy/source_eval.go @@ -330,7 +330,7 @@ func (d *defaultProviders) newRegisterDefaultProviderEvent( goal: resource.NewGoal( providers.MakeProviderType(req.Package()), req.Name(), true, inputs, "", false, nil, "", nil, nil, nil, - nil, nil, nil, "", nil, nil, false), + nil, nil, nil, "", nil, nil, false, ""), done: done, } return event, done, nil @@ -639,6 +639,8 @@ func (rm *resmon) SupportsFeature(ctx context.Context, hasSupport = !rm.disableOutputValues case "aliasSpecs": hasSupport = true + case "deletedWith": + hasSupport = true } logging.V(5).Infof("ResourceMonitor.SupportsFeature(id: %s) = %t", req.Id, hasSupport) @@ -954,6 +956,7 @@ func (rm *resmon) RegisterResource(ctx context.Context, id := resource.ID(req.GetImportId()) customTimeouts := req.GetCustomTimeouts() retainOnDelete := req.GetRetainOnDelete() + deletedWith := resource.URN(req.GetDeletedWith()) // Custom resources must have a three-part type so that we can 1) identify if they are providers and 2) retrieve the // provider responsible for managing a particular resource (based on the type's Package). @@ -1139,9 +1142,9 @@ func (rm *resmon) RegisterResource(ctx context.Context, logging.V(5).Infof( "ResourceMonitor.RegisterResource received: t=%v, name=%v, custom=%v, #props=%v, parent=%v, protect=%v, "+ "provider=%v, deps=%v, deleteBeforeReplace=%v, ignoreChanges=%v, aliases=%v, customTimeouts=%v, "+ - "providers=%v, replaceOnChanges=%v, retainOnDelete=%v", + "providers=%v, replaceOnChanges=%v, retainOnDelete=%v, deletedWith=%v", t, name, custom, len(props), parent, protect, providerRef, dependencies, deleteBeforeReplace, ignoreChanges, - aliases, timeouts, providerRefs, replaceOnChanges, retainOnDelete) + aliases, timeouts, providerRefs, replaceOnChanges, retainOnDelete, deletedWith) // If this is a remote component, fetch its provider and issue the construct call. Otherwise, register the resource. var result *RegisterResult @@ -1187,7 +1190,7 @@ func (rm *resmon) RegisterResource(ctx context.Context, step := ®isterResourceEvent{ goal: resource.NewGoal(t, name, custom, props, parent, protect, dependencies, providerRef.String(), nil, propertyDependencies, deleteBeforeReplace, ignoreChanges, - additionalSecretOutputs, aliases, id, &timeouts, replaceOnChanges, retainOnDelete), + additionalSecretOutputs, aliases, id, &timeouts, replaceOnChanges, retainOnDelete, deletedWith), done: make(chan *RegisterResult), } @@ -1249,6 +1252,7 @@ func (rm *resmon) RegisterResource(ctx context.Context, // • additionalSecretOutputs // • replaceOnChanges // • retainOnDelete + // • deletedWith // Revisit these semantics in Pulumi v4.0 // See this issue for more: https://github.com/pulumi/pulumi/issues/9704 if !custom { @@ -1273,6 +1277,9 @@ func (rm *resmon) RegisterResource(ctx context.Context, rm.checkComponentOption(result.State.URN, "retainOnDelete", func() bool { return retainOnDelete }) + rm.checkComponentOption(result.State.URN, "deletedWith", func() bool { + return deletedWith != "" + }) } logging.V(5).Infof( diff --git a/pkg/resource/deploy/source_eval_test.go b/pkg/resource/deploy/source_eval_test.go index cdc42e8663a1..fb7d8803903f 100644 --- a/pkg/resource/deploy/source_eval_test.go +++ b/pkg/resource/deploy/source_eval_test.go @@ -71,7 +71,7 @@ func fixedProgram(steps []RegisterResourceEvent) deploytest.ProgramFunc { s.Done(&RegisterResult{ State: resource.NewState(g.Type, urn, g.Custom, false, id, g.Properties, outs, g.Parent, g.Protect, false, g.Dependencies, nil, g.Provider, g.PropertyDependencies, false, nil, nil, nil, - "", false), + "", false, ""), }) } return nil @@ -181,16 +181,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, nil, nil, nil, nil, "", nil, nil, false), + nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, // 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, nil, nil, nil, nil, "", nil, nil, false), + providerARef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, &testRegEvent{ goal: resource.NewGoal("pkgA:index:typA", "res2", true, resource.PropertyMap{}, componentURN, false, nil, - providerARef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false), + providerARef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, // Register two more providers. newProviderEvent("pkgA", "providerB", nil, ""), @@ -198,11 +198,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, nil, nil, nil, nil, "", nil, nil, false), + providerBRef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, &testRegEvent{ goal: resource.NewGoal("pkgB:index:typC", "res4", true, resource.PropertyMap{}, "", false, nil, - providerCRef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false), + providerCRef.String(), []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, } @@ -236,7 +236,7 @@ func TestRegisterNoDefaultProviders(t *testing.T) { reg.Done(&RegisterResult{ State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, - false, nil, nil, nil, "", false), + false, nil, nil, nil, "", false, ""), }) processed++ @@ -267,25 +267,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, nil, nil, nil, nil, "", nil, nil, false), + nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, // Register a couple resources from package A. &testRegEvent{ goal: resource.NewGoal("pkgA:m:typA", "res1", true, resource.PropertyMap{}, - componentURN, false, nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false), + componentURN, false, nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, &testRegEvent{ goal: resource.NewGoal("pkgA:m:typA", "res2", true, resource.PropertyMap{}, - componentURN, false, nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false), + componentURN, false, nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, // Register a few resources from other packages. &testRegEvent{ goal: resource.NewGoal("pkgB:m:typB", "res3", true, resource.PropertyMap{}, "", false, - nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false), + nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, &testRegEvent{ goal: resource.NewGoal("pkgB:m:typC", "res4", true, resource.PropertyMap{}, "", false, - nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false), + nil, "", []string{}, nil, nil, nil, nil, nil, "", nil, nil, false, ""), }, } @@ -330,7 +330,7 @@ func TestRegisterDefaultProviders(t *testing.T) { reg.Done(&RegisterResult{ State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, - false, nil, nil, nil, "", false), + false, nil, nil, nil, "", false, ""), }) processed++ @@ -422,7 +422,7 @@ func TestReadInvokeNoDefaultProviders(t *testing.T) { read.Done(&ReadResult{ State: resource.NewState(read.Type(), urn, true, false, read.ID(), read.Properties(), resource.PropertyMap{}, read.Parent(), false, false, read.Dependencies(), nil, read.Provider(), nil, - false, nil, nil, nil, "", false), + false, nil, nil, nil, "", false, ""), }) reads++ } @@ -509,7 +509,7 @@ func TestReadInvokeDefaultProviders(t *testing.T) { e.Done(&RegisterResult{ State: resource.NewState(goal.Type, urn, goal.Custom, false, id, goal.Properties, resource.PropertyMap{}, goal.Parent, goal.Protect, false, goal.Dependencies, nil, goal.Provider, goal.PropertyDependencies, - false, nil, nil, nil, "", false), + false, nil, nil, nil, "", false, ""), }) registers++ @@ -518,7 +518,7 @@ func TestReadInvokeDefaultProviders(t *testing.T) { e.Done(&ReadResult{ State: resource.NewState(e.Type(), urn, true, false, e.ID(), e.Properties(), resource.PropertyMap{}, e.Parent(), false, false, e.Dependencies(), nil, e.Provider(), nil, false, - nil, nil, nil, "", false), + nil, nil, nil, "", false, ""), }) reads++ } @@ -673,7 +673,7 @@ func TestDisableDefaultProviders(t *testing.T) { event.Done(&ReadResult{ State: resource.NewState(event.Type(), urn, true, false, event.ID(), event.Properties(), resource.PropertyMap{}, event.Parent(), false, false, event.Dependencies(), nil, event.Provider(), nil, - false, nil, nil, nil, "", false), + false, nil, nil, nil, "", false, ""), }) reads++ case RegisterResourceEvent: @@ -681,7 +681,7 @@ func TestDisableDefaultProviders(t *testing.T) { event.Done(&RegisterResult{ State: resource.NewState(event.Goal().Type, urn, true, false, event.Goal().ID, event.Goal().Properties, resource.PropertyMap{}, event.Goal().Parent, false, false, event.Goal().Dependencies, nil, - event.Goal().Provider, nil, false, nil, nil, nil, "", false), + event.Goal().Provider, nil, false, nil, nil, nil, "", false, ""), }) registers++ default: diff --git a/pkg/resource/deploy/step.go b/pkg/resource/deploy/step.go index 68b39310b82d..bc162dcc5e91 100644 --- a/pkg/resource/deploy/step.go +++ b/pkg/resource/deploy/step.go @@ -268,29 +268,38 @@ func (s *CreateStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err // DeleteStep is a mutating step that deletes an existing resource. If `old` is marked "External", // DeleteStep is a no-op. type DeleteStep struct { - deployment *Deployment // the current deployment. - old *resource.State // the state of the existing resource. - replacing bool // true if part of a replacement. + deployment *Deployment // the current deployment. + old *resource.State // the state of the existing resource. + replacing bool // true if part of a replacement. + otherDeletions map[resource.URN]bool // other resources that are planned to delete } var _ Step = (*DeleteStep)(nil) -func NewDeleteStep(deployment *Deployment, old *resource.State) Step { +func NewDeleteStep(deployment *Deployment, otherDeletions map[resource.URN]bool, old *resource.State) Step { contract.Assert(old != nil) contract.Assert(old.URN != "") contract.Assert(old.ID != "" || !old.Custom) contract.Assert(!old.Custom || old.Provider != "" || providers.IsProviderType(old.Type)) + contract.Assert(otherDeletions != nil) return &DeleteStep{ - deployment: deployment, - old: old, + deployment: deployment, + old: old, + otherDeletions: otherDeletions, } } -func NewDeleteReplacementStep(deployment *Deployment, old *resource.State, pendingReplace bool) Step { +func NewDeleteReplacementStep( + deployment *Deployment, + otherDeletions map[resource.URN]bool, + old *resource.State, + pendingReplace bool, +) Step { contract.Assert(old != nil) contract.Assert(old.URN != "") contract.Assert(old.ID != "" || !old.Custom) contract.Assert(!old.Custom || old.Provider != "" || providers.IsProviderType(old.Type)) + contract.Assert(otherDeletions != nil) // There are two cases in which we create a delete-replacment step: // @@ -307,9 +316,10 @@ func NewDeleteReplacementStep(deployment *Deployment, old *resource.State, pendi contract.Assert(pendingReplace != old.Delete) old.PendingReplacement = pendingReplace return &DeleteStep{ - deployment: deployment, - old: old, - replacing: true, + deployment: deployment, + otherDeletions: otherDeletions, + old: old, + replacing: true, } } @@ -335,6 +345,17 @@ func (s *DeleteStep) New() *resource.State { return nil } func (s *DeleteStep) Res() *resource.State { return s.old } func (s *DeleteStep) Logical() bool { return !s.replacing } +func isDeletedWith(with resource.URN, otherDeletions map[resource.URN]bool) bool { + if with == "" { + return false + } + r, ok := otherDeletions[with] + if !ok { + return false + } + return r +} + func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) { // Refuse to delete protected resources (unless we're replacing them in // which case we will of checked protect elsewhere) @@ -352,6 +373,8 @@ func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err // Deleting an External resource is a no-op, since Pulumi does not own the lifecycle. } else if s.old.RetainOnDelete { // Deleting a "drop on delete" is a no-op as the user has explicitly asked us to not delete the resource. + } else if isDeletedWith(s.old.DeletedWith, s.otherDeletions) { + // No need to delete this resource since this resource will be deleted by the another deletion } else if s.old.Custom { // Not preview and not external and not Drop and is custom, do the actual delete @@ -784,7 +807,7 @@ func (s *RefreshStep) Apply(preview bool) (resource.Status, StepCompleteFunc, er s.new = resource.NewState(s.old.Type, s.old.URN, s.old.Custom, s.old.Delete, resourceID, inputs, outputs, s.old.Parent, s.old.Protect, s.old.External, s.old.Dependencies, initErrors, s.old.Provider, s.old.PropertyDependencies, s.old.PendingReplacement, s.old.AdditionalSecretOutputs, s.old.Aliases, - &s.old.CustomTimeouts, s.old.ImportID, s.old.RetainOnDelete) + &s.old.CustomTimeouts, s.old.ImportID, s.old.RetainOnDelete, s.old.DeletedWith) } else { s.new = nil } @@ -933,7 +956,8 @@ func (s *ImportStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err // 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, &s.new.CustomTimeouts, s.new.ImportID, s.new.RetainOnDelete) + s.new.PropertyDependencies, false, nil, nil, &s.new.CustomTimeouts, s.new.ImportID, s.new.RetainOnDelete, + s.new.DeletedWith) // If this step came from an import deployment, we need to fetch any required inputs from the state. if s.planned { diff --git a/pkg/resource/deploy/step_generator.go b/pkg/resource/deploy/step_generator.go index f3e1e0ee77e6..feccbc752cf7 100644 --- a/pkg/resource/deploy/step_generator.go +++ b/pkg/resource/deploy/step_generator.go @@ -200,6 +200,7 @@ func (sg *stepGenerator) GenerateReadSteps(event ReadResourceEvent) ([]Step, res nil, /* customTimeouts */ "", /* importID */ false, /* retainOnDelete */ + "", /* deletedWith */ ) old, hasOld := sg.deployment.Olds()[urn] @@ -512,7 +513,7 @@ func (sg *stepGenerator) generateSteps(event RegisterResourceEvent) ([]Step, res // get serialized into the checkpoint file. new := resource.NewState(goal.Type, urn, goal.Custom, false, "", inputs, nil, goal.Parent, goal.Protect, false, goal.Dependencies, goal.InitErrors, goal.Provider, goal.PropertyDependencies, false, - goal.AdditionalSecretOutputs, aliasUrns, &goal.CustomTimeouts, "", goal.RetainOnDelete) + goal.AdditionalSecretOutputs, aliasUrns, &goal.CustomTimeouts, "", goal.RetainOnDelete, goal.DeletedWith) // Mark the URN/resource as having been seen. So we can run analyzers on all resources seen, as well as // lookup providers for calculating replacement of resources that use the provider. @@ -980,7 +981,7 @@ func (sg *stepGenerator) generateStepsFromDiff( logging.V(7).Infof("Planner decided to delete '%v' due to dependence on condemned resource '%v'", dependentResource.URN, urn) - steps = append(steps, NewDeleteReplacementStep(sg.deployment, dependentResource, true)) + steps = append(steps, NewDeleteReplacementStep(sg.deployment, sg.deletes, dependentResource, true)) // Mark the condemned resource as deleted. We won't know until later in the deployment whether // or not we're going to be replacing this resource. sg.deletes[dependentResource.URN] = true @@ -988,7 +989,7 @@ func (sg *stepGenerator) generateStepsFromDiff( } return append(steps, - NewDeleteReplacementStep(sg.deployment, old, true), + NewDeleteReplacementStep(sg.deployment, sg.deletes, old, true), NewReplaceStep(sg.deployment, old, new, diff.ReplaceKeys, diff.ChangedKeys, diff.DetailedDiff, false), NewCreateReplacementStep( sg.deployment, event, old, new, diff.ReplaceKeys, diff.ChangedKeys, diff.DetailedDiff, false), @@ -1064,7 +1065,7 @@ func (sg *stepGenerator) GenerateDeletes(targetsOpt UrnTargets) ([]Step, result. logging.V(7).Infof("Planner decided to delete '%v' due to replacement", res.URN) sg.deletes[res.URN] = true - dels = append(dels, NewDeleteReplacementStep(sg.deployment, res, false)) + dels = append(dels, NewDeleteReplacementStep(sg.deployment, sg.deletes, res, false)) } else if _, aliased := sg.aliased[res.URN]; !sg.sames[res.URN] && !sg.updates[res.URN] && !sg.replaces[res.URN] && !sg.reads[res.URN] && !aliased { // NOTE: we deliberately do not check sg.deletes here, as it is possible for us to issue multiple @@ -1072,7 +1073,7 @@ func (sg *stepGenerator) GenerateDeletes(targetsOpt UrnTargets) ([]Step, result. logging.V(7).Infof("Planner decided to delete '%v'", res.URN) sg.deletes[res.URN] = true if !res.PendingReplacement { - dels = append(dels, NewDeleteStep(sg.deployment, res)) + dels = append(dels, NewDeleteStep(sg.deployment, sg.deletes, res)) } else { dels = append(dels, NewRemovePendingReplaceStep(sg.deployment, res)) } diff --git a/pkg/resource/stack/deployment.go b/pkg/resource/stack/deployment.go index 64f9d0e742ec..cc581530c6b9 100644 --- a/pkg/resource/stack/deployment.go +++ b/pkg/resource/stack/deployment.go @@ -329,6 +329,7 @@ func SerializeResource(res *resource.State, enc config.Encrypter, showSecrets bo Aliases: res.Aliases, ImportID: res.ImportID, RetainOnDelete: res.RetainOnDelete, + DeletedWith: res.DeletedWith, } if res.CustomTimeouts.IsNotEmpty() { @@ -512,7 +513,7 @@ func DeserializeResource(res apitype.ResourceV3, dec config.Decrypter, enc confi res.Type, res.URN, res.Custom, res.Delete, res.ID, inputs, outputs, res.Parent, res.Protect, res.External, res.Dependencies, res.InitErrors, res.Provider, res.PropertyDependencies, res.PendingReplacement, res.AdditionalSecretOutputs, res.Aliases, res.CustomTimeouts, - res.ImportID, res.RetainOnDelete), nil + res.ImportID, res.RetainOnDelete, res.DeletedWith), nil } func DeserializeOperation(op apitype.OperationV2, dec config.Decrypter, diff --git a/pkg/resource/stack/deployment_test.go b/pkg/resource/stack/deployment_test.go index af1893c5f775..c0e7ca72a409 100644 --- a/pkg/resource/stack/deployment_test.go +++ b/pkg/resource/stack/deployment_test.go @@ -99,6 +99,7 @@ func TestDeploymentSerialization(t *testing.T) { nil, "", false, + "", ) dep, err := SerializeResource(res, config.NopEncrypter, false /* showSecrets */) diff --git a/proto/.checksum.txt b/proto/.checksum.txt index e97e5f7ed11e..5468aab4bdce 100644 --- a/proto/.checksum.txt +++ b/proto/.checksum.txt @@ -14,4 +14,4 @@ 3300935796 5024 proto/pulumi/language.proto 2700626499 1743 proto/pulumi/plugin.proto 1451439690 19667 proto/pulumi/provider.proto -3808155704 10824 proto/pulumi/resource.proto +1325776472 11014 proto/pulumi/resource.proto diff --git a/proto/pulumi/resource.proto b/proto/pulumi/resource.proto index 56712490b12c..17b9a17d2ab4 100644 --- a/proto/pulumi/resource.proto +++ b/proto/pulumi/resource.proto @@ -117,6 +117,7 @@ message RegisterResourceRequest { string pluginDownloadURL = 24; // the server URL of the provider to use when servicing this request. bool retainOnDelete = 25; // if true the engine will not call the resource providers delete method for this resource. repeated Alias aliases = 26; // a list of additional aliases that should be considered the same. + string deletedWith = 27; // if set the engine will not call the resource providers delete method for this resource when specified resource is deleted. } // RegisterResourceResponse is returned by the engine after a resource has finished being initialized. It includes the diff --git a/sdk/dotnet/Pulumi/Deployment/Deployment.cs b/sdk/dotnet/Pulumi/Deployment/Deployment.cs index 4ad5d4d2e1c6..0b45a56230a6 100644 --- a/sdk/dotnet/Pulumi/Deployment/Deployment.cs +++ b/sdk/dotnet/Pulumi/Deployment/Deployment.cs @@ -217,6 +217,11 @@ internal Task MonitorSupportsOutputValues() return MonitorSupportsFeature("outputValues"); } + internal Task MonitorSupportsDeletedWith() + { + return MonitorSupportsFeature("deletedWith"); + } + // Because the secrets feature predates the Pulumi .NET SDK, we assume // that the monitor supports secrets. } diff --git a/sdk/dotnet/Pulumi/Deployment/Deployment_RegisterResource.cs b/sdk/dotnet/Pulumi/Deployment/Deployment_RegisterResource.cs index 8b98fbe450f8..494bda92a220 100644 --- a/sdk/dotnet/Pulumi/Deployment/Deployment_RegisterResource.cs +++ b/sdk/dotnet/Pulumi/Deployment/Deployment_RegisterResource.cs @@ -21,7 +21,11 @@ public partial class Deployment var label = $"resource:{name}[{type}]"; Log.Debug($"Registering resource start: t={type}, name={name}, custom={custom}, remote={remote}"); - var request = CreateRegisterResourceRequest(type, name, custom, remote, options); + if (options.DeletedWith != null && !(await MonitorSupportsDeletedWith().ConfigureAwait(false))) { + throw new Exception("The Pulumi CLI does not support the DeletedWith option. Please update the Pulumi CLI."); + } + + var request = await CreateRegisterResourceRequest(type, name, custom, remote, options); Log.Debug($"Preparing resource: t={type}, name={name}, custom={custom}, remote={remote}"); var prepareResult = await PrepareResourceAsync(label, resource, custom, remote, args, options).ConfigureAwait(false); @@ -64,11 +68,15 @@ private static void PopulateRequest(RegisterResourceRequest request, PrepareResu } } - private static RegisterResourceRequest CreateRegisterResourceRequest( + private async static Task CreateRegisterResourceRequest( string type, string name, bool custom, bool remote, ResourceOptions options) { var customOpts = options as CustomResourceOptions; var deleteBeforeReplace = customOpts?.DeleteBeforeReplace; + var deletedWith = ""; + if (options.DeletedWith != null) { + deletedWith = await options.DeletedWith.Urn.GetValueAsync("").ConfigureAwait(false); + } var request = new RegisterResourceRequest { @@ -91,6 +99,7 @@ private static void PopulateRequest(RegisterResourceRequest request, PrepareResu }, Remote = remote, RetainOnDelete = options.RetainOnDelete ?? false, + DeletedWith = deletedWith, }; if (customOpts != null) diff --git a/sdk/dotnet/Pulumi/PublicAPI.Shipped.txt b/sdk/dotnet/Pulumi/PublicAPI.Shipped.txt index bdc89965e566..fa8c52b5998c 100644 --- a/sdk/dotnet/Pulumi/PublicAPI.Shipped.txt +++ b/sdk/dotnet/Pulumi/PublicAPI.Shipped.txt @@ -200,6 +200,8 @@ Pulumi.ResourceOptions.PluginDownloadURL.get -> string Pulumi.ResourceOptions.PluginDownloadURL.set -> void Pulumi.ResourceOptions.RetainOnDelete.get -> bool? Pulumi.ResourceOptions.RetainOnDelete.set -> void +Pulumi.ResourceOptions.DeletedWith.get -> Pulumi.Resource +Pulumi.ResourceOptions.DeletedWith.set -> void Pulumi.ResourceTransformation Pulumi.ResourceTransformationArgs Pulumi.ResourceTransformationArgs.Args.get -> Pulumi.ResourceArgs diff --git a/sdk/dotnet/Pulumi/Resources/ResourceOptions.cs b/sdk/dotnet/Pulumi/Resources/ResourceOptions.cs index 4cbc4a52ae74..916a7cd88784 100644 --- a/sdk/dotnet/Pulumi/Resources/ResourceOptions.cs +++ b/sdk/dotnet/Pulumi/Resources/ResourceOptions.cs @@ -123,5 +123,11 @@ public List ReplaceOnChanges /// If set to True, the providers Delete method will not be called for this resource. /// public bool? RetainOnDelete { get; set; } + + /// + /// If set, the providers Delete method will not be called for this resource + /// if specified resource is being deleted as well. + /// + public Resource? DeletedWith { get; set; } } } diff --git a/sdk/dotnet/Pulumi/Resources/ResourceOptions_Copy.cs b/sdk/dotnet/Pulumi/Resources/ResourceOptions_Copy.cs index 844c4eead87a..d3388be70e9c 100644 --- a/sdk/dotnet/Pulumi/Resources/ResourceOptions_Copy.cs +++ b/sdk/dotnet/Pulumi/Resources/ResourceOptions_Copy.cs @@ -23,7 +23,8 @@ internal static TResourceOptions CreateCopy(ResourceOptions op Urn = options.Urn, Version = options.Version, PluginDownloadURL = options.PluginDownloadURL, - RetainOnDelete = options.RetainOnDelete + RetainOnDelete = options.RetainOnDelete, + DeletedWith = options.DeletedWith }; internal static CustomResourceOptions CreateCustomResourceOptionsCopy(ResourceOptions? options) diff --git a/sdk/dotnet/Pulumi/Resources/ResourceOptions_Merge.cs b/sdk/dotnet/Pulumi/Resources/ResourceOptions_Merge.cs index 5ec6ee11b0c4..f8b91ca743a8 100644 --- a/sdk/dotnet/Pulumi/Resources/ResourceOptions_Merge.cs +++ b/sdk/dotnet/Pulumi/Resources/ResourceOptions_Merge.cs @@ -15,6 +15,7 @@ internal static void MergeNormalOptions(ResourceOptions options1, ResourceOption options1.Provider = options2.Provider ?? options1.Provider; options1.CustomTimeouts = options2.CustomTimeouts ?? options1.CustomTimeouts; options1.RetainOnDelete = options2.RetainOnDelete ?? options1.RetainOnDelete; + options1.DeletedWith = options2.DeletedWith ?? options1.DeletedWith; options1.IgnoreChanges.AddRange(options2.IgnoreChanges); options1.ResourceTransformations.AddRange(options2.ResourceTransformations); diff --git a/sdk/go/common/apitype/core.go b/sdk/go/common/apitype/core.go index 5ea009917010..1e35bb907e3c 100644 --- a/sdk/go/common/apitype/core.go +++ b/sdk/go/common/apitype/core.go @@ -326,6 +326,9 @@ type ResourceV3 struct { ImportID resource.ID `json:"importID,omitempty" yaml:"importID,omitempty"` // If set to True, the providers Delete method will not be called for this resource. Pulumi simply stops tracking the deleted resource. RetainOnDelete bool `json:"retainOnDelete,omitempty" yaml:"retainOnDelete,omitempty"` + // If set, the providers Delete method will not be called for this resource + // if specified resource is being deleted as well. + DeletedWith resource.URN `json:"deletedWith,omitempty" yaml:"deletedWith,omitempty"` } // ManifestV1 captures meta-information about this checkpoint file, such as versions of binaries, etc. diff --git a/sdk/go/common/resource/resource_goal.go b/sdk/go/common/resource/resource_goal.go index 3f23889e65dc..84f29d4aefd0 100644 --- a/sdk/go/common/resource/resource_goal.go +++ b/sdk/go/common/resource/resource_goal.go @@ -40,6 +40,9 @@ type Goal struct { ReplaceOnChanges []string // a list of property paths that if changed should force a replacement. // if set to True, the providers Delete method will not be called for this resource. RetainOnDelete bool + // if set, the providers Delete method will not be called for this resource + // if specified resource is being deleted as well. + DeletedWith URN } // NewGoal allocates a new resource goal state. @@ -47,7 +50,7 @@ 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 []Alias, id ID, customTimeouts *CustomTimeouts, - replaceOnChanges []string, retainOnDelete bool) *Goal { + replaceOnChanges []string, retainOnDelete bool, deletedWith URN) *Goal { g := &Goal{ Type: t, @@ -67,6 +70,7 @@ func NewGoal(t tokens.Type, name tokens.QName, custom bool, props PropertyMap, ID: id, ReplaceOnChanges: replaceOnChanges, RetainOnDelete: retainOnDelete, + DeletedWith: deletedWith, } if customTimeouts != nil { diff --git a/sdk/go/common/resource/resource_state.go b/sdk/go/common/resource/resource_state.go index 612d579e915c..7d174d4f19a9 100644 --- a/sdk/go/common/resource/resource_state.go +++ b/sdk/go/common/resource/resource_state.go @@ -44,6 +44,7 @@ type State struct { CustomTimeouts CustomTimeouts // A config block that will be used to configure timeouts for CRUD operations. ImportID ID // the resource's import id, if this was an imported resource. RetainOnDelete bool // if set to True, the providers Delete method will not be called for this resource. + DeletedWith URN // If set, the providers Delete method will not be called for this resource if specified resource is being deleted as well. } func (s *State) GetAliasURNs() []URN { @@ -64,7 +65,7 @@ func NewState(t tokens.Type, urn URN, custom bool, del bool, id ID, external bool, dependencies []URN, initErrors []string, provider string, propertyDependencies map[PropertyKey][]URN, pendingReplacement bool, additionalSecretOutputs []PropertyKey, aliases []URN, timeouts *CustomTimeouts, - importID ID, retainOnDelete bool) *State { + importID ID, retainOnDelete bool, deletedWith URN) *State { contract.Assertf(t != "", "type was empty") contract.Assertf(custom || id == "", "is custom or had empty ID") @@ -90,6 +91,7 @@ func NewState(t tokens.Type, urn URN, custom bool, del bool, id ID, Aliases: aliases, ImportID: importID, RetainOnDelete: retainOnDelete, + DeletedWith: deletedWith, } if timeouts != nil { diff --git a/sdk/go/pulumi/context.go b/sdk/go/pulumi/context.go index 0ec0534d9ab8..73fcbd3717f7 100644 --- a/sdk/go/pulumi/context.go +++ b/sdk/go/pulumi/context.go @@ -51,8 +51,9 @@ type Context struct { engine pulumirpc.EngineClient engineConn *grpc.ClientConn - keepResources bool // true if resources should be marshaled as strongly-typed references. - keepOutputValues bool // true if outputs should be marshaled as strongly-type output values. + keepResources bool // true if resources should be marshaled as strongly-typed references. + keepOutputValues bool // true if outputs should be marshaled as strongly-type output values. + supportsDeletedWith bool // true if deletedWith supported by pulumi rpcs int // the number of outstanding RPC requests. rpcsDone *sync.Cond // an event signaling completion of RPCs. @@ -126,16 +127,22 @@ func NewContext(ctx context.Context, info RunInfo) (*Context, error) { return nil, err } + supportsDeletedWith, err := supportsFeature("deletedWith") + if err != nil { + return nil, err + } + context := &Context{ - ctx: ctx, - info: info, - exports: make(map[string]Input), - monitorConn: monitorConn, - monitor: monitor, - engineConn: engineConn, - engine: engine, - keepResources: keepResources, - keepOutputValues: keepOutputValues, + ctx: ctx, + info: info, + exports: make(map[string]Input), + monitorConn: monitorConn, + monitor: monitor, + engineConn: engineConn, + engine: engine, + keepResources: keepResources, + keepOutputValues: keepOutputValues, + supportsDeletedWith: supportsDeletedWith, } context.rpcsDone = sync.NewCond(&context.rpcsLock) context.Log = &logState{ @@ -596,6 +603,10 @@ func (ctx *Context) ReadResource( return err } + if options.DeletedWith != "" && !ctx.supportsDeletedWith { + return errors.New("the Pulumi CLI does not support the DeletedWith option. Please update the Pulumi CLI") + } + // Note that we're about to make an outstanding RPC request, so that we can rendezvous during shutdown. if err := ctx.beginRPC(); err != nil { return err @@ -856,6 +867,7 @@ func (ctx *Context) registerResource( Remote: remote, ReplaceOnChanges: inputs.replaceOnChanges, RetainOnDelete: inputs.retainOnDelete, + DeletedWith: inputs.deletedWith, }) if err != nil { logging.V(9).Infof("RegisterResource(%s, %s): error: %v", t, name, err) @@ -1249,6 +1261,7 @@ type resourceInputs struct { pluginDownloadURL string replaceOnChanges []string retainOnDelete bool + deletedWith string } // prepareResourceInputs prepares the inputs for a resource operation, shared between read and register. @@ -1336,6 +1349,7 @@ func (ctx *Context) prepareResourceInputs(res Resource, props Input, t string, o pluginDownloadURL: state.pluginDownloadURL, replaceOnChanges: resOpts.replaceOnChanges, retainOnDelete: opts.RetainOnDelete, + deletedWith: string(opts.DeletedWith), }, nil } diff --git a/sdk/go/pulumi/resource.go b/sdk/go/pulumi/resource.go index bc69910b25e3..90502eed7425 100644 --- a/sdk/go/pulumi/resource.go +++ b/sdk/go/pulumi/resource.go @@ -311,6 +311,9 @@ type resourceOptions struct { PluginDownloadURL string // If set to True, the providers Delete method will not be called for this resource. RetainOnDelete bool + // If set, the providers Delete method will not be called for this resource + // if specified resource is being deleted as well. + DeletedWith URN } type invokeOptions struct { @@ -590,3 +593,11 @@ func RetainOnDelete(b bool) ResourceOption { ro.RetainOnDelete = b }) } + +// If set, the providers Delete method will not be called for this resource +// if specified resource is being deleted as well. +func DeletedWith(dw URN) ResourceOption { + return resourceOption(func(ro *resourceOptions) { + ro.DeletedWith = dw + }) +} diff --git a/sdk/nodejs/proto/resource_pb.js b/sdk/nodejs/proto/resource_pb.js index e43605f6d2d8..e4b7e962f53c 100644 --- a/sdk/nodejs/proto/resource_pb.js +++ b/sdk/nodejs/proto/resource_pb.js @@ -1297,7 +1297,8 @@ proto.pulumirpc.RegisterResourceRequest.toObject = function(includeInstance, msg plugindownloadurl: jspb.Message.getFieldWithDefault(msg, 24, ""), retainondelete: jspb.Message.getBooleanFieldWithDefault(msg, 25, false), aliasesList: jspb.Message.toObjectList(msg.getAliasesList(), - pulumi_alias_pb.Alias.toObject, includeInstance) + pulumi_alias_pb.Alias.toObject, includeInstance), + deletedwith: jspb.Message.getFieldWithDefault(msg, 27, "") }; if (includeInstance) { @@ -1445,6 +1446,10 @@ proto.pulumirpc.RegisterResourceRequest.deserializeBinaryFromReader = function(m reader.readMessage(value,pulumi_alias_pb.Alias.deserializeBinaryFromReader); msg.addAliases(value); break; + case 27: + var value = /** @type {string} */ (reader.readString()); + msg.setDeletedwith(value); + break; default: reader.skipField(); break; @@ -1653,6 +1658,13 @@ proto.pulumirpc.RegisterResourceRequest.serializeBinaryToWriter = function(messa pulumi_alias_pb.Alias.serializeBinaryToWriter ); } + f = message.getDeletedwith(); + if (f.length > 0) { + writer.writeString( + 27, + f + ); + } }; @@ -2631,6 +2643,24 @@ proto.pulumirpc.RegisterResourceRequest.prototype.clearAliasesList = function() }; +/** + * optional string deletedWith = 27; + * @return {string} + */ +proto.pulumirpc.RegisterResourceRequest.prototype.getDeletedwith = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 27, "")); +}; + + +/** + * @param {string} value + * @return {!proto.pulumirpc.RegisterResourceRequest} returns this + */ +proto.pulumirpc.RegisterResourceRequest.prototype.setDeletedwith = function(value) { + return jspb.Message.setProto3StringField(this, 27, value); +}; + + /** * List of repeated fields within this message type. diff --git a/sdk/nodejs/resource.ts b/sdk/nodejs/resource.ts index 2534f2318e8e..26a3447e7839 100644 --- a/sdk/nodejs/resource.ts +++ b/sdk/nodejs/resource.ts @@ -599,6 +599,11 @@ export interface ResourceOptions { * If set to True, the providers Delete method will not be called for this resource. */ retainOnDelete?: boolean; + /** + * If set, the providers Delete method will not be called for this resource + * if specified is being deleted as well. + */ + deletedWith?: Resource; // !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies // that mergeOptions works properly for it. diff --git a/sdk/nodejs/runtime/resource.ts b/sdk/nodejs/runtime/resource.ts index b94d2700bb91..8c5711dc0297 100644 --- a/sdk/nodejs/runtime/resource.ts +++ b/sdk/nodejs/runtime/resource.ts @@ -33,6 +33,7 @@ import { URN, } from "../resource"; import { debuggablePromise } from "./debuggable"; +import { monitorSupportsDeletedWith } from "./settings"; import { invoke } from "./invoke"; import { @@ -317,6 +318,11 @@ export function registerResource(res: Resource, parent: Resource | undefined, t: req.setReplaceonchangesList(opts.replaceOnChanges || []); req.setPlugindownloadurl(opts.pluginDownloadURL || ""); req.setRetainondelete(opts.retainOnDelete || false); + req.setDeletedwith(opts.deletedWith); + + if (opts.deletedWith && !(await monitorSupportsDeletedWith())) { + throw new Error("The Pulumi CLI does not support the DeletedWith option. Please update the Pulumi CLI."); + } const customTimeouts = new resproto.RegisterResourceRequest.CustomTimeouts(); if (opts.customTimeouts != null) { diff --git a/sdk/nodejs/runtime/settings.ts b/sdk/nodejs/runtime/settings.ts index 34e54412f934..520fdd6b3e43 100644 --- a/sdk/nodejs/runtime/settings.ts +++ b/sdk/nodejs/runtime/settings.ts @@ -510,6 +510,14 @@ export async function monitorSupportsOutputValues(): Promise { return monitorSupportsFeature("outputValues"); } +/** + * monitorSupportsDeletedWith returns a promise that when resolved tells you if the resource monitor we are + * connected to is able to support the deletedWith resource option across its RPC interface. + */ +export async function monitorSupportsDeletedWith(): Promise { + return monitorSupportsFeature("deletedWith"); +} + // sxsRandomIdentifier is a module level global that is transfered to process.env. // the goal is to detect side by side (sxs) pulumi/pulumi situations for inline programs // and fail fast. See https://github.com/pulumi/pulumi/issues/7333 for details. diff --git a/sdk/proto/go/resource.pb.go b/sdk/proto/go/resource.pb.go index 537c36f59460..e43a7f02a655 100644 --- a/sdk/proto/go/resource.pb.go +++ b/sdk/proto/go/resource.pb.go @@ -360,6 +360,7 @@ type RegisterResourceRequest struct { PluginDownloadURL string `protobuf:"bytes,24,opt,name=pluginDownloadURL,proto3" json:"pluginDownloadURL,omitempty"` // the server URL of the provider to use when servicing this request. RetainOnDelete bool `protobuf:"varint,25,opt,name=retainOnDelete,proto3" json:"retainOnDelete,omitempty"` // if true the engine will not call the resource providers delete method for this resource. Aliases []*Alias `protobuf:"bytes,26,rep,name=aliases,proto3" json:"aliases,omitempty"` // a list of additional aliases that should be considered the same. + DeletedWith string `protobuf:"bytes,27,opt,name=deletedWith,proto3" json:"deletedWith,omitempty"` // if set the engine will not call the resource providers delete method for this resource when specified resource is deleted. } func (x *RegisterResourceRequest) Reset() { @@ -576,6 +577,13 @@ func (x *RegisterResourceRequest) GetAliases() []*Alias { return nil } +func (x *RegisterResourceRequest) GetDeletedWith() string { + if x != nil { + return x.DeletedWith + } + return "" +} + // RegisterResourceResponse is returned by the engine after a resource has finished being initialized. It includes the // auto-assigned URN, the provider-assigned ID, and any other properties initialized by the engine. type RegisterResourceResponse struct { @@ -1020,7 +1028,7 @@ var file_pulumi_resource_proto_rawDesc = []byte{ 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x22, 0xd3, 0x0b, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x22, 0xf5, 0x0b, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, @@ -1093,117 +1101,120 @@ var file_pulumi_resource_proto_rawDesc = []byte{ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x1a, 0x2a, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x72, 0x6e, 0x73, 0x1a, 0x58, 0x0a, - 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x1a, 0x80, 0x01, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc2, 0x03, 0x0a, 0x18, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x07, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x71, 0x0a, 0x14, 0x70, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, - 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x2a, 0x0a, - 0x14, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x72, 0x6e, 0x73, 0x1a, 0x81, 0x01, 0x0a, 0x19, 0x50, 0x72, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, + 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x57, + 0x69, 0x74, 0x68, 0x1a, 0x2a, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, + 0x72, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x72, 0x6e, 0x73, 0x1a, + 0x58, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x1a, 0x80, 0x01, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4e, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4d, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x65, 0x0a, - 0x1e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, - 0x6e, 0x12, 0x31, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x73, 0x22, 0xe4, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x74, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6f, 0x6b, - 0x12, 0x2b, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, - 0x11, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x55, - 0x52, 0x4c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, 0x32, 0xd4, 0x04, 0x0a, 0x0f, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, - 0x5a, 0x0a, 0x0f, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x12, 0x21, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x06, 0x49, - 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x20, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, - 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x6e, - 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x20, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, - 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x39, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x16, 0x2e, - 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x12, 0x1e, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x75, - 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x29, 0x2e, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, + 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc2, 0x03, 0x0a, 0x18, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x71, 0x0a, 0x14, + 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x70, 0x75, 0x6c, + 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, + 0x2a, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x72, 0x6e, 0x73, 0x1a, 0x81, 0x01, 0x0a, 0x19, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4e, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x70, 0x75, 0x6c, + 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x65, 0x0a, 0x1e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6e, 0x12, 0x31, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0xe4, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, + 0x6f, 0x6b, 0x12, 0x2b, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, + 0x64, 0x55, 0x52, 0x4c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, 0x32, 0xd4, 0x04, + 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, + 0x72, 0x12, 0x5a, 0x0a, 0x0f, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x46, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x12, 0x21, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, + 0x06, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x20, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x76, 0x6f, + 0x6b, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x75, 0x6c, 0x75, + 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x20, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x6b, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, + 0x69, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x39, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, + 0x16, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x1e, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x70, 0x75, 0x6c, 0x75, + 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x2f, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x2f, 0x73, - 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x3b, 0x70, - 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, + 0x29, 0x2e, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x2f, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, + 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, + 0x3b, 0x70, 0x75, 0x6c, 0x75, 0x6d, 0x69, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/sdk/python/lib/pulumi/resource.py b/sdk/python/lib/pulumi/resource.py index 33f79236996d..41beb37c926b 100644 --- a/sdk/python/lib/pulumi/resource.py +++ b/sdk/python/lib/pulumi/resource.py @@ -487,6 +487,12 @@ class ResourceOptions: If set to True, the providers Delete method will not be called for this resource. """ + deleted_with: Optional["Resource"] + """ + If set, the providers Delete method will not be called for this resource + if specified resource is being deleted as well. + """ + # pylint: disable=redefined-builtin def __init__( self, @@ -512,6 +518,7 @@ def __init__( replace_on_changes: Optional[List[str]] = None, plugin_download_url: Optional[str] = None, retain_on_delete: Optional[bool] = None, + deleted_with: Optional["Resource"] = None, ) -> None: """ :param Optional[Resource] parent: If provided, the currently-constructing resource should be the child of @@ -552,6 +559,8 @@ def __init__( from the provided url. This url overrides the plugin download url inferred from the current package and should rarely be used. :param Optional[bool] retain_on_delete: If set to True, the providers Delete method will not be called for this resource. + :param Optional[Resource] deleted_with: If set, the providers Delete method will not be called for this resource + if specified resource is being deleted as well. """ # Expose 'merge' again this this object, but this time as an instance method. @@ -577,6 +586,7 @@ def __init__( self.replace_on_changes = replace_on_changes self.depends_on = depends_on self.retain_on_delete = retain_on_delete + self.deleted_with = deleted_with # Proactively check that `depends_on` values are of type # `Resource`. We cannot complete the check in the general case @@ -704,6 +714,9 @@ def expand_providers(providers) -> Optional[Sequence["ProviderResource"]]: if source.retain_on_delete is None else source.retain_on_delete ) + dest.deleted_with = ( + dest.deleted_with if source.deleted_with is None else source.deleted_with + ) # Now, if we are left with a .providers that is just a single key/value pair, then # collapse that down into .provider form. diff --git a/sdk/python/lib/pulumi/runtime/proto/resource_pb2.py b/sdk/python/lib/pulumi/runtime/proto/resource_pb2.py index 0b07d2fddd4f..5d58a4f9202f 100644 --- a/sdk/python/lib/pulumi/runtime/proto/resource_pb2.py +++ b/sdk/python/lib/pulumi/runtime/proto/resource_pb2.py @@ -17,7 +17,7 @@ from . import alias_pb2 as pulumi_dot_alias__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15pulumi/resource.proto\x12\tpulumirpc\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x15pulumi/provider.proto\x1a\x12pulumi/alias.proto\"$\n\x16SupportsFeatureRequest\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x17SupportsFeatureResponse\x12\x12\n\nhasSupport\x18\x01 \x01(\x08\"\xae\x02\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\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x0c \x01(\x08\x12\x19\n\x11pluginDownloadURL\x18\r \x01(\tJ\x04\x08\x0b\x10\x0cR\x07\x61liases\"P\n\x14ReadResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12+\n\nproperties\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xb2\x08\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\x11\n\taliasURNs\x18\x0f \x03(\t\x12\x10\n\x08importId\x18\x10 \x01(\t\x12I\n\x0e\x63ustomTimeouts\x18\x11 \x01(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.CustomTimeouts\x12\"\n\x1a\x64\x65leteBeforeReplaceDefined\x18\x12 \x01(\x08\x12\x1d\n\x15supportsPartialValues\x18\x13 \x01(\x08\x12\x0e\n\x06remote\x18\x14 \x01(\x08\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x15 \x01(\x08\x12\x44\n\tproviders\x18\x16 \x03(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.ProvidersEntry\x12\x18\n\x10replaceOnChanges\x18\x17 \x03(\t\x12\x19\n\x11pluginDownloadURL\x18\x18 \x01(\t\x12\x16\n\x0eretainOnDelete\x18\x19 \x01(\x08\x12!\n\x07\x61liases\x18\x1a \x03(\x0b\x32\x10.pulumirpc.Alias\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1a@\n\x0e\x43ustomTimeouts\x12\x0e\n\x06\x63reate\x18\x01 \x01(\t\x12\x0e\n\x06update\x18\x02 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x03 \x01(\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\x1a\x30\n\x0eProvidersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf7\x02\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\x12[\n\x14propertyDependencies\x18\x06 \x03(\x0b\x32=.pulumirpc.RegisterResourceResponse.PropertyDependenciesEntry\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1au\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.pulumirpc.RegisterResourceResponse.PropertyDependencies:\x02\x38\x01\"W\n\x1eRegisterResourceOutputsRequest\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12(\n\x07outputs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xa2\x01\n\x15ResourceInvokeRequest\x12\x0b\n\x03tok\x18\x01 \x01(\t\x12%\n\x04\x61rgs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x10\n\x08provider\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x05 \x01(\x08\x12\x19\n\x11pluginDownloadURL\x18\x06 \x01(\t2\xd4\x04\n\x0fResourceMonitor\x12Z\n\x0fSupportsFeature\x12!.pulumirpc.SupportsFeatureRequest\x1a\".pulumirpc.SupportsFeatureResponse\"\x00\x12G\n\x06Invoke\x12 .pulumirpc.ResourceInvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x12O\n\x0cStreamInvoke\x12 .pulumirpc.ResourceInvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x30\x01\x12\x39\n\x04\x43\x61ll\x12\x16.pulumirpc.CallRequest\x1a\x17.pulumirpc.CallResponse\"\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\x42\x34Z2github.com/pulumi/pulumi/sdk/v3/proto/go;pulumirpcb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15pulumi/resource.proto\x12\tpulumirpc\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x15pulumi/provider.proto\x1a\x12pulumi/alias.proto\"$\n\x16SupportsFeatureRequest\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x17SupportsFeatureResponse\x12\x12\n\nhasSupport\x18\x01 \x01(\x08\"\xae\x02\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\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x0c \x01(\x08\x12\x19\n\x11pluginDownloadURL\x18\r \x01(\tJ\x04\x08\x0b\x10\x0cR\x07\x61liases\"P\n\x14ReadResourceResponse\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12+\n\nproperties\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xc7\x08\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\x11\n\taliasURNs\x18\x0f \x03(\t\x12\x10\n\x08importId\x18\x10 \x01(\t\x12I\n\x0e\x63ustomTimeouts\x18\x11 \x01(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.CustomTimeouts\x12\"\n\x1a\x64\x65leteBeforeReplaceDefined\x18\x12 \x01(\x08\x12\x1d\n\x15supportsPartialValues\x18\x13 \x01(\x08\x12\x0e\n\x06remote\x18\x14 \x01(\x08\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x15 \x01(\x08\x12\x44\n\tproviders\x18\x16 \x03(\x0b\x32\x31.pulumirpc.RegisterResourceRequest.ProvidersEntry\x12\x18\n\x10replaceOnChanges\x18\x17 \x03(\t\x12\x19\n\x11pluginDownloadURL\x18\x18 \x01(\t\x12\x16\n\x0eretainOnDelete\x18\x19 \x01(\x08\x12!\n\x07\x61liases\x18\x1a \x03(\x0b\x32\x10.pulumirpc.Alias\x12\x13\n\x0b\x64\x65letedWith\x18\x1b \x01(\t\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1a@\n\x0e\x43ustomTimeouts\x12\x0e\n\x06\x63reate\x18\x01 \x01(\t\x12\x0e\n\x06update\x18\x02 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x03 \x01(\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\x1a\x30\n\x0eProvidersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf7\x02\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\x12[\n\x14propertyDependencies\x18\x06 \x03(\x0b\x32=.pulumirpc.RegisterResourceResponse.PropertyDependenciesEntry\x1a$\n\x14PropertyDependencies\x12\x0c\n\x04urns\x18\x01 \x03(\t\x1au\n\x19PropertyDependenciesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12G\n\x05value\x18\x02 \x01(\x0b\x32\x38.pulumirpc.RegisterResourceResponse.PropertyDependencies:\x02\x38\x01\"W\n\x1eRegisterResourceOutputsRequest\x12\x0b\n\x03urn\x18\x01 \x01(\t\x12(\n\x07outputs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xa2\x01\n\x15ResourceInvokeRequest\x12\x0b\n\x03tok\x18\x01 \x01(\t\x12%\n\x04\x61rgs\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x10\n\x08provider\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65ptResources\x18\x05 \x01(\x08\x12\x19\n\x11pluginDownloadURL\x18\x06 \x01(\t2\xd4\x04\n\x0fResourceMonitor\x12Z\n\x0fSupportsFeature\x12!.pulumirpc.SupportsFeatureRequest\x1a\".pulumirpc.SupportsFeatureResponse\"\x00\x12G\n\x06Invoke\x12 .pulumirpc.ResourceInvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x12O\n\x0cStreamInvoke\x12 .pulumirpc.ResourceInvokeRequest\x1a\x19.pulumirpc.InvokeResponse\"\x00\x30\x01\x12\x39\n\x04\x43\x61ll\x12\x16.pulumirpc.CallRequest\x1a\x17.pulumirpc.CallResponse\"\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\x42\x34Z2github.com/pulumi/pulumi/sdk/v3/proto/go;pulumirpcb\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pulumi.resource_pb2', globals()) @@ -40,25 +40,25 @@ _READRESOURCERESPONSE._serialized_start=528 _READRESOURCERESPONSE._serialized_end=608 _REGISTERRESOURCEREQUEST._serialized_start=611 - _REGISTERRESOURCEREQUEST._serialized_end=1685 - _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIES._serialized_start=1415 - _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIES._serialized_end=1451 - _REGISTERRESOURCEREQUEST_CUSTOMTIMEOUTS._serialized_start=1453 - _REGISTERRESOURCEREQUEST_CUSTOMTIMEOUTS._serialized_end=1517 - _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY._serialized_start=1519 - _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY._serialized_end=1635 - _REGISTERRESOURCEREQUEST_PROVIDERSENTRY._serialized_start=1637 - _REGISTERRESOURCEREQUEST_PROVIDERSENTRY._serialized_end=1685 - _REGISTERRESOURCERESPONSE._serialized_start=1688 - _REGISTERRESOURCERESPONSE._serialized_end=2063 - _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIES._serialized_start=1415 - _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIES._serialized_end=1451 - _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIESENTRY._serialized_start=1946 - _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIESENTRY._serialized_end=2063 - _REGISTERRESOURCEOUTPUTSREQUEST._serialized_start=2065 - _REGISTERRESOURCEOUTPUTSREQUEST._serialized_end=2152 - _RESOURCEINVOKEREQUEST._serialized_start=2155 - _RESOURCEINVOKEREQUEST._serialized_end=2317 - _RESOURCEMONITOR._serialized_start=2320 - _RESOURCEMONITOR._serialized_end=2916 + _REGISTERRESOURCEREQUEST._serialized_end=1706 + _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIES._serialized_start=1436 + _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIES._serialized_end=1472 + _REGISTERRESOURCEREQUEST_CUSTOMTIMEOUTS._serialized_start=1474 + _REGISTERRESOURCEREQUEST_CUSTOMTIMEOUTS._serialized_end=1538 + _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY._serialized_start=1540 + _REGISTERRESOURCEREQUEST_PROPERTYDEPENDENCIESENTRY._serialized_end=1656 + _REGISTERRESOURCEREQUEST_PROVIDERSENTRY._serialized_start=1658 + _REGISTERRESOURCEREQUEST_PROVIDERSENTRY._serialized_end=1706 + _REGISTERRESOURCERESPONSE._serialized_start=1709 + _REGISTERRESOURCERESPONSE._serialized_end=2084 + _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIES._serialized_start=1436 + _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIES._serialized_end=1472 + _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIESENTRY._serialized_start=1967 + _REGISTERRESOURCERESPONSE_PROPERTYDEPENDENCIESENTRY._serialized_end=2084 + _REGISTERRESOURCEOUTPUTSREQUEST._serialized_start=2086 + _REGISTERRESOURCEOUTPUTSREQUEST._serialized_end=2173 + _RESOURCEINVOKEREQUEST._serialized_start=2176 + _RESOURCEINVOKEREQUEST._serialized_end=2338 + _RESOURCEMONITOR._serialized_start=2341 + _RESOURCEMONITOR._serialized_end=2937 # @@protoc_insertion_point(module_scope) diff --git a/sdk/python/lib/pulumi/runtime/resource.py b/sdk/python/lib/pulumi/runtime/resource.py index a9a310e0e966..75b3845ad29c 100644 --- a/sdk/python/lib/pulumi/runtime/resource.py +++ b/sdk/python/lib/pulumi/runtime/resource.py @@ -553,6 +553,11 @@ async def do_register(): "Expected custom_timeouts to be a CustomTimeouts object" ) + if opts.deleted_with and not await settings.monitor_supports_deleted_with(): + raise Exception( + "The Pulumi CLI does not support the DeletedWith option. Please update the Pulumi CLI." + ) + accept_resources = not ( os.getenv("PULUMI_DISABLE_RESOURCE_REFERENCES", "").upper() in {"TRUE", "1"} @@ -584,6 +589,7 @@ async def do_register(): remote=remote, replaceOnChanges=replace_on_changes, retainOnDelete=opts.retain_on_delete or False, + deletedWith=opts.deleted_with, ) from ..resource import create_urn # pylint: disable=import-outside-toplevel diff --git a/sdk/python/lib/pulumi/runtime/settings.py b/sdk/python/lib/pulumi/runtime/settings.py index efdb2ade788e..d264daec6cbe 100644 --- a/sdk/python/lib/pulumi/runtime/settings.py +++ b/sdk/python/lib/pulumi/runtime/settings.py @@ -285,6 +285,10 @@ async def monitor_supports_output_values() -> bool: return await monitor_supports_feature("outputValues") +async def monitor_supports_deleted_with() -> bool: + return await monitor_supports_feature("deletedWith") + + def reset_options( project: Optional[str] = None, stack: Optional[str] = None,