use the PlanGraphBuilder for destroy

Rather than maintain a separate graph builder for destroy, use the
normal plan graph with some extra options. Utilize the same pattern as
the validate graph for now, where we take the normal plan graph builder
and inject a new concrete function for the destroy nodes.
This commit is contained in:
James Bardin 2022-05-27 10:59:11 -04:00
parent 77d13808d5
commit 8fed14fc59
3 changed files with 46 additions and 132 deletions

View File

@ -574,7 +574,7 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
}).Build(addrs.RootModuleInstance) }).Build(addrs.RootModuleInstance)
return graph, walkPlan, diags return graph, walkPlan, diags
case plans.DestroyMode: case plans.DestroyMode:
graph, diags := (&DestroyPlanGraphBuilder{ graph, diags := DestroyPlanGraphBuilder(&PlanGraphBuilder{
Config: config, Config: config,
State: prevRunState, State: prevRunState,
RootVariableValues: opts.SetVariables, RootVariableValues: opts.SetVariables,

View File

@ -1,115 +1,17 @@
package terraform package terraform
import ( import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag" "github.com/hashicorp/terraform/internal/dag"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
) )
// DestroyPlanGraphBuilder implements GraphBuilder and is responsible for func DestroyPlanGraphBuilder(p *PlanGraphBuilder) GraphBuilder {
// planning a pure-destroy. p.ConcreteResourceInstance = func(a *NodeAbstractResourceInstance) dag.Vertex {
//
// Planning a pure destroy operation is simple because we can ignore most
// ordering configuration and simply reverse the state. This graph mainly
// exists for targeting, because we need to walk the destroy dependencies to
// ensure we plan the required resources. Without the requirement for
// targeting, the plan could theoretically be created directly from the state.
type DestroyPlanGraphBuilder struct {
// Config is the configuration tree to build the plan from.
Config *configs.Config
// State is the current state
State *states.State
// RootVariableValues are the raw input values for root input variables
// given by the caller, which we'll resolve into final values as part
// of the plan walk.
RootVariableValues InputValues
// Plugins is a library of plug-in components (providers and
// provisioners) available for use.
Plugins *contextPlugins
// Targets are resources to target
Targets []addrs.Targetable
// If set, skipRefresh will cause us stop skip refreshing any existing
// resource instances as part of our planning. This will cause us to fail
// to detect if an object has already been deleted outside of Terraform.
skipRefresh bool
}
// See GraphBuilder
func (b *DestroyPlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
return (&BasicGraphBuilder{
Steps: b.Steps(),
Name: "DestroyPlanGraphBuilder",
}).Build(path)
}
// See GraphBuilder
func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer {
concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex {
return &NodePlanDestroyableResourceInstance{ return &NodePlanDestroyableResourceInstance{
NodeAbstractResourceInstance: a, NodeAbstractResourceInstance: a,
skipRefresh: b.skipRefresh, skipRefresh: p.skipRefresh,
} }
} }
concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { p.destroy = true
return &NodePlanDeposedResourceInstanceObject{
NodeAbstractResourceInstance: a, return p
DeposedKey: key,
skipRefresh: b.skipRefresh,
}
}
concreteProvider := func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
}
}
steps := []GraphTransformer{
// Creates nodes for the resource instances tracked in the state.
&StateTransformer{
ConcreteCurrent: concreteResourceInstance,
ConcreteDeposed: concreteResourceInstanceDeposed,
State: b.State,
},
// Create the delete changes for root module outputs.
&OutputTransformer{
Config: b.Config,
Destroy: true,
},
// Attach the state
&AttachStateTransformer{State: b.State},
// Attach the configuration to any resources
&AttachResourceConfigTransformer{Config: b.Config},
transformProviders(concreteProvider, b.Config),
// Destruction ordering. We require this only so that
// targeting below will prune the correct things.
&DestroyEdgeTransformer{
Config: b.Config,
State: b.State,
},
&TargetsTransformer{Targets: b.Targets},
// Close opened plugin connections
&CloseProviderTransformer{},
// Close the root module
&CloseRootModuleTransformer{},
&TransitiveReductionTransformer{},
}
return steps
} }

View File

