mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
destroy locals referenced by root outputs
When planning a destroy operations, locals only referenced by root outputs do not need to be kept in the graph, because the root output does not get evaluated. Rather than try and prune the local based on this condition, we can prevent the connection from being created by ensuring that a root output destroy node has no references. The separate plan+apply destroy fields used for outputs can be simplified by combining, since they are only ever referenced together.
This commit is contained in:
parent
53755180fd
commit
0a921976cd
@ -4005,6 +4005,48 @@ output "out" {
|
||||
assertNoErrors(t, diags)
|
||||
}
|
||||
|
||||
func TestContext2Plan_destroyPartialStateLocalRef(t *testing.T) {
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
module "already_destroyed" {
|
||||
count = 1
|
||||
source = "./mod"
|
||||
}
|
||||
|
||||
locals {
|
||||
eval_error = module.already_destroyed[0].out
|
||||
}
|
||||
|
||||
output "already_destroyed" {
|
||||
value = local.eval_error
|
||||
}
|
||||
|
||||
`,
|
||||
|
||||
"./mod/main.tf": `
|
||||
resource "test_object" "a" {
|
||||
}
|
||||
|
||||
output "out" {
|
||||
value = test_object.a.test_string
|
||||
}
|
||||
`})
|
||||
|
||||
p := simpleMockProvider()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Make sure the data sources in the prior state are serializeable even if
|
||||
// there were an error in the plan.
|
||||
func TestContext2Plan_dataSourceReadPlanError(t *testing.T) {
|
||||
|
@ -99,8 +99,8 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||
&ModuleVariableTransformer{Config: b.Config},
|
||||
&LocalTransformer{Config: b.Config},
|
||||
&OutputTransformer{
|
||||
Config: b.Config,
|
||||
ApplyDestroy: b.Operation == walkDestroy,
|
||||
Config: b.Config,
|
||||
Destroying: b.Operation == walkDestroy,
|
||||
},
|
||||
|
||||
// Creates all the resource instances represented in the diff, along
|
||||
|
@ -127,7 +127,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||
&OutputTransformer{
|
||||
Config: b.Config,
|
||||
RefreshOnly: b.skipPlanChanges || b.preDestroyRefresh,
|
||||
PlanDestroy: b.Operation == walkPlanDestroy,
|
||||
Destroying: b.Operation == walkPlanDestroy,
|
||||
|
||||
// NOTE: We currently treat anything built with the plan graph
|
||||
// builder as "planning" for our purposes here, because we share
|
||||
|
@ -23,12 +23,11 @@ import (
|
||||
// nodeExpandOutput is the placeholder for a non-root module output that has
|
||||
// not yet had its module path expanded.
|
||||
type nodeExpandOutput struct {
|
||||
Addr addrs.OutputValue
|
||||
Module addrs.Module
|
||||
Config *configs.Output
|
||||
PlanDestroy bool
|
||||
ApplyDestroy bool
|
||||
RefreshOnly bool
|
||||
Addr addrs.OutputValue
|
||||
Module addrs.Module
|
||||
Config *configs.Output
|
||||
Destroying bool
|
||||
RefreshOnly bool
|
||||
|
||||
// Planning is set to true when this node is in a graph that was produced
|
||||
// by the plan graph builder, as opposed to the apply graph builder.
|
||||
@ -103,13 +102,13 @@ func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
|
||||
var node dag.Vertex
|
||||
switch {
|
||||
case module.IsRoot() && (n.PlanDestroy || n.ApplyDestroy):
|
||||
case module.IsRoot() && n.Destroying:
|
||||
node = &NodeDestroyableOutput{
|
||||
Addr: absAddr,
|
||||
Planning: n.Planning,
|
||||
}
|
||||
|
||||
case n.PlanDestroy:
|
||||
case n.Destroying:
|
||||
// nothing is done here for non-root outputs
|
||||
continue
|
||||
|
||||
@ -119,7 +118,7 @@ func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
Config: n.Config,
|
||||
Change: change,
|
||||
RefreshOnly: n.RefreshOnly,
|
||||
DestroyApply: n.ApplyDestroy,
|
||||
DestroyApply: n.Destroying,
|
||||
Planning: n.Planning,
|
||||
}
|
||||
}
|
||||
@ -186,7 +185,7 @@ func (n *nodeExpandOutput) ReferenceOutside() (selfPath, referencePath addrs.Mod
|
||||
// GraphNodeReferencer
|
||||
func (n *nodeExpandOutput) References() []*addrs.Reference {
|
||||
// DestroyNodes do not reference anything.
|
||||
if n.Module.IsRoot() && n.ApplyDestroy {
|
||||
if n.Module.IsRoot() && n.Destroying {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,8 @@ type OutputTransformer struct {
|
||||
Planning bool
|
||||
|
||||
// If this is a planned destroy, root outputs are still in the configuration
|
||||
// so we need to record that we wish to remove them
|
||||
PlanDestroy bool
|
||||
|
||||
// ApplyDestroy indicates that this is being added to an apply graph, which
|
||||
// is the result of a destroy plan.
|
||||
ApplyDestroy bool
|
||||
// so we need to record that we wish to remove them.
|
||||
Destroying bool
|
||||
}
|
||||
|
||||
func (t *OutputTransformer) Transform(g *Graph) error {
|
||||
@ -59,13 +55,12 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error {
|
||||
addr := addrs.OutputValue{Name: o.Name}
|
||||
|
||||
node := &nodeExpandOutput{
|
||||
Addr: addr,
|
||||
Module: c.Path,
|
||||
Config: o,
|
||||
PlanDestroy: t.PlanDestroy,
|
||||
ApplyDestroy: t.ApplyDestroy,
|
||||
RefreshOnly: t.RefreshOnly,
|
||||
Planning: t.Planning,
|
||||
Addr: addr,
|
||||
Module: c.Path,
|
||||
Config: o,
|
||||
Destroying: t.Destroying,
|
||||
RefreshOnly: t.RefreshOnly,
|
||||
Planning: t.Planning,
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] OutputTransformer: adding %s as %T", o.Name, node)
|
||||
|
Loading…
Reference in New Issue
Block a user