mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-24 16:10:46 -06:00
0b3b84acc1
Because the destroy plan only creates the necessary changes for apply to remove all the resources, it does no reading of resources or data sources, leading to stale data in the state. In most cases this is not a problem, but when a provider configuration is using resource values, the provider may not be able to run correctly during apply. In prior versions of terraform, the implicit refresh that happened during `terraform destroy` would update the data sources and remove missing resources from state as required. The destroy plan graph has a minimal amount of information, so it is not feasible to work the reading of resources into the operation without completely replicating the normal plan graph, and updating the plan graph and all destroy node implementation is also a considerable amount of refactoring. Instead, we can run a normal plan which is used to refresh the state before creating the destroy plan. This brings back similar behavior to core versions prior to 0.14, and the refresh can still be skipped using the `-refresh=false` cli flag.
113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package terraform
|
|
|
|
import (
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/hashicorp/terraform/dag"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// DestroyPlanGraphBuilder implements GraphBuilder and is responsible for
|
|
// planning a pure-destroy.
|
|
//
|
|
// 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
|
|
|
|
// Components is a factory for the plug-in components (providers and
|
|
// provisioners) available for use.
|
|
Components contextComponentFactory
|
|
|
|
// Schemas is the repository of schemas we will draw from to analyse
|
|
// the configuration.
|
|
Schemas *Schemas
|
|
|
|
// Targets are resources to target
|
|
Targets []addrs.Targetable
|
|
|
|
// Validate will do structural validation of the graph.
|
|
Validate bool
|
|
}
|
|
|
|
// See GraphBuilder
|
|
func (b *DestroyPlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
|
|
return (&BasicGraphBuilder{
|
|
Steps: b.Steps(),
|
|
Validate: b.Validate,
|
|
Name: "DestroyPlanGraphBuilder",
|
|
}).Build(path)
|
|
}
|
|
|
|
// See GraphBuilder
|
|
func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer {
|
|
concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
|
return &NodePlanDestroyableResourceInstance{
|
|
NodeAbstractResourceInstance: a,
|
|
}
|
|
}
|
|
concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
|
|
return &NodePlanDeposedResourceInstanceObject{
|
|
NodeAbstractResourceInstance: a,
|
|
DeposedKey: key,
|
|
}
|
|
}
|
|
|
|
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(b.Components.ResourceProviders(), 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,
|
|
Schemas: b.Schemas,
|
|
},
|
|
|
|
&TargetsTransformer{Targets: b.Targets},
|
|
|
|
// Close opened plugin connections
|
|
&CloseProviderTransformer{},
|
|
|
|
// Close the root module
|
|
&CloseRootModuleTransformer{},
|
|
|
|
&TransitiveReductionTransformer{},
|
|
}
|
|
|
|
return steps
|
|
}
|