@ -1,8 +1,6 @@
package terraform package terraform
import ( import (
"sync"
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag" "github.com/hashicorp/terraform/internal/dag"
@ -60,10 +58,13 @@ type PlanGraphBuilder struct {
CustomConcrete bool CustomConcrete bool
ConcreteProvider ConcreteProviderNodeFunc ConcreteProvider ConcreteProviderNodeFunc
ConcreteResource ConcreteResourceNodeFunc ConcreteResource ConcreteResourceNodeFunc
ConcreteResourceInstance ConcreteResourceInstanceNodeFunc
ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc
ConcreteResourceInstanceDeposed ConcreteResourceInstanceDeposedNodeFunc
ConcreteModule ConcreteModuleNodeFunc ConcreteModule ConcreteModuleNodeFunc
once sync.Once // destroy is set to true when create a full destroy plan.
destroy bool
} }
// See GraphBuilder // See GraphBuilder
@ -76,36 +77,32 @@ func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Dia
// See GraphBuilder // See GraphBuilder
func (b *PlanGraphBuilder) Steps() []GraphTransformer { func (b *PlanGraphBuilder) Steps() []GraphTransformer {
b.once.Do(b.init) b.init()
concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
return &NodePlanDeposedResourceInstanceObject{
NodeAbstractResourceInstance: a,
DeposedKey: key,
skipRefresh: b.skipRefresh,
skipPlanChanges: b.skipPlanChanges,
}
}
steps := []GraphTransformer{ steps := []GraphTransformer{
// Creates all the resources represented in the config // Creates all the resources represented in the config
&ConfigTransformer{ &ConfigTransformer{
Concrete: b.ConcreteResource, Concrete: b.ConcreteResource,
Config: b.Config, Config: b.Config,
destroyPlan: b.destroy,
}, },
// Add dynamic values // Add dynamic values
&RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues}, &RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues},
&ModuleVariableTransformer{Config: b.Config}, &ModuleVariableTransformer{Config: b.Config},
&LocalTransformer{Config: b.Config}, &LocalTransformer{Config: b.Config},
&OutputTransformer{Config: b.Config, RefreshOnly: b.skipPlanChanges}, &OutputTransformer{
Config: b.Config,
RefreshOnly: b.skipPlanChanges,
destroyPlan: b.destroy,
},
// Add orphan resources // Add orphan resources
&OrphanResourceInstanceTransformer{ &OrphanResourceInstanceTransformer{
Concrete: b.ConcreteResourceOrphan, Concrete: b.ConcreteResourceOrphan,
State: b.State, State: b.State,
Config: b.Config, Config: b.Config,
destroyPlan: b.destroy,
}, },
// We also need nodes for any deposed instance objects present in the // We also need nodes for any deposed instance objects present in the
@ -113,7 +110,8 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// skips creating nodes for _current_ objects, since ConfigTransformer // skips creating nodes for _current_ objects, since ConfigTransformer
// created nodes that will do that during DynamicExpand.) // created nodes that will do that during DynamicExpand.)
&StateTransformer{ &StateTransformer{
ConcreteDeposed: concreteResourceInstanceDeposed, ConcreteCurrent: b.ConcreteResourceInstance,
ConcreteDeposed: b.ConcreteResourceInstanceDeposed,
State: b.State, State: b.State,
}, },
@ -141,15 +139,18 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// objects that can belong to modules. // objects that can belong to modules.
&ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config}, &ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config},
// Connect so that the references are ready for targeting. We'll
// have to connect again later for providers and so on.
&ReferenceTransformer{}, &ReferenceTransformer{},
&AttachDependenciesTransformer{}, &AttachDependenciesTransformer{},
// Make sure data sources are aware of any depends_on from the // Make sure data sources are aware of any depends_on from the
// configuration // configuration
&attachDataResourceDependsOnTransformer{}, &attachDataResourceDependsOnTransformer{},
// DestroyEdgeTransformer is only required during a plan so that the
// TargetsTransformer can determine which nodes to keep in the graph.
&DestroyEdgeTransformer{},
// Target // Target
&TargetsTransformer{Targets: b.Targets}, &TargetsTransformer{Targets: b.Targets},
@ -199,4 +200,15 @@ func (b *PlanGraphBuilder) init() {
skipPlanChanges: b.skipPlanChanges, skipPlanChanges: b.skipPlanChanges,
} }
} }
b.ConcreteResourceInstanceDeposed = func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
return &NodePlanDeposedResourceInstanceObject{
NodeAbstractResourceInstance: a,
DeposedKey: key,
skipRefresh: b.skipRefresh,
skipPlanChanges: b.skipPlanChanges,
}
}
} }