mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
This turned out to be a big messy commit, since the way providers are referenced is tightly coupled throughout the code. That starts to unify how providers are referenced, using the format output node Name method. Add a new field to the internal resource data types called ResolvedProvider. This is set by a new setter method SetProvider when a resource is connected to a provider during graph creation. This allows us to later lookup the provider instance a resource is connected to, without requiring it to have the same module path. The InitProvider context method now takes 2 arguments, one if the provider type and the second is the full name of the provider. While the provider type could still be parsed from the full name, this makes it more explicit and, and changes to the name format won't effect this code.
290 lines
6.4 KiB
Go
290 lines
6.4 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{}
|
|
}
|
|
|
|
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{},
|
|
},
|
|
},
|
|
}
|
|
}
|