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

prune unused nodes from a destroy plan graph #31858

Merged
merged 1 commit into from Sep 26, 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
41 changes: 41 additions & 0 deletions internal/terraform/context_plan2_test.go
Expand Up @@ -3518,3 +3518,44 @@ resource "test_object" "b" {
t.Fatalf("no cycle error found:\n got: %s\n", msg)
}
}

// plan a destroy with no state where configuration could fail to evaluate
// expansion indexes.
func TestContext2Plan_emptyDestroy(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
locals {
enable = true
value = local.enable ? module.example[0].out : null
}

module "example" {
count = local.enable ? 1 : 0
source = "./example"
}
`,
"example/main.tf": `
resource "test_resource" "x" {
}

output "out" {
value = test_resource.x
}
`,
})

p := testProvider("test")
state := states.NewState()

ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})

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

assertNoErrors(t, diags)
}
4 changes: 4 additions & 0 deletions internal/terraform/graph_builder_plan.go
Expand Up @@ -170,6 +170,10 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// TargetsTransformer can determine which nodes to keep in the graph.
&DestroyEdgeTransformer{},

&pruneUnusedNodesTransformer{
skip: b.Operation != walkPlanDestroy,
},

// Target
&TargetsTransformer{Targets: b.Targets},

Expand Down
4 changes: 4 additions & 0 deletions internal/terraform/node_resource_plan.go
Expand Up @@ -53,12 +53,16 @@ var (
_ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil)
_ GraphNodeAttachDependencies = (*nodeExpandPlannableResource)(nil)
_ GraphNodeTargetable = (*nodeExpandPlannableResource)(nil)
_ graphNodeExpandsInstances = (*nodeExpandPlannableResource)(nil)
)

func (n *nodeExpandPlannableResource) Name() string {
return n.NodeAbstractResource.Name() + " (expand)"
}

func (n *nodeExpandPlannableResource) expandsInstances() {
}

// GraphNodeAttachDependencies
func (n *nodeExpandPlannableResource) AttachDependencies(deps []addrs.ConfigResource) {
n.dependencies = deps
Expand Down
9 changes: 9 additions & 0 deletions internal/terraform/transform_destroy_edge.go
Expand Up @@ -170,9 +170,18 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// closers also need to disable their use of expansion if the module itself is
// no longer present.
type pruneUnusedNodesTransformer struct {
// The plan graph builder will skip this transformer except during a full
// destroy. Planing normally involves all nodes, but during a destroy plan
// we may need to prune things which are in the configuration but do not
// exist in state to evaluate.
skip bool
}

func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error {
if t.skip {
return nil
}

// We need a reverse depth first walk of modules, processing them in order
// from the leaf modules to the root. This allows us to remove unneeded
// dependencies from child modules, freeing up nodes in the parent module
Expand Down