Merge pull request #29720 from hashicorp/jbardin/destroy-plan-deposed
don't refresh deposed instances during the destroy plan
This commit is contained in:
commit
3c942ee3ac
|
@ -549,3 +549,50 @@ resource "test_object" "y" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext2Apply_destroyWithDeposed(t *testing.T) {
|
||||||
|
m := testModuleInline(t, map[string]string{
|
||||||
|
"main.tf": `
|
||||||
|
resource "test_object" "x" {
|
||||||
|
test_string = "ok"
|
||||||
|
lifecycle {
|
||||||
|
create_before_destroy = true
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
p := simpleMockProvider()
|
||||||
|
|
||||||
|
deposedKey := states.NewDeposedKey()
|
||||||
|
|
||||||
|
state := states.NewState()
|
||||||
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
||||||
|
root.SetResourceInstanceDeposed(
|
||||||
|
mustResourceInstanceAddr("test_object.x").Resource,
|
||||||
|
deposedKey,
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectTainted,
|
||||||
|
AttrsJSON: []byte(`{"test_string":"deposed"}`),
|
||||||
|
},
|
||||||
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
|
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
plan, diags := ctx.Plan(m, state, &PlanOpts{
|
||||||
|
Mode: plans.DestroyMode,
|
||||||
|
})
|
||||||
|
if diags.HasErrors() {
|
||||||
|
t.Fatalf("plan: %s", diags.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, diags = ctx.Apply(plan, m)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
t.Fatalf("apply: %s", diags.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -419,7 +420,12 @@ resource "test_object" "a" {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is called from the first instance of this provider, so we can't
|
||||||
|
// check p.ReadResourceCalled after plan.
|
||||||
|
readResourceCalled := false
|
||||||
p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
||||||
|
readResourceCalled = true
|
||||||
newVal, err := cty.Transform(req.PriorState, func(path cty.Path, v cty.Value) (cty.Value, error) {
|
newVal, err := cty.Transform(req.PriorState, func(path cty.Path, v cty.Value) (cty.Value, error) {
|
||||||
if len(path) == 1 && path[0] == (cty.GetAttrStep{Name: "arg"}) {
|
if len(path) == 1 && path[0] == (cty.GetAttrStep{Name: "arg"}) {
|
||||||
return cty.StringVal("current"), nil
|
return cty.StringVal("current"), nil
|
||||||
|
@ -435,7 +441,10 @@ resource "test_object" "a" {
|
||||||
NewState: newVal,
|
NewState: newVal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upgradeResourceStateCalled := false
|
||||||
p.UpgradeResourceStateFn = func(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
|
p.UpgradeResourceStateFn = func(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
|
||||||
|
upgradeResourceStateCalled = true
|
||||||
t.Logf("UpgradeResourceState %s", req.RawStateJSON)
|
t.Logf("UpgradeResourceState %s", req.RawStateJSON)
|
||||||
|
|
||||||
// In the destroy-with-refresh codepath we end up calling
|
// In the destroy-with-refresh codepath we end up calling
|
||||||
|
@ -479,10 +488,10 @@ resource "test_object" "a" {
|
||||||
})
|
})
|
||||||
assertNoErrors(t, diags)
|
assertNoErrors(t, diags)
|
||||||
|
|
||||||
if !p.UpgradeResourceStateCalled {
|
if !upgradeResourceStateCalled {
|
||||||
t.Errorf("Provider's UpgradeResourceState wasn't called; should've been")
|
t.Errorf("Provider's UpgradeResourceState wasn't called; should've been")
|
||||||
}
|
}
|
||||||
if !p.ReadResourceCalled {
|
if !readResourceCalled {
|
||||||
t.Errorf("Provider's ReadResource wasn't called; should've been")
|
t.Errorf("Provider's ReadResource wasn't called; should've been")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,7 +691,7 @@ func TestContext2Plan_destroyNoProviderConfig(t *testing.T) {
|
||||||
p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||||
v := req.Config.GetAttr("test_string")
|
v := req.Config.GetAttr("test_string")
|
||||||
if v.IsNull() || !v.IsKnown() || v.AsString() != "ok" {
|
if v.IsNull() || !v.IsKnown() || v.AsString() != "ok" {
|
||||||
resp.Diagnostics = resp.Diagnostics.Append(errors.New("invalid provider configuration"))
|
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("invalid provider configuration: %#v", req.Config))
|
||||||
}
|
}
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) (diag
|
||||||
log.Printf("[TRACE] NodeApplyableProvider: validating configuration for %s", n.Addr)
|
log.Printf("[TRACE] NodeApplyableProvider: validating configuration for %s", n.Addr)
|
||||||
return diags.Append(n.ValidateProvider(ctx, provider))
|
return diags.Append(n.ValidateProvider(ctx, provider))
|
||||||
case walkPlan, walkApply, walkDestroy:
|
case walkPlan, walkApply, walkDestroy:
|
||||||
|
// walkPlanDestroy is purposely skipped here, since the config is not
|
||||||
|
// evaluated, and the provider is not needed to create delete actions
|
||||||
|
// for all instances.
|
||||||
log.Printf("[TRACE] NodeApplyableProvider: configuring %s", n.Addr)
|
log.Printf("[TRACE] NodeApplyableProvider: configuring %s", n.Addr)
|
||||||
return diags.Append(n.ConfigureProvider(ctx, provider, false))
|
return diags.Append(n.ConfigureProvider(ctx, provider, false))
|
||||||
case walkImport:
|
case walkImport:
|
||||||
|
|
|
@ -95,7 +95,12 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
if !n.skipRefresh {
|
// We don't refresh during the planDestroy walk, since that is only adding
|
||||||
|
// the destroy changes to the plan and the provider will not be configured
|
||||||
|
// at this point. The other nodes use separate types for plan and destroy,
|
||||||
|
// while deposed instances are always a destroy operation, so the logic
|
||||||
|
// here is a bit overloaded.
|
||||||
|
if !n.skipRefresh && op != walkPlanDestroy {
|
||||||
// Refresh this object even though it is going to be destroyed, in
|
// Refresh this object even though it is going to be destroyed, in
|
||||||
// case it's already been deleted outside of Terraform. If this is a
|
// case it's already been deleted outside of Terraform. If this is a
|
||||||
// normal plan, providers expect a Read request to remove missing
|
// normal plan, providers expect a Read request to remove missing
|
||||||
|
|
|
@ -26,6 +26,7 @@ func TestNodePlanDeposedResourceInstanceObject_Execute(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
p := testProvider("test")
|
p := testProvider("test")
|
||||||
|
p.ConfigureProvider(providers.ConfigureProviderRequest{})
|
||||||
p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{
|
p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{
|
||||||
UpgradedState: cty.ObjectVal(map[string]cty.Value{
|
UpgradedState: cty.ObjectVal(map[string]cty.Value{
|
||||||
"id": cty.StringVal("bar"),
|
"id": cty.StringVal("bar"),
|
||||||
|
@ -106,6 +107,7 @@ func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p := testProvider("test")
|
p := testProvider("test")
|
||||||
|
p.ConfigureProvider(providers.ConfigureProviderRequest{})
|
||||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
|
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
|
||||||
|
|
||||||
p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{
|
p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{
|
||||||
|
|
|
@ -33,6 +33,7 @@ func TestNodeResourcePlanOrphanExecute(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
p := simpleMockProvider()
|
p := simpleMockProvider()
|
||||||
|
p.ConfigureProvider(providers.ConfigureProviderRequest{})
|
||||||
ctx := &MockEvalContext{
|
ctx := &MockEvalContext{
|
||||||
StateState: state.SyncWrapper(),
|
StateState: state.SyncWrapper(),
|
||||||
RefreshStateState: state.DeepCopy().SyncWrapper(),
|
RefreshStateState: state.DeepCopy().SyncWrapper(),
|
||||||
|
@ -93,6 +94,7 @@ func TestNodeResourcePlanOrphanExecute_alreadyDeleted(t *testing.T) {
|
||||||
changes := plans.NewChanges()
|
changes := plans.NewChanges()
|
||||||
|
|
||||||
p := simpleMockProvider()
|
p := simpleMockProvider()
|
||||||
|
p.ConfigureProvider(providers.ConfigureProviderRequest{})
|
||||||
p.ReadResourceResponse = &providers.ReadResourceResponse{
|
p.ReadResourceResponse = &providers.ReadResourceResponse{
|
||||||
NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["test_string"].Block.ImpliedType()),
|
NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["test_string"].Block.ImpliedType()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,6 +297,11 @@ func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
||||||
p.ReadResourceCalled = true
|
p.ReadResourceCalled = true
|
||||||
p.ReadResourceRequest = r
|
p.ReadResourceRequest = r
|
||||||
|
|
||||||
|
if !p.ConfigureProviderCalled {
|
||||||
|
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ReadResource %q", r.TypeName))
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
if p.ReadResourceFn != nil {
|
if p.ReadResourceFn != nil {
|
||||||
return p.ReadResourceFn(r)
|
return p.ReadResourceFn(r)
|
||||||
}
|
}
|
||||||
|
@ -330,6 +335,11 @@ func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if !p.ConfigureProviderCalled {
|
||||||
|
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before PlanResourceChange %q", r.TypeName))
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
p.PlanResourceChangeCalled = true
|
p.PlanResourceChangeCalled = true
|
||||||
p.PlanResourceChangeRequest = r
|
p.PlanResourceChangeRequest = r
|
||||||
|
|
||||||
|
@ -400,6 +410,11 @@ func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
||||||
p.ApplyResourceChangeRequest = r
|
p.ApplyResourceChangeRequest = r
|
||||||
p.Unlock()
|
p.Unlock()
|
||||||
|
|
||||||
|
if !p.ConfigureProviderCalled {
|
||||||
|
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ApplyResourceChange %q", r.TypeName))
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
if p.ApplyResourceChangeFn != nil {
|
if p.ApplyResourceChangeFn != nil {
|
||||||
return p.ApplyResourceChangeFn(r)
|
return p.ApplyResourceChangeFn(r)
|
||||||
}
|
}
|
||||||
|
@ -460,6 +475,11 @@ func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateReques
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if !p.ConfigureProviderCalled {
|
||||||
|
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ImportResourceState %q", r.TypeName))
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
p.ImportResourceStateCalled = true
|
p.ImportResourceStateCalled = true
|
||||||
p.ImportResourceStateRequest = r
|
p.ImportResourceStateRequest = r
|
||||||
if p.ImportResourceStateFn != nil {
|
if p.ImportResourceStateFn != nil {
|
||||||
|
@ -494,6 +514,11 @@ func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
|
if !p.ConfigureProviderCalled {
|
||||||
|
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ReadDataSource %q", r.TypeName))
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
p.ReadDataSourceCalled = true
|
p.ReadDataSourceCalled = true
|
||||||
p.ReadDataSourceRequest = r
|
p.ReadDataSourceRequest = r
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,23 @@ func testSetResourceInstanceTainted(module *states.Module, resource, attrsJson,
|
||||||
|
|
||||||
func testProviderFuncFixed(rp providers.Interface) providers.Factory {
|
func testProviderFuncFixed(rp providers.Interface) providers.Factory {
|
||||||
return func() (providers.Interface, error) {
|
return func() (providers.Interface, error) {
|
||||||
|
if p, ok := rp.(*MockProvider); ok {
|
||||||
|
// make sure none of the methods were "called" on this new instance
|
||||||
|
p.GetProviderSchemaCalled = false
|
||||||
|
p.ValidateProviderConfigCalled = false
|
||||||
|
p.ValidateResourceConfigCalled = false
|
||||||
|
p.ValidateDataResourceConfigCalled = false
|
||||||
|
p.UpgradeResourceStateCalled = false
|
||||||
|
p.ConfigureProviderCalled = false
|
||||||
|
p.StopCalled = false
|
||||||
|
p.ReadResourceCalled = false
|
||||||
|
p.PlanResourceChangeCalled = false
|
||||||
|
p.ApplyResourceChangeCalled = false
|
||||||
|
p.ImportResourceStateCalled = false
|
||||||
|
p.ReadDataSourceCalled = false
|
||||||
|
p.CloseCalled = false
|
||||||
|
}
|
||||||
|
|
||||||
return rp, nil
|
return rp, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ func TestGraphNodeImportStateExecute(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
provider.ConfigureProvider(providers.ConfigureProviderRequest{})
|
||||||
|
|
||||||
ctx := &MockEvalContext{
|
ctx := &MockEvalContext{
|
||||||
StateState: state.SyncWrapper(),
|
StateState: state.SyncWrapper(),
|
||||||
|
@ -64,6 +65,7 @@ func TestGraphNodeImportStateExecute(t *testing.T) {
|
||||||
func TestGraphNodeImportStateSubExecute(t *testing.T) {
|
func TestGraphNodeImportStateSubExecute(t *testing.T) {
|
||||||
state := states.NewState()
|
state := states.NewState()
|
||||||
provider := testProvider("aws")
|
provider := testProvider("aws")
|
||||||
|
provider.ConfigureProvider(providers.ConfigureProviderRequest{})
|
||||||
ctx := &MockEvalContext{
|
ctx := &MockEvalContext{
|
||||||
StateState: state.SyncWrapper(),
|
StateState: state.SyncWrapper(),
|
||||||
ProviderProvider: provider,
|
ProviderProvider: provider,
|
||||||
|
|
Loading…
Reference in New Issue