Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure destroy plan contains valid state values #32207

Merged
merged 1 commit into from Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 11 additions & 13 deletions internal/terraform/context_plan.go
Expand Up @@ -325,7 +325,6 @@ func (c *Context) refreshOnlyPlan(config *configs.Config, prevRunState *states.S

func (c *Context) destroyPlan(config *configs.Config, prevRunState *states.State, opts *PlanOpts) (*plans.Plan, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
pendingPlan := &plans.Plan{}

if opts.Mode != plans.DestroyMode {
panic(fmt.Sprintf("called Context.destroyPlan with %s", opts.Mode))
Expand Down Expand Up @@ -373,18 +372,17 @@ func (c *Context) destroyPlan(config *configs.Config, prevRunState *states.State
return nil, diags
}

// insert the refreshed state into the destroy plan result, and ignore
// the changes recorded from the refresh.
pendingPlan.PriorState = refreshPlan.PriorState.DeepCopy()
pendingPlan.PrevRunState = refreshPlan.PrevRunState.DeepCopy()
log.Printf("[TRACE] Context.destroyPlan: now _really_ creating a destroy plan")

// We'll use the refreshed state -- which is the "prior state" from
// the perspective of this "pending plan" -- as the starting state
// the perspective of this "destroy plan" -- as the starting state
// for our destroy-plan walk, so it can take into account if we
// detected during refreshing that anything was already deleted outside
// of Terraform.
priorState = pendingPlan.PriorState
priorState = refreshPlan.PriorState.DeepCopy()

// The refresh plan may have upgraded state for some resources, make
// sure we store the new version.
prevRunState = refreshPlan.PrevRunState.DeepCopy()
log.Printf("[TRACE] Context.destroyPlan: now _really_ creating a destroy plan")
}

destroyPlan, walkDiags := c.planWalk(config, priorState, opts)
Expand All @@ -394,10 +392,10 @@ func (c *Context) destroyPlan(config *configs.Config, prevRunState *states.State
}

if !opts.SkipRefresh {
// If we didn't skip refreshing then we want the previous run state
// prior state to be the one we originally fed into the c.plan call
// above, not the refreshed version we used for the destroy walk.
destroyPlan.PrevRunState = pendingPlan.PrevRunState
// If we didn't skip refreshing then we want the previous run state to
// be the one we originally fed into the c.refreshOnlyPlan call above,
// not the refreshed version we used for the destroy planWalk.
destroyPlan.PrevRunState = prevRunState
}

relevantAttrs, rDiags := c.relevantResourceAttrsForPlan(config, destroyPlan)
Expand Down
10 changes: 9 additions & 1 deletion internal/terraform/context_plan2_test.go
Expand Up @@ -3676,11 +3676,19 @@ output "out" {
},
})

_, diags := ctx.Plan(m, state, &PlanOpts{
plan, diags := ctx.Plan(m, state, &PlanOpts{
Mode: plans.DestroyMode,
})

assertNoErrors(t, diags)

// ensure that the given states are valid and can be serialized
if plan.PrevRunState == nil {
t.Fatal("nil plan.PrevRunState")
}
if plan.PriorState == nil {
t.Fatal("nil plan.PriorState")
}
}

// A deposed instances which no longer exists during ReadResource creates NoOp
Expand Down