opentofu/terraform/node_resource_destroy.go
James Bardin b79adeae02 save resolved providers for resources to state
Use the ResourceState.Provider field to store the full name of the
provider used during apply. This field is only used when a resource is
removed from the config, and will allow that resource to be removed by
the exact same provider with which it was created.

Modify the locations which might accept the alue of the
ResourceState.Provider field to detect that the name is resolved.
2017-11-07 13:09:36 -05:00

292 lines
6.5 KiB
Go

package terraform
import (
"fmt"
"github.com/hashicorp/terraform/config"
)
// NodeDestroyResource represents a resource that is to be destroyed.
type NodeDestroyResource struct {
*NodeAbstractResource
}
func (n *NodeDestroyResource) Name() string {
return n.NodeAbstractResource.Name() + " (destroy)"
}
// GraphNodeDestroyer
func (n *NodeDestroyResource) DestroyAddr() *ResourceAddress {
return n.Addr
}
// GraphNodeDestroyerCBD
func (n *NodeDestroyResource) CreateBeforeDestroy() bool {
// If we have no config, we just assume no
if n.Config == nil {
return false
}
return n.Config.Lifecycle.CreateBeforeDestroy
}
// GraphNodeDestroyerCBD
func (n *NodeDestroyResource) ModifyCreateBeforeDestroy(v bool) error {
// If we have no config, do nothing since it won't affect the
// create step anyways.
if n.Config == nil {
return nil
}
// Set CBD to true
n.Config.Lifecycle.CreateBeforeDestroy = true
return nil
}
// GraphNodeReferenceable, overriding NodeAbstractResource
func (n *NodeDestroyResource) ReferenceableName() []string {
// We modify our referenceable name to have the suffix of ".destroy"
// since depending on the creation side doesn't necessarilly mean
// depending on destruction.
suffix := ".destroy"
// If we're CBD, we also append "-cbd". This is because CBD will setup
// its own edges (in CBDEdgeTransformer). Depending on the "destroy"
// side generally doesn't mean depending on CBD as well. See GH-11349
if n.CreateBeforeDestroy() {
suffix += "-cbd"
}
result := n.NodeAbstractResource.ReferenceableName()
for i, v := range result {
result[i] = v + suffix
}
return result
}
// GraphNodeReferencer, overriding NodeAbstractResource
func (n *NodeDestroyResource) References() []string {
// If we have a config, then we need to include destroy-time dependencies
if c := n.Config; c != nil {
var result []string
for _, p := range c.Provisioners {
// We include conn info and config for destroy time provisioners
// as dependencies that we have.
if p.When == config.ProvisionerWhenDestroy {
result = append(result, ReferencesFromConfig(p.ConnInfo)...)
result = append(result, ReferencesFromConfig(p.RawConfig)...)
}
}
return result
}
return nil
}
// GraphNodeDynamicExpandable
func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// If we have no config we do nothing
if n.Addr == nil {
return nil, nil
}
state, lock := ctx.State()
lock.RLock()
defer lock.RUnlock()
// Start creating the steps
steps := make([]GraphTransformer, 0, 5)
// We want deposed resources in the state to be destroyed
steps = append(steps, &DeposedTransformer{
State: state,
View: n.Addr.stateId(),
ResolvedProvider: n.ResolvedProvider,
})
// Target
steps = append(steps, &TargetsTransformer{
ParsedTargets: n.Targets,
})
// Always end with the root being added
steps = append(steps, &RootTransformer{})
// Build the graph
b := &BasicGraphBuilder{
Steps: steps,
Name: "NodeResourceDestroy",
}
return b.Build(ctx.Path())
}
// GraphNodeEvalable
func (n *NodeDestroyResource) EvalTree() EvalNode {
// stateId is the ID to put into the state
stateId := n.Addr.stateId()
// Build the instance info. More of this will be populated during eval
info := &InstanceInfo{
Id: stateId,
Type: n.Addr.Type,
uniqueExtra: "destroy",
}
// Build the resource for eval
addr := n.Addr
resource := &Resource{
Name: addr.Name,
Type: addr.Type,
CountIndex: addr.Index,
}
if resource.CountIndex < 0 {
resource.CountIndex = 0
}
// Get our state
rs := n.ResourceState
if rs == nil {
rs = &ResourceState{
Provider: n.ResolvedProvider,
}
}
var diffApply *InstanceDiff
var provider ResourceProvider
var state *InstanceState
var err error
return &EvalOpFilter{
Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{
Nodes: []EvalNode{
// Get the saved diff for apply
&EvalReadDiff{
Name: stateId,
Diff: &diffApply,
},
// Filter the diff so we only get the destroy
&EvalFilterDiff{
Diff: &diffApply,
Output: &diffApply,
Destroy: true,
},
// If we're not destroying, then compare diffs
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if diffApply != nil && diffApply.GetDestroy() {
return true, nil
}
return true, EvalEarlyExitError{}
},
Then: EvalNoop{},
},
// Load the instance info so we have the module path set
&EvalInstanceInfo{Info: info},
&EvalGetProvider{
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadState{
Name: stateId,
Output: &state,
},
&EvalRequireState{
State: &state,
},
// Call pre-apply hook
&EvalApplyPre{
Info: info,
State: &state,
Diff: &diffApply,
},
// Run destroy provisioners if not tainted
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if state != nil && state.Tainted {
return false, nil
}
return true, nil
},
Then: &EvalApplyProvisioners{
Info: info,
State: &state,
Resource: n.Config,
InterpResource: resource,
Error: &err,
When: config.ProvisionerWhenDestroy,
},
},
// If we have a provisioning error, then we just call
// the post-apply hook now.
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
return err != nil, nil
},
Then: &EvalApplyPost{
Info: info,
State: &state,
Error: &err,
},
},
// Make sure we handle data sources properly.
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if n.Addr == nil {
return false, fmt.Errorf("nil address")
}
if n.Addr.Mode == config.DataResourceMode {
return true, nil
}
return false, nil
},
Then: &EvalReadDataApply{
Info: info,
Diff: &diffApply,
Provider: &provider,
Output: &state,
},
Else: &EvalApply{
Info: info,
State: &state,
Diff: &diffApply,
Provider: &provider,
Output: &state,
Error: &err,
},
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Addr.Type,
Provider: rs.Provider,
Dependencies: rs.Dependencies,
State: &state,
},
&EvalApplyPost{
Info: info,
State: &state,
Error: &err,
},
&EvalUpdateStateHook{},
},
},
}
}