opentofu/terraform/node_resource_destroy.go
James Bardin a32028aeed evaluate vars and outputs during import
Outputs were not being evaluated during import, because it was not added
to the walk filter.

Remove any unnecessary walk filters from all the Execute nodes.
2020-10-06 17:22:50 -04:00

267 lines
7.1 KiB
Go

package terraform
import (
"fmt"
"log"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/states"
)
// NodeDestroyResourceInstance represents a resource instance that is to be
// destroyed.
type NodeDestroyResourceInstance struct {
*NodeAbstractResourceInstance
// If DeposedKey is set to anything other than states.NotDeposed then
// this node destroys a deposed object of the associated instance
// rather than its current object.
DeposedKey states.DeposedKey
}
var (
_ GraphNodeModuleInstance = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeConfigResource = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeDestroyerCBD = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeReferenceable = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeReferencer = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeExecutable = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeProviderConsumer = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeProvisionerConsumer = (*NodeDestroyResourceInstance)(nil)
)
func (n *NodeDestroyResourceInstance) Name() string {
if n.DeposedKey != states.NotDeposed {
return fmt.Sprintf("%s (destroy deposed %s)", n.ResourceInstanceAddr(), n.DeposedKey)
}
return n.ResourceInstanceAddr().String() + " (destroy)"
}
// GraphNodeDestroyer
func (n *NodeDestroyResourceInstance) DestroyAddr() *addrs.AbsResourceInstance {
addr := n.ResourceInstanceAddr()
return &addr
}
// GraphNodeDestroyerCBD
func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool {
// State takes precedence during destroy.
// If the resource was removed, there is no config to check.
// If CBD was forced from descendent, it should be saved in the state
// already.
if s := n.instanceState; s != nil {
if s.Current != nil {
return s.Current.CreateBeforeDestroy
}
}
if n.Config != nil && n.Config.Managed != nil {
return n.Config.Managed.CreateBeforeDestroy
}
return false
}
// GraphNodeDestroyerCBD
func (n *NodeDestroyResourceInstance) ModifyCreateBeforeDestroy(v bool) error {
return nil
}
// GraphNodeReferenceable, overriding NodeAbstractResource
func (n *NodeDestroyResourceInstance) ReferenceableAddrs() []addrs.Referenceable {
normalAddrs := n.NodeAbstractResourceInstance.ReferenceableAddrs()
destroyAddrs := make([]addrs.Referenceable, len(normalAddrs))
phaseType := addrs.ResourceInstancePhaseDestroy
if n.CreateBeforeDestroy() {
phaseType = addrs.ResourceInstancePhaseDestroyCBD
}
for i, normalAddr := range normalAddrs {
switch ta := normalAddr.(type) {
case addrs.Resource:
destroyAddrs[i] = ta.Phase(phaseType)
case addrs.ResourceInstance:
destroyAddrs[i] = ta.Phase(phaseType)
default:
destroyAddrs[i] = normalAddr
}
}
return destroyAddrs
}
// GraphNodeReferencer, overriding NodeAbstractResource
func (n *NodeDestroyResourceInstance) References() []*addrs.Reference {
// If we have a config, then we need to include destroy-time dependencies
if c := n.Config; c != nil && c.Managed != nil {
var result []*addrs.Reference
// We include conn info and config for destroy time provisioners
// as dependencies that we have.
for _, p := range c.Managed.Provisioners {
schema := n.ProvisionerSchemas[p.Type]
if p.When == configs.ProvisionerWhenDestroy {
if p.Connection != nil {
result = append(result, ReferencesFromConfig(p.Connection.Config, connectionBlockSupersetSchema)...)
}
result = append(result, ReferencesFromConfig(p.Config, schema)...)
}
}
return result
}
return nil
}
// GraphNodeExecutable
func (n *NodeDestroyResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
addr := n.ResourceInstanceAddr()
// Get our state
is := n.instanceState
if is == nil {
log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr)
}
// These vars are updated through pointers at various stages below.
var changeApply *plans.ResourceInstanceChange
var state *states.ResourceInstanceObject
var provisionerErr error
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
if err != nil {
return err
}
changeApply, err = n.readDiff(ctx, providerSchema)
if err != nil {
return err
}
evalReduceDiff := &EvalReduceDiff{
Addr: addr.Resource,
InChange: &changeApply,
Destroy: true,
OutChange: &changeApply,
}
_, err = evalReduceDiff.Eval(ctx)
if err != nil {
return err
}
// EvalReduceDiff may have simplified our planned change
// into a NoOp if it does not require destroying.
if changeApply == nil || changeApply.Action == plans.NoOp {
return EvalEarlyExitError{}
}
state, err = n.ReadResourceInstanceState(ctx, addr)
if err != nil {
return err
}
// Exit early if the state object is null after reading the state
if state == nil || state.Value.IsNull() {
return EvalEarlyExitError{}
}
evalApplyPre := &EvalApplyPre{
Addr: addr.Resource,
State: &state,
Change: &changeApply,
}
_, err = evalApplyPre.Eval(ctx)
if err != nil {
return err
}
// Run destroy provisioners if not tainted
if state != nil && state.Status != states.ObjectTainted {
evalApplyProvisioners := &EvalApplyProvisioners{
Addr: addr.Resource,
State: &state,
ResourceConfig: n.Config,
Error: &provisionerErr,
When: configs.ProvisionerWhenDestroy,
}
_, err := evalApplyProvisioners.Eval(ctx)
if err != nil {
return err
}
if provisionerErr != nil {
// If we have a provisioning error, then we just call
// the post-apply hook now.
evalApplyPost := &EvalApplyPost{
Addr: addr.Resource,
State: &state,
Error: &provisionerErr,
}
_, err = evalApplyPost.Eval(ctx)
if err != nil {
return err
}
}
}
// Managed resources need to be destroyed, while data sources
// are only removed from state.
if addr.Resource.Resource.Mode == addrs.ManagedResourceMode {
evalApply := &EvalApply{
Addr: addr.Resource,
Config: nil, // No configuration because we are destroying
State: &state,
Change: &changeApply,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
Output: &state,
Error: &provisionerErr,
}
_, err = evalApply.Eval(ctx)
if err != nil {
return err
}
evalWriteState := &EvalWriteState{
Addr: addr.Resource,
ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema,
State: &state,
}
_, err = evalWriteState.Eval(ctx)
if err != nil {
return err
}
} else {
log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr)
state := ctx.State()
state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
}
evalApplyPost := &EvalApplyPost{
Addr: addr.Resource,
State: &state,
Error: &provisionerErr,
}
_, err = evalApplyPost.Eval(ctx)
if err != nil {
return err
}
err = UpdateStateHook(ctx)
if err != nil {
return err
}
return nil
}