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

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/plans"
     9  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
    11  )
    12  
    13  // ConcreteResourceInstanceDeposedNodeFunc is a callback type used to convert
    14  // an abstract resource instance to a concrete one of some type that has
    15  // an associated deposed object key.
    16  type ConcreteResourceInstanceDeposedNodeFunc func(*NodeAbstractResourceInstance, states.DeposedKey) dag.Vertex
    17  
    18  type GraphNodeDeposedResourceInstanceObject interface {
    19  	DeposedInstanceObjectKey() states.DeposedKey
    20  }
    21  
    22  // NodePlanDeposedResourceInstanceObject represents deposed resource
    23  // instance objects during plan. These are distinct from the primary object
    24  // for each resource instance since the only valid operation to do with them
    25  // is to destroy them.
    26  //
    27  // This node type is also used during the refresh walk to ensure that the
    28  // record of a deposed object is up-to-date before we plan to destroy it.
    29  type NodePlanDeposedResourceInstanceObject struct {
    30  	*NodeAbstractResourceInstance
    31  	DeposedKey states.DeposedKey
    32  }
    33  
    34  var (
    35  	_ GraphNodeDeposedResourceInstanceObject = (*NodePlanDeposedResourceInstanceObject)(nil)
    36  	_ GraphNodeResource                      = (*NodePlanDeposedResourceInstanceObject)(nil)
    37  	_ GraphNodeResourceInstance              = (*NodePlanDeposedResourceInstanceObject)(nil)
    38  	_ GraphNodeReferenceable                 = (*NodePlanDeposedResourceInstanceObject)(nil)
    39  	_ GraphNodeReferencer                    = (*NodePlanDeposedResourceInstanceObject)(nil)
    40  	_ GraphNodeEvalable                      = (*NodePlanDeposedResourceInstanceObject)(nil)
    41  	_ GraphNodeProviderConsumer              = (*NodePlanDeposedResourceInstanceObject)(nil)
    42  	_ GraphNodeProvisionerConsumer           = (*NodePlanDeposedResourceInstanceObject)(nil)
    43  )
    44  
    45  func (n *NodePlanDeposedResourceInstanceObject) Name() string {
    46  	return fmt.Sprintf("%s (deposed %s)", n.ResourceInstanceAddr().String(), n.DeposedKey)
    47  }
    48  
    49  func (n *NodePlanDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey {
    50  	return n.DeposedKey
    51  }
    52  
    53  // GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance
    54  func (n *NodePlanDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable {
    55  	// Deposed objects don't participate in references.
    56  	return nil
    57  }
    58  
    59  // GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance
    60  func (n *NodePlanDeposedResourceInstanceObject) References() []*addrs.Reference {
    61  	// We don't evaluate configuration for deposed objects, so they effectively
    62  	// make no references.
    63  	return nil
    64  }
    65  
    66  // GraphNodeEvalable impl.
    67  func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode {
    68  	addr := n.ResourceInstanceAddr()
    69  
    70  	var provider providers.Interface
    71  	var providerSchema *ProviderSchema
    72  	var state *states.ResourceInstanceObject
    73  
    74  	seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
    75  
    76  	// During the refresh walk we will ensure that our record of the deposed
    77  	// object is up-to-date. If it was already deleted outside of Terraform
    78  	// then this will remove it from state and thus avoid us planning a
    79  	// destroy for it during the subsequent plan walk.
    80  	seq.Nodes = append(seq.Nodes, &EvalOpFilter{
    81  		Ops: []walkOperation{walkRefresh},
    82  		Node: &EvalSequence{
    83  			Nodes: []EvalNode{
    84  				&EvalGetProvider{
    85  					Addr:   n.ResolvedProvider,
    86  					Output: &provider,
    87  					Schema: &providerSchema,
    88  				},
    89  				&EvalReadStateDeposed{
    90  					Addr:           addr.Resource,
    91  					Provider:       &provider,
    92  					ProviderSchema: &providerSchema,
    93  					Key:            n.DeposedKey,
    94  					Output:         &state,
    95  				},
    96  				&EvalRefresh{
    97  					Addr:           addr.Resource,
    98  					ProviderAddr:   n.ResolvedProvider,
    99  					Provider:       &provider,
   100  					ProviderSchema: &providerSchema,
   101  					State:          &state,
   102  					Output:         &state,
   103  				},
   104  				&EvalWriteStateDeposed{
   105  					Addr:           addr.Resource,
   106  					Key:            n.DeposedKey,
   107  					ProviderAddr:   n.ResolvedProvider,
   108  					ProviderSchema: &providerSchema,
   109  					State:          &state,
   110  				},
   111  			},
   112  		},
   113  	})
   114  
   115  	// During the plan walk we always produce a planned destroy change, because
   116  	// destroying is the only supported action for deposed objects.
   117  	var change *plans.ResourceInstanceChange
   118  	seq.Nodes = append(seq.Nodes, &EvalOpFilter{
   119  		Ops: []walkOperation{walkPlan, walkPlanDestroy},
   120  		Node: &EvalSequence{
   121  			Nodes: []EvalNode{
   122  				&EvalGetProvider{
   123  					Addr:   n.ResolvedProvider,
   124  					Output: &provider,
   125  					Schema: &providerSchema,
   126  				},
   127  				&EvalReadStateDeposed{
   128  					Addr:           addr.Resource,
   129  					Output:         &state,
   130  					Key:            n.DeposedKey,
   131  					Provider:       &provider,
   132  					ProviderSchema: &providerSchema,
   133  				},
   134  				&EvalDiffDestroy{
   135  					Addr:         addr.Resource,
   136  					ProviderAddr: n.ResolvedProvider,
   137  					DeposedKey:   n.DeposedKey,
   138  					State:        &state,
   139  					Output:       &change,
   140  				},
   141  				&EvalWriteDiff{
   142  					Addr:           addr.Resource,
   143  					DeposedKey:     n.DeposedKey,
   144  					ProviderSchema: &providerSchema,
   145  					Change:         &change,
   146  				},
   147  				// Since deposed objects cannot be referenced by expressions
   148  				// elsewhere, we don't need to also record the planned new
   149  				// state in this case.
   150  			},
   151  		},
   152  	})
   153  
   154  	return seq
   155  }
   156  
   157  // NodeDestroyDeposedResourceInstanceObject represents deposed resource
   158  // instance objects during apply. Nodes of this type are inserted by
   159  // DiffTransformer when the planned changeset contains "delete" changes for
   160  // deposed instance objects, and its only supported operation is to destroy
   161  // and then forget the associated object.
   162  type NodeDestroyDeposedResourceInstanceObject struct {
   163  	*NodeAbstractResourceInstance
   164  	DeposedKey states.DeposedKey
   165  }
   166  
   167  var (
   168  	_ GraphNodeDeposedResourceInstanceObject = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   169  	_ GraphNodeResource                      = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   170  	_ GraphNodeResourceInstance              = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   171  	_ GraphNodeDestroyer                     = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   172  	_ GraphNodeDestroyerCBD                  = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   173  	_ GraphNodeReferenceable                 = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   174  	_ GraphNodeReferencer                    = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   175  	_ GraphNodeEvalable                      = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   176  	_ GraphNodeProviderConsumer              = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   177  	_ GraphNodeProvisionerConsumer           = (*NodeDestroyDeposedResourceInstanceObject)(nil)
   178  )
   179  
   180  func (n *NodeDestroyDeposedResourceInstanceObject) Name() string {
   181  	return fmt.Sprintf("%s (destroy deposed %s)", n.Addr.String(), n.DeposedKey)
   182  }
   183  
   184  func (n *NodeDestroyDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey {
   185  	return n.DeposedKey
   186  }
   187  
   188  // GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance
   189  func (n *NodeDestroyDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable {
   190  	// Deposed objects don't participate in references.
   191  	return nil
   192  }
   193  
   194  // GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance
   195  func (n *NodeDestroyDeposedResourceInstanceObject) References() []*addrs.Reference {
   196  	// We don't evaluate configuration for deposed objects, so they effectively
   197  	// make no references.
   198  	return nil
   199  }
   200  
   201  // GraphNodeDestroyer
   202  func (n *NodeDestroyDeposedResourceInstanceObject) DestroyAddr() *addrs.AbsResourceInstance {
   203  	addr := n.ResourceInstanceAddr()
   204  	return &addr
   205  }
   206  
   207  // GraphNodeDestroyerCBD
   208  func (n *NodeDestroyDeposedResourceInstanceObject) CreateBeforeDestroy() bool {
   209  	// A deposed instance is always CreateBeforeDestroy by definition, since
   210  	// we use deposed only to handle create-before-destroy.
   211  	return true
   212  }
   213  
   214  // GraphNodeDestroyerCBD
   215  func (n *NodeDestroyDeposedResourceInstanceObject) ModifyCreateBeforeDestroy(v bool) error {
   216  	if !v {
   217  		// Should never happen: deposed instances are _always_ create_before_destroy.
   218  		return fmt.Errorf("can't deactivate create_before_destroy for a deposed instance")
   219  	}
   220  	return nil
   221  }
   222  
   223  // GraphNodeEvalable impl.
   224  func (n *NodeDestroyDeposedResourceInstanceObject) EvalTree() EvalNode {
   225  	addr := n.ResourceInstanceAddr()
   226  
   227  	var provider providers.Interface
   228  	var providerSchema *ProviderSchema
   229  	var state *states.ResourceInstanceObject
   230  	var change *plans.ResourceInstanceChange
   231  	var err error
   232  
   233  	return &EvalSequence{
   234  		Nodes: []EvalNode{
   235  			&EvalGetProvider{
   236  				Addr:   n.ResolvedProvider,
   237  				Output: &provider,
   238  				Schema: &providerSchema,
   239  			},
   240  			&EvalReadStateDeposed{
   241  				Addr:           addr.Resource,
   242  				Output:         &state,
   243  				Key:            n.DeposedKey,
   244  				Provider:       &provider,
   245  				ProviderSchema: &providerSchema,
   246  			},
   247  			&EvalDiffDestroy{
   248  				Addr:         addr.Resource,
   249  				ProviderAddr: n.ResolvedProvider,
   250  				State:        &state,
   251  				Output:       &change,
   252  			},
   253  			// Call pre-apply hook
   254  			&EvalApplyPre{
   255  				Addr:   addr.Resource,
   256  				State:  &state,
   257  				Change: &change,
   258  			},
   259  			&EvalApply{
   260  				Addr:           addr.Resource,
   261  				Config:         nil, // No configuration because we are destroying
   262  				State:          &state,
   263  				Change:         &change,
   264  				Provider:       &provider,
   265  				ProviderAddr:   n.ResolvedProvider,
   266  				ProviderSchema: &providerSchema,
   267  				Output:         &state,
   268  				Error:          &err,
   269  			},
   270  			// Always write the resource back to the state deposed... if it
   271  			// was successfully destroyed it will be pruned. If it was not, it will
   272  			// be caught on the next run.
   273  			&EvalWriteStateDeposed{
   274  				Addr:           addr.Resource,
   275  				Key:            n.DeposedKey,
   276  				ProviderAddr:   n.ResolvedProvider,
   277  				ProviderSchema: &providerSchema,
   278  				State:          &state,
   279  			},
   280  			&EvalApplyPost{
   281  				Addr:  addr.Resource,
   282  				State: &state,
   283  				Error: &err,
   284  			},
   285  			&EvalReturnError{
   286  				Error: &err,
   287  			},
   288  			&EvalUpdateStateHook{},
   289  		},
   290  	}
   291  }
   292  
   293  // GraphNodeDeposer is an optional interface implemented by graph nodes that
   294  // might create a single new deposed object for a specific associated resource
   295  // instance, allowing a caller to optionally pre-allocate a DeposedKey for
   296  // it.
   297  type GraphNodeDeposer interface {
   298  	// SetPreallocatedDeposedKey will be called during graph construction
   299  	// if a particular node must use a pre-allocated deposed key if/when it
   300  	// "deposes" the current object of its associated resource instance.
   301  	SetPreallocatedDeposedKey(key states.DeposedKey)
   302  }
   303  
   304  // graphNodeDeposer is an embeddable implementation of GraphNodeDeposer.
   305  // Embed it in a node type to get automatic support for it, and then access
   306  // the field PreallocatedDeposedKey to access any pre-allocated key.
   307  type graphNodeDeposer struct {
   308  	PreallocatedDeposedKey states.DeposedKey
   309  }
   310  
   311  func (n *graphNodeDeposer) SetPreallocatedDeposedKey(key states.DeposedKey) {
   312  	n.PreallocatedDeposedKey = key
   313  }