Merge pull request #27563 from hashicorp/jbardin/refresh-tainted

Error planning tainted resource when it no longer exists
This commit is contained in:
James Bardin 2021-01-21 17:05:27 -05:00 committed by GitHub
commit 543706c0d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 2 deletions

View File

@ -0,0 +1 @@
package terraform

View File

@ -12564,3 +12564,8 @@ resource "test_object" "a" {
t.Fatalf("error missing expected info: %q", errString)
}
}
////////////////////////////////////////////////////////////////////////////////
// NOTE: Due to the size of this file, new tests should be added to
// context_apply2_test.go.
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,61 @@
package terraform
import (
"testing"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/states"
"github.com/zclconf/go-cty/cty"
)
func TestContext2Plan_removedDuringRefresh(t *testing.T) {
// The resource was added to state but actually failed to create and was
// left tainted. This should be removed during plan and result in a Create
// action.
m := testModuleInline(t, map[string]string{
"main.tf": `
resource "test_object" "a" {
}
`,
})
p := simpleMockProvider()
p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
resp.NewState = cty.NullVal(req.PriorState.Type())
return resp
}
addr := mustResourceInstanceAddr("test_object.a")
state := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"test_string":"foo"}`),
Status: states.ObjectTainted,
}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`))
})
ctx := testContext2(t, &ContextOpts{
Config: m,
State: state,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatal(diags.Err())
}
for _, c := range plan.Changes.Resources {
if c.Action != plans.Create {
t.Fatalf("expected Create action for missing %s, got %s", c.Addr, c.Action)
}
}
_, diags = ctx.Apply()
if diags.HasErrors() {
t.Fatal(diags.Err())
}
}

View File

@ -6732,3 +6732,8 @@ output "planned" {
}
}
}
////////////////////////////////////////////////////////////////////////////////
// NOTE: Due to the size of this file, new tests should be added to
// context_plan2_test.go.
////////////////////////////////////////////////////////////////////////////////

View File

@ -905,7 +905,7 @@ func (n *NodeAbstractResourceInstance) plan(
// If our prior value was tainted then we actually want this to appear
// as a replace change, even though so far we've been treating it as a
// create.
if action == plans.Create && priorValTainted != cty.NilVal {
if action == plans.Create && !priorValTainted.IsNull() {
if createBeforeDestroy {
action = plans.CreateThenDelete
} else {

View File

@ -128,11 +128,12 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
// Refresh, maybe
if !n.skipRefresh {
instanceRefreshState, refreshDiags := n.refresh(ctx, instanceRefreshState)
s, refreshDiags := n.refresh(ctx, instanceRefreshState)
diags = diags.Append(refreshDiags)
if diags.HasErrors() {
return diags
}
instanceRefreshState = s
diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, n.Dependencies, refreshState))
if diags.HasErrors() {