Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions internal/terraform/context_plan2_test.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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