github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_resource_destroy.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/plans"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
     9  
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
    11  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
    12  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
    13  )
    14  
    15  // NodeDestroyResourceInstance represents a resource instance that is to be
    16  // destroyed.
    17  type NodeDestroyResourceInstance struct {
    18  	*NodeAbstractResourceInstance
    19  
    20  	// If DeposedKey is set to anything other than states.NotDeposed then
    21  	// this node destroys a deposed object of the associated instance
    22  	// rather than its current object.
    23  	DeposedKey states.DeposedKey
    24  
    25  	CreateBeforeDestroyOverride *bool
    26  }
    27  
    28  var (
    29  	_ GraphNodeResource            = (*NodeDestroyResourceInstance)(nil)
    30  	_ GraphNodeResourceInstance    = (*NodeDestroyResourceInstance)(nil)
    31  	_ GraphNodeDestroyer           = (*NodeDestroyResourceInstance)(nil)
    32  	_ GraphNodeDestroyerCBD        = (*NodeDestroyResourceInstance)(nil)
    33  	_ GraphNodeReferenceable       = (*NodeDestroyResourceInstance)(nil)
    34  	_ GraphNodeReferencer          = (*NodeDestroyResourceInstance)(nil)
    35  	_ GraphNodeEvalable            = (*NodeDestroyResourceInstance)(nil)
    36  	_ GraphNodeProviderConsumer    = (*NodeDestroyResourceInstance)(nil)
    37  	_ GraphNodeProvisionerConsumer = (*NodeDestroyResourceInstance)(nil)
    38  )
    39  
    40  func (n *NodeDestroyResourceInstance) Name() string {
    41  	if n.DeposedKey != states.NotDeposed {
    42  		return fmt.Sprintf("%s (destroy deposed %s)", n.ResourceInstanceAddr(), n.DeposedKey)
    43  	}
    44  	return n.ResourceInstanceAddr().String() + " (destroy)"
    45  }
    46  
    47  // GraphNodeDestroyer
    48  func (n *NodeDestroyResourceInstance) DestroyAddr() *addrs.AbsResourceInstance {
    49  	addr := n.ResourceInstanceAddr()
    50  	return &addr
    51  }
    52  
    53  // GraphNodeDestroyerCBD
    54  func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool {
    55  	if n.CreateBeforeDestroyOverride != nil {
    56  		return *n.CreateBeforeDestroyOverride
    57  	}
    58  
    59  	// If we have no config, we just assume no
    60  	if n.Config == nil || n.Config.Managed == nil {
    61  		return false
    62  	}
    63  
    64  	return n.Config.Managed.CreateBeforeDestroy
    65  }
    66  
    67  // GraphNodeDestroyerCBD
    68  func (n *NodeDestroyResourceInstance) ModifyCreateBeforeDestroy(v bool) error {
    69  	n.CreateBeforeDestroyOverride = &v
    70  	return nil
    71  }
    72  
    73  // GraphNodeReferenceable, overriding NodeAbstractResource
    74  func (n *NodeDestroyResourceInstance) ReferenceableAddrs() []addrs.Referenceable {
    75  	normalAddrs := n.NodeAbstractResourceInstance.ReferenceableAddrs()
    76  	destroyAddrs := make([]addrs.Referenceable, len(normalAddrs))
    77  
    78  	phaseType := addrs.ResourceInstancePhaseDestroy
    79  	if n.CreateBeforeDestroy() {
    80  		phaseType = addrs.ResourceInstancePhaseDestroyCBD
    81  	}
    82  
    83  	for i, normalAddr := range normalAddrs {
    84  		switch ta := normalAddr.(type) {
    85  		case addrs.Resource:
    86  			destroyAddrs[i] = ta.Phase(phaseType)
    87  		case addrs.ResourceInstance:
    88  			destroyAddrs[i] = ta.Phase(phaseType)
    89  		default:
    90  			destroyAddrs[i] = normalAddr
    91  		}
    92  	}
    93  
    94  	return destroyAddrs
    95  }
    96  
    97  // GraphNodeReferencer, overriding NodeAbstractResource
    98  func (n *NodeDestroyResourceInstance) References() []*addrs.Reference {
    99  	// If we have a config, then we need to include destroy-time dependencies
   100  	if c := n.Config; c != nil && c.Managed != nil {
   101  		var result []*addrs.Reference
   102  
   103  		// We include conn info and config for destroy time provisioners
   104  		// as dependencies that we have.
   105  		for _, p := range c.Managed.Provisioners {
   106  			schema := n.ProvisionerSchemas[p.Type]
   107  
   108  			if p.When == configs.ProvisionerWhenDestroy {
   109  				if p.Connection != nil {
   110  					result = append(result, ReferencesFromConfig(p.Connection.Config, connectionBlockSupersetSchema)...)
   111  				}
   112  				result = append(result, ReferencesFromConfig(p.Config, schema)...)
   113  			}
   114  		}
   115  
   116  		return result
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  // GraphNodeEvalable
   123  func (n *NodeDestroyResourceInstance) EvalTree() EvalNode {
   124  	addr := n.ResourceInstanceAddr()
   125  
   126  	// Get our state
   127  	rs := n.ResourceState
   128  	var is *states.ResourceInstance
   129  	if rs != nil {
   130  		is = rs.Instance(n.InstanceKey)
   131  	}
   132  	if is == nil {
   133  		log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr)
   134  	}
   135  
   136  	var changeApply *plans.ResourceInstanceChange
   137  	var provider providers.Interface
   138  	var providerSchema *ProviderSchema
   139  	var state *states.ResourceInstanceObject
   140  	var err error
   141  	return &EvalOpFilter{
   142  		Ops: []walkOperation{walkApply, walkDestroy},
   143  		Node: &EvalSequence{
   144  			Nodes: []EvalNode{
   145  				&EvalGetProvider{
   146  					Addr:   n.ResolvedProvider,
   147  					Output: &provider,
   148  					Schema: &providerSchema,
   149  				},
   150  
   151  				// Get the saved diff for apply
   152  				&EvalReadDiff{
   153  					Addr:           addr.Resource,
   154  					ProviderSchema: &providerSchema,
   155  					Change:         &changeApply,
   156  				},
   157  
   158  				&EvalReduceDiff{
   159  					Addr:      addr.Resource,
   160  					InChange:  &changeApply,
   161  					Destroy:   true,
   162  					OutChange: &changeApply,
   163  				},
   164  
   165  				// EvalReduceDiff may have simplified our planned change
   166  				// into a NoOp if it does not require destroying.
   167  				&EvalIf{
   168  					If: func(ctx EvalContext) (bool, error) {
   169  						if changeApply == nil || changeApply.Action == plans.NoOp {
   170  							return true, EvalEarlyExitError{}
   171  						}
   172  						return true, nil
   173  					},
   174  					Then: EvalNoop{},
   175  				},
   176  
   177  				&EvalReadState{
   178  					Addr:           addr.Resource,
   179  					Output:         &state,
   180  					Provider:       &provider,
   181  					ProviderSchema: &providerSchema,
   182  				},
   183  				&EvalRequireState{
   184  					State: &state,
   185  				},
   186  
   187  				// Call pre-apply hook
   188  				&EvalApplyPre{
   189  					Addr:   addr.Resource,
   190  					State:  &state,
   191  					Change: &changeApply,
   192  				},
   193  
   194  				// Run destroy provisioners if not tainted
   195  				&EvalIf{
   196  					If: func(ctx EvalContext) (bool, error) {
   197  						if state != nil && state.Status == states.ObjectTainted {
   198  							return false, nil
   199  						}
   200  
   201  						return true, nil
   202  					},
   203  
   204  					Then: &EvalApplyProvisioners{
   205  						Addr:           addr.Resource,
   206  						State:          &state,
   207  						ResourceConfig: n.Config,
   208  						Error:          &err,
   209  						When:           configs.ProvisionerWhenDestroy,
   210  					},
   211  				},
   212  
   213  				// If we have a provisioning error, then we just call
   214  				// the post-apply hook now.
   215  				&EvalIf{
   216  					If: func(ctx EvalContext) (bool, error) {
   217  						return err != nil, nil
   218  					},
   219  
   220  					Then: &EvalApplyPost{
   221  						Addr:  addr.Resource,
   222  						State: &state,
   223  						Error: &err,
   224  					},
   225  				},
   226  
   227  				// Make sure we handle data sources properly.
   228  				&EvalIf{
   229  					If: func(ctx EvalContext) (bool, error) {
   230  						return addr.Resource.Resource.Mode == addrs.DataResourceMode, nil
   231  					},
   232  
   233  					Then: &EvalReadDataApply{
   234  						Addr:           addr.Resource,
   235  						Config:         n.Config,
   236  						Change:         &changeApply,
   237  						Provider:       &provider,
   238  						ProviderAddr:   n.ResolvedProvider,
   239  						ProviderSchema: &providerSchema,
   240  						Output:         &state,
   241  					},
   242  					Else: &EvalApply{
   243  						Addr:           addr.Resource,
   244  						Config:         nil, // No configuration because we are destroying
   245  						State:          &state,
   246  						Change:         &changeApply,
   247  						Provider:       &provider,
   248  						ProviderAddr:   n.ResolvedProvider,
   249  						ProviderSchema: &providerSchema,
   250  						Output:         &state,
   251  						Error:          &err,
   252  					},
   253  				},
   254  				&EvalWriteState{
   255  					Addr:           addr.Resource,
   256  					ProviderAddr:   n.ResolvedProvider,
   257  					ProviderSchema: &providerSchema,
   258  					State:          &state,
   259  				},
   260  				&EvalApplyPost{
   261  					Addr:  addr.Resource,
   262  					State: &state,
   263  					Error: &err,
   264  				},
   265  				&EvalUpdateStateHook{},
   266  			},
   267  		},
   268  	}
   269  }
   270  
   271  // NodeDestroyResourceInstance represents a resource that is to be destroyed.
   272  //
   273  // Destroying a resource is a state-only operation: it is the individual
   274  // instances being destroyed that affects remote objects. During graph
   275  // construction, NodeDestroyResource should always depend on any other node
   276  // related to the given resource, since it's just a final cleanup to avoid
   277  // leaving skeleton resource objects in state after their instances have
   278  // all been destroyed.
   279  type NodeDestroyResource struct {
   280  	*NodeAbstractResource
   281  }
   282  
   283  var (
   284  	_ GraphNodeResource      = (*NodeDestroyResource)(nil)
   285  	_ GraphNodeReferenceable = (*NodeDestroyResource)(nil)
   286  	_ GraphNodeReferencer    = (*NodeDestroyResource)(nil)
   287  	_ GraphNodeEvalable      = (*NodeDestroyResource)(nil)
   288  )
   289  
   290  func (n *NodeDestroyResource) Name() string {
   291  	return n.ResourceAddr().String() + " (clean up state)"
   292  }
   293  
   294  // GraphNodeReferenceable, overriding NodeAbstractResource
   295  func (n *NodeDestroyResource) ReferenceableAddrs() []addrs.Referenceable {
   296  	// NodeDestroyResource doesn't participate in references: the graph
   297  	// builder that created it should ensure directly that it already depends
   298  	// on every other node related to its resource, without relying on
   299  	// references.
   300  	return nil
   301  }
   302  
   303  // GraphNodeReferencer, overriding NodeAbstractResource
   304  func (n *NodeDestroyResource) References() []*addrs.Reference {
   305  	// NodeDestroyResource doesn't participate in references: the graph
   306  	// builder that created it should ensure directly that it already depends
   307  	// on every other node related to its resource, without relying on
   308  	// references.
   309  	return nil
   310  }
   311  
   312  // GraphNodeEvalable
   313  func (n *NodeDestroyResource) EvalTree() EvalNode {
   314  	// This EvalNode will produce an error if the resource isn't already
   315  	// empty by the time it is called, since it should just be pruning the
   316  	// leftover husk of a resource in state after all of the child instances
   317  	// and their objects were destroyed.
   318  	return &EvalForgetResourceState{
   319  		Addr: n.ResourceAddr().Resource,
   320  	}
   321  }