Send old inputs to Delete ()

<!--- 
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->

# Description

<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->

Fixes https://github.com/pulumi/pulumi/issues/14115.

This was missed as part of https://github.com/pulumi/pulumi/pull/13139.

Adds a new configure flag (sends_old_inputs_to_delete) which the engine
will now always set to true. If that's set providers can rely on the old
inputs being sent to delete, otherwise they'll get nil.

## Checklist

- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
  - [ ] I have formatted my code using `gofumpt`

<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
This commit is contained in:
Fraser Waters 2023-10-13 15:12:26 +01:00 committed by GitHub
parent 68af26af35
commit ca12edfb4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 854 additions and 973 deletions

View File

@ -0,0 +1,4 @@
changes:
- type: feat
scope: engine
description: Old inputs are sent to provider Delete functions, as well as the old outputs.

View File

@ -27,7 +27,9 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
)
type badProvider struct{}
type badProvider struct {
plugin.UnimplementedProvider
}
var _ plugin.Provider = (*badProvider)(nil)
@ -97,78 +99,10 @@ func (p *badProvider) CheckConfig(urn resource.URN, oldInputs, newInputs resourc
return newInputs, nil, nil
}
func (p *badProvider) DiffConfig(urn resource.URN, oldInputs, oldOutputs, newInputs resource.PropertyMap,
allowUnknowns bool, ignoreChanges []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, fmt.Errorf("DiffConfig not implemented")
}
func (p *badProvider) Configure(inputs resource.PropertyMap) error {
return nil
}
func (p *badProvider) Check(urn resource.URN, oldInputs, newInputs resource.PropertyMap,
allowUnknowns bool, randomSeed []byte,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, fmt.Errorf("Check not implemented")
}
func (p *badProvider) Diff(urn resource.URN, id resource.ID, oldInputs, oldOutputs, newInputs resource.PropertyMap,
allowUnknowns bool, ignoreChanges []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, fmt.Errorf("Diff not implemented")
}
func (p *badProvider) Create(
urn resource.URN, news resource.PropertyMap,
timeout float64, preview bool,
) (resource.ID, resource.PropertyMap, resource.Status, error) {
return "", nil, resource.StatusOK, fmt.Errorf("Create not implemented")
}
func (p *badProvider) Read(urn resource.URN, id resource.ID,
inputs, state resource.PropertyMap,
) (plugin.ReadResult, resource.Status, error) {
return plugin.ReadResult{}, resource.StatusOK, fmt.Errorf("Read not implemented")
}
func (p *badProvider) Update(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, timeout float64,
ignoreChanges []string, preview bool,
) (resource.PropertyMap, resource.Status, error) {
return nil, resource.StatusOK, fmt.Errorf("Update not implemented")
}
func (p *badProvider) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
return resource.StatusOK, fmt.Errorf("Delete not implemented")
}
func (p *badProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
inputs resource.PropertyMap, options plugin.ConstructOptions,
) (plugin.ConstructResult, error) {
return plugin.ConstructResult{}, fmt.Errorf("Construct not implemented")
}
func (p *badProvider) Invoke(tok tokens.ModuleMember,
args resource.PropertyMap,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, fmt.Errorf("Invoke not implemented")
}
func (p *badProvider) StreamInvoke(tok tokens.ModuleMember, args resource.PropertyMap,
onNext func(resource.PropertyMap) error,
) ([]plugin.CheckFailure, error) {
return nil, fmt.Errorf("StreamInvoke not implemented")
}
func (p *badProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
options plugin.CallOptions,
) (plugin.CallResult, error) {
return plugin.CallResult{}, fmt.Errorf("Call not implemented")
}
func (p *badProvider) GetPluginInfo() (workspace.PluginInfo, error) {
ver := semver.MustParse("1.0.0")
return workspace.PluginInfo{

View File

@ -27,7 +27,9 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
)
type simpleProvider struct{}
type simpleProvider struct {
plugin.UnimplementedProvider
}
var _ plugin.Provider = (*simpleProvider)(nil)
@ -35,6 +37,10 @@ func (p *simpleProvider) Close() error {
return nil
}
func (p *simpleProvider) Configure(inputs resource.PropertyMap) error {
return nil
}
func (p *simpleProvider) Pkg() tokens.Package {
return "simple"
}
@ -102,17 +108,6 @@ func (p *simpleProvider) CheckConfig(urn resource.URN, oldInputs, newInputs reso
return newInputs, nil, nil
}
func (p *simpleProvider) DiffConfig(urn resource.URN, oldInputs, oldOutputs, newInputs resource.PropertyMap,
allowUnknowns bool, ignoreChanges []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, fmt.Errorf("DiffConfig not implemented")
}
func (p *simpleProvider) Configure(inputs resource.PropertyMap) error {
// Nothing to configure
return nil
}
func (p *simpleProvider) Check(urn resource.URN, oldInputs, newInputs resource.PropertyMap,
allowUnknowns bool, randomSeed []byte,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
@ -136,12 +131,6 @@ func (p *simpleProvider) Check(urn resource.URN, oldInputs, newInputs resource.P
return newInputs, nil, nil
}
func (p *simpleProvider) Diff(urn resource.URN, id resource.ID, oldInputs, oldOutputs, newInputs resource.PropertyMap,
allowUnknowns bool, ignoreChanges []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, fmt.Errorf("Diff not implemented")
}
func (p *simpleProvider) Create(
urn resource.URN, news resource.PropertyMap,
timeout float64, preview bool,
@ -159,49 +148,6 @@ func (p *simpleProvider) Create(
return resource.ID(id), news, resource.StatusOK, nil
}
func (p *simpleProvider) Read(urn resource.URN, id resource.ID,
inputs, state resource.PropertyMap,
) (plugin.ReadResult, resource.Status, error) {
return plugin.ReadResult{}, resource.StatusOK, fmt.Errorf("Read not implemented")
}
func (p *simpleProvider) Update(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, timeout float64,
ignoreChanges []string, preview bool,
) (resource.PropertyMap, resource.Status, error) {
return nil, resource.StatusOK, fmt.Errorf("Update not implemented")
}
func (p *simpleProvider) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
return resource.StatusOK, fmt.Errorf("Delete not implemented")
}
func (p *simpleProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
inputs resource.PropertyMap, options plugin.ConstructOptions,
) (plugin.ConstructResult, error) {
return plugin.ConstructResult{}, fmt.Errorf("Construct not implemented")
}
func (p *simpleProvider) Invoke(tok tokens.ModuleMember,
args resource.PropertyMap,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, fmt.Errorf("Invoke not implemented")
}
func (p *simpleProvider) StreamInvoke(tok tokens.ModuleMember, args resource.PropertyMap,
onNext func(resource.PropertyMap) error,
) ([]plugin.CheckFailure, error) {
return nil, fmt.Errorf("StreamInvoke not implemented")
}
func (p *simpleProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
options plugin.CallOptions,
) (plugin.CallResult, error) {
return plugin.CallResult{}, fmt.Errorf("Call not implemented")
}
func (p *simpleProvider) GetPluginInfo() (workspace.PluginInfo, error) {
ver := semver.MustParse("1.0.0")
return workspace.PluginInfo{

View File

@ -16,14 +16,12 @@ package convert
import (
"context"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/blang/semver"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
@ -38,109 +36,16 @@ func (ws *testWorkspace) GetPlugins() ([]workspace.PluginInfo, error) {
}
type testProvider struct {
plugin.UnimplementedProvider
pkg tokens.Package
mapping func(key, provider string) ([]byte, string, error)
mappings func(key string) ([]string, error)
}
func (prov *testProvider) SignalCancellation() error {
return nil
}
func (prov *testProvider) Close() error {
return nil
}
func (prov *testProvider) Pkg() tokens.Package {
return prov.pkg
}
func (prov *testProvider) GetSchema(version int) ([]byte, error) {
return nil, errors.New("unsupported")
}
func (prov *testProvider) CheckConfig(urn resource.URN, olds,
news resource.PropertyMap, allowUnknowns bool,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, errors.New("unsupported")
}
func (prov *testProvider) DiffConfig(urn resource.URN, oldInputs, oldOutputs, newInputs resource.PropertyMap,
allowUnknowns bool, ignoreChanges []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, errors.New("unsupported")
}
func (prov *testProvider) Configure(inputs resource.PropertyMap) error {
return nil
}
func (prov *testProvider) Check(urn resource.URN,
olds, news resource.PropertyMap, _ bool, _ []byte,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, errors.New("unsupported")
}
func (prov *testProvider) Create(urn resource.URN, props resource.PropertyMap, timeout float64,
preview bool,
) (resource.ID, resource.PropertyMap, resource.Status, error) {
return "", nil, resource.StatusOK, errors.New("unsupported")
}
func (prov *testProvider) Read(urn resource.URN, id resource.ID,
inputs, state resource.PropertyMap,
) (plugin.ReadResult, resource.Status, error) {
return plugin.ReadResult{}, resource.StatusUnknown, errors.New("unsupported")
}
func (prov *testProvider) Diff(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, _ bool, _ []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, errors.New("unsupported")
}
func (prov *testProvider) Update(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, timeout float64,
ignoreChanges []string, preview bool,
) (resource.PropertyMap, resource.Status, error) {
return nil, resource.StatusOK, errors.New("unsupported")
}
func (prov *testProvider) Delete(urn resource.URN,
id resource.ID, props resource.PropertyMap, timeout float64,
) (resource.Status, error) {
return resource.StatusOK, errors.New("unsupported")
}
func (prov *testProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
inputs resource.PropertyMap, options plugin.ConstructOptions,
) (plugin.ConstructResult, error) {
return plugin.ConstructResult{}, errors.New("unsupported")
}
func (prov *testProvider) Invoke(tok tokens.ModuleMember,
args resource.PropertyMap,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, errors.New("unsupported")
}
func (prov *testProvider) StreamInvoke(
tok tokens.ModuleMember, args resource.PropertyMap,
onNext func(resource.PropertyMap) error,
) ([]plugin.CheckFailure, error) {
return nil, fmt.Errorf("not implemented")
}
func (prov *testProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
options plugin.CallOptions,
) (plugin.CallResult, error) {
return plugin.CallResult{}, errors.New("unsupported")
}
func (prov *testProvider) GetPluginInfo() (workspace.PluginInfo, error) {
return workspace.PluginInfo{}, errors.New("unsupported")
}
func (prov *testProvider) GetMapping(key, provider string) ([]byte, string, error) {
return prov.mapping(key, provider)
}

View File

@ -1334,7 +1334,7 @@ func TestComponentToCustomUpdate(t *testing.T) {
return id, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN,
id resource.ID, olds resource.PropertyMap, timeout float64,
id resource.ID, oldInputs, oldOutputs resource.PropertyMap, timeout float64,
) (resource.Status, error) {
return resource.StatusOK, nil
},
@ -1689,7 +1689,7 @@ func TestFailDeleteDuplicateAliases(t *testing.T) {
return "created-id", news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
DeleteF: func(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
// We should only delete things in the last pass

View File

@ -392,7 +392,7 @@ func (p *configurableProvider) create(urn resource.URN, inputs resource.Property
return id, inputs, resource.StatusOK, nil
}
func (p *configurableProvider) delete(urn resource.URN, id resource.ID, olds resource.PropertyMap,
func (p *configurableProvider) delete(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
p.deletes.Store(id, p.id)

View File

@ -2791,7 +2791,7 @@ func TestProtect(t *testing.T) {
idCounter = idCounter + 1
return resourceID, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
DeleteF: func(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
deleteCounter = deleteCounter + 1
@ -2947,7 +2947,7 @@ func TestRetainOnDelete(t *testing.T) {
idCounter = idCounter + 1
return resourceID, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
DeleteF: func(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
assert.Fail(t, "Delete was called")
@ -3039,7 +3039,7 @@ func TestDeletedWith(t *testing.T) {
idCounter = idCounter + 1
return resourceID, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
DeleteF: func(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
if urn != topURN {
@ -3144,7 +3144,7 @@ func TestDeletedWithCircularDependency(t *testing.T) {
idCounter = idCounter + 1
return resourceID, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
DeleteF: func(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
assert.Fail(t, "Delete was called")
@ -3521,7 +3521,7 @@ func TestPendingDeleteOrder(t *testing.T) {
return id, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN,
id resource.ID, olds resource.PropertyMap, timeout float64,
id resource.ID, oldInputs, oldOutputs resource.PropertyMap, timeout float64,
) (resource.Status, error) {
// Fail if anything in cloud state still points to us
for other, res := range cloudState {
@ -3665,7 +3665,7 @@ func TestPendingDeleteReplacement(t *testing.T) {
return id, news, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN,
id resource.ID, olds resource.PropertyMap, timeout float64,
id resource.ID, oldInputs, oldOutputs resource.PropertyMap, timeout float64,
) (resource.Status, error) {
// Fail if anything in cloud state still points to us
for _, res := range cloudState {
@ -3923,7 +3923,7 @@ func TestTimestampTracking(t *testing.T) {
func TestOldCheckedInputsAreSent(t *testing.T) {
// Test for https://github.com/pulumi/pulumi/issues/5973, check that the old inputs from Check are passed
// to Diff and Update.
// to Diff, Update, and Delete.
t.Parallel()
firstUpdate := true
@ -4037,6 +4037,23 @@ func TestOldCheckedInputsAreSent(t *testing.T) {
return results, resource.StatusOK, nil
},
DeleteF: func(urn resource.URN, id resource.ID,
oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
// Check that the old inputs and outputs are passed to UpdateF
assert.Equal(t, resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": "baz",
"default": "default",
}), oldInputs)
assert.Equal(t, resource.NewPropertyMapFromMap(map[string]interface{}{
"foo": "baz",
"default": "default",
"computed": "computed",
}), oldOutputs)
return resource.StatusOK, nil
},
}, nil
}, deploytest.WithoutGrpc),
}
@ -4096,6 +4113,12 @@ func TestOldCheckedInputsAreSent(t *testing.T) {
"default": "default",
"computed": "computed",
}), resA.Outputs)
// Now run a destroy to delete the resource and check the stored inputs and outputs are sent
snap, err = TestOp(Destroy).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
assert.NoError(t, err)
assert.NotNil(t, snap)
assert.Len(t, snap.Resources, 0)
}
func TestResourceNames(t *testing.T) {

View File

@ -215,7 +215,7 @@ func TestUnplannedDelete(t *testing.T) {
DeleteF: func(
urn resource.URN,
id resource.ID,
olds resource.PropertyMap,
oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
return resource.StatusOK, nil
@ -292,7 +292,7 @@ func TestExpectedDelete(t *testing.T) {
DeleteF: func(
urn resource.URN,
id resource.ID,
olds resource.PropertyMap,
oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
return resource.StatusOK, nil
@ -561,7 +561,7 @@ func TestExpectedUnneededDelete(t *testing.T) {
DeleteF: func(
urn resource.URN,
id resource.ID,
olds resource.PropertyMap,
oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
return resource.StatusOK, nil

View File

@ -148,7 +148,7 @@ func (p *builtinProvider) Update(urn resource.URN, id resource.ID,
}
func (p *builtinProvider) Delete(urn resource.URN, id resource.ID,
state resource.PropertyMap, timeout float64,
oldInputs, oldOutputs resource.PropertyMap, timeout float64,
) (resource.Status, error) {
contract.Assertf(urn.Type() == stackReferenceType, "expected resource type %v, got %v", stackReferenceType, urn.Type())

View File

@ -52,8 +52,9 @@ type Provider struct {
preview bool) (resource.ID, resource.PropertyMap, resource.Status, error)
UpdateF func(urn resource.URN, id resource.ID, oldInputs, oldOutputs, newInputs resource.PropertyMap, timeout float64,
ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error)
DeleteF func(urn resource.URN, id resource.ID, olds resource.PropertyMap, timeout float64) (resource.Status, error)
ReadF func(urn resource.URN, id resource.ID,
DeleteF func(urn resource.URN, id resource.ID,
oldInputs, oldOutputs resource.PropertyMap, timeout float64) (resource.Status, error)
ReadF func(urn resource.URN, id resource.ID,
inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error)
ConstructF func(monitor *ResourceMonitor, typ, name string, parent resource.URN, inputs resource.PropertyMap,
@ -172,12 +173,12 @@ func (prov *Provider) Update(urn resource.URN, id resource.ID, oldInputs, oldOut
}
func (prov *Provider) Delete(urn resource.URN,
id resource.ID, props resource.PropertyMap, timeout float64,
id resource.ID, oldInputs, oldOutputs resource.PropertyMap, timeout float64,
) (resource.Status, error) {
if prov.DeleteF == nil {
return resource.StatusOK, nil
}
return prov.DeleteF(urn, id, props, timeout)
return prov.DeleteF(urn, id, oldInputs, oldOutputs, timeout)
}
func (prov *Provider) Read(urn resource.URN, id resource.ID,

View File

@ -549,7 +549,7 @@ func (r *Registry) Update(urn resource.URN, id resource.ID,
// Delete unregisters and unloads the provider with the given URN and ID. If the provider was never loaded
// this is a no-op.
func (r *Registry) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap,
func (r *Registry) Delete(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
contract.Assertf(!r.isPreview, "Delete must not be called during preview")

View File

@ -109,6 +109,8 @@ func (host *testPluginHost) GetRequiredPlugins(info plugin.ProgInfo,
}
type testProvider struct {
plugin.UnimplementedProvider
pkg tokens.Package
version semver.Version
configured bool
@ -118,14 +120,6 @@ type testProvider struct {
config func(resource.PropertyMap) error
}
func (prov *testProvider) SignalCancellation() error {
return nil
}
func (prov *testProvider) Close() error {
return nil
}
func (prov *testProvider) Pkg() tokens.Package {
return prov.pkg
}
@ -154,68 +148,6 @@ func (prov *testProvider) Configure(inputs resource.PropertyMap) error {
return nil
}
func (prov *testProvider) Check(urn resource.URN,
olds, news resource.PropertyMap, _ bool, _ []byte,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, errors.New("unsupported")
}
func (prov *testProvider) Create(urn resource.URN, props resource.PropertyMap, timeout float64,
preview bool,
) (resource.ID, resource.PropertyMap, resource.Status, error) {
return "", nil, resource.StatusOK, errors.New("unsupported")
}
func (prov *testProvider) Read(urn resource.URN, id resource.ID,
inputs, state resource.PropertyMap,
) (plugin.ReadResult, resource.Status, error) {
return plugin.ReadResult{}, resource.StatusUnknown, errors.New("unsupported")
}
func (prov *testProvider) Diff(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, _ bool, _ []string,
) (plugin.DiffResult, error) {
return plugin.DiffResult{}, errors.New("unsupported")
}
func (prov *testProvider) Update(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, timeout float64,
ignoreChanges []string, preview bool,
) (resource.PropertyMap, resource.Status, error) {
return nil, resource.StatusOK, errors.New("unsupported")
}
func (prov *testProvider) Delete(urn resource.URN,
id resource.ID, props resource.PropertyMap, timeout float64,
) (resource.Status, error) {
return resource.StatusOK, errors.New("unsupported")
}
func (prov *testProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
inputs resource.PropertyMap, options plugin.ConstructOptions,
) (plugin.ConstructResult, error) {
return plugin.ConstructResult{}, errors.New("unsupported")
}
func (prov *testProvider) Invoke(tok tokens.ModuleMember,
args resource.PropertyMap,
) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, errors.New("unsupported")
}
func (prov *testProvider) StreamInvoke(
tok tokens.ModuleMember, args resource.PropertyMap,
onNext func(resource.PropertyMap) error,
) ([]plugin.CheckFailure, error) {
return nil, fmt.Errorf("not implemented")
}
func (prov *testProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
options plugin.CallOptions,
) (plugin.CallResult, error) {
return plugin.CallResult{}, errors.New("unsupported")
}
func (prov *testProvider) GetPluginInfo() (workspace.PluginInfo, error) {
return workspace.PluginInfo{
Name: "testProvider",
@ -519,7 +451,7 @@ func TestCRUD(t *testing.T) {
assert.True(t, ok)
// Delete
status, err := r.Delete(urn, id, resource.PropertyMap{}, timeout)
status, err := r.Delete(urn, id, resource.PropertyMap{}, resource.PropertyMap{}, timeout)
assert.NoError(t, err)
assert.Equal(t, resource.StatusOK, status)

View File

@ -415,7 +415,7 @@ func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, err
return resource.StatusOK, nil, err
}
if rst, err := prov.Delete(s.URN(), s.old.ID, s.old.Outputs, s.old.CustomTimeouts.Delete); err != nil {
if rst, err := prov.Delete(s.URN(), s.old.ID, s.old.Inputs, s.old.Outputs, s.old.CustomTimeouts.Delete); err != nil {
return rst, nil, err
}
}

View File

@ -17,7 +17,7 @@
3421371250 793 proto/pulumi/errors.proto
10996825 8394 proto/pulumi/language.proto
2893249402 1992 proto/pulumi/plugin.proto
194783772 24349 proto/pulumi/provider.proto
2539158637 24561 proto/pulumi/provider.proto
1320626516 12214 proto/pulumi/resource.proto
607478140 1008 proto/pulumi/source.proto
2565199107 2157 proto/pulumi/testing/language.proto

View File

@ -104,6 +104,7 @@ message ConfigureRequest {
bool acceptSecrets = 3; // when true, operations should return secrets as strongly typed.
bool acceptResources = 4; // when true, operations should return resources as strongly typed values to the provider.
bool sends_old_inputs = 5; // when true, diff and update will be called with the old outputs and the old inputs.
bool sends_old_inputs_to_delete = 6; // when true, delete will be called with the old outputs and the old inputs.
}
message ConfigureResponse {
@ -317,6 +318,7 @@ message DeleteRequest {
string urn = 2; // the Pulumi URN for this resource.
google.protobuf.Struct properties = 3; // the current properties on the resource.
double timeout = 4; // the delete request timeout represented in seconds.
google.protobuf.Struct old_inputs = 5; // the old input values of the resource to delete.
}
message ConstructRequest {

View File

@ -73,8 +73,9 @@ type Provider interface {
Update(urn resource.URN, id resource.ID,
oldInputs, oldOutputs, newInputs resource.PropertyMap, timeout float64,
ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error)
// Delete tears down an existing resource.
Delete(urn resource.URN, id resource.ID, props resource.PropertyMap, timeout float64) (resource.Status, error)
// Delete tears down an existing resource. The inputs and outputs are the last recorded ones from state.
Delete(urn resource.URN, id resource.ID,
inputs, outputs resource.PropertyMap, timeout float64) (resource.Status, error)
// Construct creates a new component resource.
Construct(info ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN, inputs resource.PropertyMap,

View File

@ -736,11 +736,12 @@ func (p *provider) Configure(inputs resource.PropertyMap) error {
// want to make forward progress, even as the configure call is happening.
go func() {
resp, err := p.clientRaw.Configure(p.requestContext(), &pulumirpc.ConfigureRequest{
AcceptSecrets: true,
AcceptResources: true,
SendsOldInputs: true,
Variables: config,
Args: minputs,
AcceptSecrets: true,
AcceptResources: true,
SendsOldInputs: true,
SendsOldInputsToDelete: true,
Variables: config,
Args: minputs,
})
if err != nil {
rpcError := rpcerror.Convert(err)
@ -1309,14 +1310,14 @@ func (p *provider) Update(urn resource.URN, id resource.ID,
}
// Delete tears down an existing resource.
func (p *provider) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap,
func (p *provider) Delete(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap,
timeout float64,
) (resource.Status, error) {
contract.Assertf(urn != "", "Delete requires a URN")
contract.Assertf(id != "", "Delete requires an ID")
label := fmt.Sprintf("%s.Delete(%s,%s)", p.label(), urn, id)
logging.V(7).Infof("%s executing (#props=%d)", label, len(props))
logging.V(7).Infof("%s executing (#inputs=%d, #outputs=%d)", label, len(oldInputs), len(oldOutputs))
// Ensure that the plugin is configured.
client := p.clientRaw
@ -1328,7 +1329,17 @@ func (p *provider) Delete(urn resource.URN, id resource.ID, props resource.Prope
// We should never call delete at preview time, so we should never see unknowns here
contract.Assertf(pcfg.known, "Delete cannot be called if the configuration is unknown")
mprops, err := MarshalProperties(props, MarshalOptions{
minputs, err := MarshalProperties(oldInputs, MarshalOptions{
Label: label,
ElideAssetContents: true,
KeepSecrets: pcfg.acceptSecrets,
KeepResources: pcfg.acceptResources,
})
if err != nil {
return resource.StatusOK, err
}
moutputs, err := MarshalProperties(oldOutputs, MarshalOptions{
Label: label,
ElideAssetContents: true,
KeepSecrets: pcfg.acceptSecrets,
@ -1344,8 +1355,9 @@ func (p *provider) Delete(urn resource.URN, id resource.ID, props resource.Prope
if _, err := client.Delete(p.requestContext(), &pulumirpc.DeleteRequest{
Id: string(id),
Urn: string(urn),
Properties: mprops,
Properties: moutputs,
Timeout: timeout,
OldInputs: minputs,
}); err != nil {
resourceStatus, rpcErr := resourceStateAndError(err)
logging.V(7).Infof("%s failed: %v", label, rpcErr)

View File

@ -658,6 +658,7 @@ func TestProvider_ConfigureDeleteRace(t *testing.T) {
resource.NewURN("org/proj/dev", "foo", "", "bar:baz", "qux"),
"whatever",
props,
props,
1000,
)
assert.NoError(t, err, "Delete failed")

View File

@ -411,12 +411,17 @@ func (p *providerServer) Update(ctx context.Context, req *pulumirpc.UpdateReques
func (p *providerServer) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) {
urn, id := resource.URN(req.GetUrn()), resource.ID(req.GetId())
state, err := UnmarshalProperties(req.GetProperties(), p.unmarshalOptions("state"))
inputs, err := UnmarshalProperties(req.GetOldInputs(), p.unmarshalOptions("inputs"))
if err != nil {
return nil, err
}
if _, err = p.provider.Delete(urn, id, state, req.GetTimeout()); err != nil {
outputs, err := UnmarshalProperties(req.GetProperties(), p.unmarshalOptions("outputs"))
if err != nil {
return nil, err
}
if _, err = p.provider.Delete(urn, id, inputs, outputs, req.GetTimeout()); err != nil {
return nil, err
}

View File

@ -74,7 +74,7 @@ func (p *UnimplementedProvider) Update(urn resource.URN, id resource.ID, oldInpu
return resource.PropertyMap{}, resource.StatusUnknown, status.Error(codes.Unimplemented, "Update is not yet implemented")
}
func (p *UnimplementedProvider) Delete(urn resource.URN, id resource.ID, props resource.PropertyMap, timeout float64) (resource.Status, error) {
func (p *UnimplementedProvider) Delete(urn resource.URN, id resource.ID, oldInputs, oldOutputs resource.PropertyMap, timeout float64) (resource.Status, error) {
return resource.StatusUnknown, status.Error(codes.Unimplemented, "Delete is not yet implemented")
}

View File

@ -1091,7 +1091,8 @@ proto.pulumirpc.ConfigureRequest.toObject = function(includeInstance, msg) {
args: (f = msg.getArgs()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f),
acceptsecrets: jspb.Message.getBooleanFieldWithDefault(msg, 3, false),
acceptresources: jspb.Message.getBooleanFieldWithDefault(msg, 4, false),
sendsOldInputs: jspb.Message.getBooleanFieldWithDefault(msg, 5, false)
sendsOldInputs: jspb.Message.getBooleanFieldWithDefault(msg, 5, false),
sendsOldInputsToDelete: jspb.Message.getBooleanFieldWithDefault(msg, 6, false)
};
if (includeInstance) {
@ -1151,6 +1152,10 @@ proto.pulumirpc.ConfigureRequest.deserializeBinaryFromReader = function(msg, rea
var value = /** @type {boolean} */ (reader.readBool());
msg.setSendsOldInputs(value);
break;
case 6:
var value = /** @type {boolean} */ (reader.readBool());
msg.setSendsOldInputsToDelete(value);
break;
default:
reader.skipField();
break;
@ -1213,6 +1218,13 @@ proto.pulumirpc.ConfigureRequest.serializeBinaryToWriter = function(message, wri
f
);
}
f = message.getSendsOldInputsToDelete();
if (f) {
writer.writeBool(
6,
f
);
}
};
@ -1329,6 +1341,24 @@ proto.pulumirpc.ConfigureRequest.prototype.setSendsOldInputs = function(value) {
};
/**
* optional bool sends_old_inputs_to_delete = 6;
* @return {boolean}
*/
proto.pulumirpc.ConfigureRequest.prototype.getSendsOldInputsToDelete = function() {
return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 6, false));
};
/**
* @param {boolean} value
* @return {!proto.pulumirpc.ConfigureRequest} returns this
*/
proto.pulumirpc.ConfigureRequest.prototype.setSendsOldInputsToDelete = function(value) {
return jspb.Message.setProto3BooleanField(this, 6, value);
};
@ -6589,7 +6619,8 @@ proto.pulumirpc.DeleteRequest.toObject = function(includeInstance, msg) {
id: jspb.Message.getFieldWithDefault(msg, 1, ""),
urn: jspb.Message.getFieldWithDefault(msg, 2, ""),
properties: (f = msg.getProperties()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f),
timeout: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0)
timeout: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0),
oldInputs: (f = msg.getOldInputs()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f)
};
if (includeInstance) {
@ -6643,6 +6674,11 @@ proto.pulumirpc.DeleteRequest.deserializeBinaryFromReader = function(msg, reader
var value = /** @type {number} */ (reader.readDouble());
msg.setTimeout(value);
break;
case 5:
var value = new google_protobuf_struct_pb.Struct;
reader.readMessage(value,google_protobuf_struct_pb.Struct.deserializeBinaryFromReader);
msg.setOldInputs(value);
break;
default:
reader.skipField();
break;
@ -6701,6 +6737,14 @@ proto.pulumirpc.DeleteRequest.serializeBinaryToWriter = function(message, writer
f
);
}
f = message.getOldInputs();
if (f != null) {
writer.writeMessage(
5,
f,
google_protobuf_struct_pb.Struct.serializeBinaryToWriter
);
}
};
@ -6795,6 +6839,43 @@ proto.pulumirpc.DeleteRequest.prototype.setTimeout = function(value) {
};
/**
* optional google.protobuf.Struct old_inputs = 5;
* @return {?proto.google.protobuf.Struct}
*/
proto.pulumirpc.DeleteRequest.prototype.getOldInputs = function() {
return /** @type{?proto.google.protobuf.Struct} */ (
jspb.Message.getWrapperField(this, google_protobuf_struct_pb.Struct, 5));
};
/**
* @param {?proto.google.protobuf.Struct|undefined} value
* @return {!proto.pulumirpc.DeleteRequest} returns this
*/
proto.pulumirpc.DeleteRequest.prototype.setOldInputs = function(value) {
return jspb.Message.setWrapperField(this, 5, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.pulumirpc.DeleteRequest} returns this
*/
proto.pulumirpc.DeleteRequest.prototype.clearOldInputs = function() {
return this.setOldInputs(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.pulumirpc.DeleteRequest.prototype.hasOldInputs = function() {
return jspb.Message.getField(this, 5) != null;
};
/**
* List of repeated fields within this message type.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -90,6 +90,7 @@ class ConfigureRequest(google.protobuf.message.Message):
ACCEPTSECRETS_FIELD_NUMBER: builtins.int
ACCEPTRESOURCES_FIELD_NUMBER: builtins.int
SENDS_OLD_INPUTS_FIELD_NUMBER: builtins.int
SENDS_OLD_INPUTS_TO_DELETE_FIELD_NUMBER: builtins.int
@property
def variables(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]:
"""a map of configuration keys to values."""
@ -102,6 +103,8 @@ class ConfigureRequest(google.protobuf.message.Message):
"""when true, operations should return resources as strongly typed values to the provider."""
sends_old_inputs: builtins.bool
"""when true, diff and update will be called with the old outputs and the old inputs."""
sends_old_inputs_to_delete: builtins.bool
"""when true, delete will be called with the old outputs and the old inputs."""
def __init__(
self,
*,
@ -110,9 +113,10 @@ class ConfigureRequest(google.protobuf.message.Message):
acceptSecrets: builtins.bool = ...,
acceptResources: builtins.bool = ...,
sends_old_inputs: builtins.bool = ...,
sends_old_inputs_to_delete: builtins.bool = ...,
) -> None: ...
def HasField(self, field_name: typing_extensions.Literal["args", b"args"]) -> builtins.bool: ...
def ClearField(self, field_name: typing_extensions.Literal["acceptResources", b"acceptResources", "acceptSecrets", b"acceptSecrets", "args", b"args", "sends_old_inputs", b"sends_old_inputs", "variables", b"variables"]) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["acceptResources", b"acceptResources", "acceptSecrets", b"acceptSecrets", "args", b"args", "sends_old_inputs", b"sends_old_inputs", "sends_old_inputs_to_delete", b"sends_old_inputs_to_delete", "variables", b"variables"]) -> None: ...
global___ConfigureRequest = ConfigureRequest
@ -902,6 +906,7 @@ class DeleteRequest(google.protobuf.message.Message):
URN_FIELD_NUMBER: builtins.int
PROPERTIES_FIELD_NUMBER: builtins.int
TIMEOUT_FIELD_NUMBER: builtins.int
OLD_INPUTS_FIELD_NUMBER: builtins.int
id: builtins.str
"""the ID of the resource to delete."""
urn: builtins.str
@ -911,6 +916,9 @@ class DeleteRequest(google.protobuf.message.Message):
"""the current properties on the resource."""
timeout: builtins.float
"""the delete request timeout represented in seconds."""
@property
def old_inputs(self) -> google.protobuf.struct_pb2.Struct:
"""the old input values of the resource to delete."""
def __init__(
self,
*,
@ -918,9 +926,10 @@ class DeleteRequest(google.protobuf.message.Message):
urn: builtins.str = ...,
properties: google.protobuf.struct_pb2.Struct | None = ...,
timeout: builtins.float = ...,
old_inputs: google.protobuf.struct_pb2.Struct | None = ...,
) -> None: ...
def HasField(self, field_name: typing_extensions.Literal["properties", b"properties"]) -> builtins.bool: ...
def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "properties", b"properties", "timeout", b"timeout", "urn", b"urn"]) -> None: ...
def HasField(self, field_name: typing_extensions.Literal["old_inputs", b"old_inputs", "properties", b"properties"]) -> builtins.bool: ...
def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "old_inputs", b"old_inputs", "properties", b"properties", "timeout", b"timeout", "urn", b"urn"]) -> None: ...
global___DeleteRequest = DeleteRequest