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

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags"
     8  )
     9  
    10  // NodePlannableResource represents a resource that is "plannable":
    11  // it is ready to be planned in order to create a diff.
    12  type NodePlannableResource struct {
    13  	*NodeAbstractResource
    14  
    15  	// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
    16  	// during graph construction, if dependencies require us to force this
    17  	// on regardless of what the configuration says.
    18  	ForceCreateBeforeDestroy *bool
    19  }
    20  
    21  var (
    22  	_ GraphNodeSubPath              = (*NodePlannableResource)(nil)
    23  	_ GraphNodeDestroyerCBD         = (*NodePlannableResource)(nil)
    24  	_ GraphNodeDynamicExpandable    = (*NodePlannableResource)(nil)
    25  	_ GraphNodeReferenceable        = (*NodePlannableResource)(nil)
    26  	_ GraphNodeReferencer           = (*NodePlannableResource)(nil)
    27  	_ GraphNodeResource             = (*NodePlannableResource)(nil)
    28  	_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
    29  )
    30  
    31  // GraphNodeEvalable
    32  func (n *NodePlannableResource) EvalTree() EvalNode {
    33  	addr := n.ResourceAddr()
    34  	config := n.Config
    35  
    36  	if config == nil {
    37  		// Nothing to do, then.
    38  		log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr)
    39  		return &EvalNoop{}
    40  	}
    41  
    42  	// this ensures we can reference the resource even if the count is 0
    43  	return &EvalWriteResourceState{
    44  		Addr:         addr.Resource,
    45  		Config:       config,
    46  		ProviderAddr: n.ResolvedProvider,
    47  	}
    48  }
    49  
    50  // GraphNodeDestroyerCBD
    51  func (n *NodePlannableResource) CreateBeforeDestroy() bool {
    52  	if n.ForceCreateBeforeDestroy != nil {
    53  		return *n.ForceCreateBeforeDestroy
    54  	}
    55  
    56  	// If we have no config, we just assume no
    57  	if n.Config == nil || n.Config.Managed == nil {
    58  		return false
    59  	}
    60  
    61  	return n.Config.Managed.CreateBeforeDestroy
    62  }
    63  
    64  // GraphNodeDestroyerCBD
    65  func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
    66  	n.ForceCreateBeforeDestroy = &v
    67  	return nil
    68  }
    69  
    70  // GraphNodeDynamicExpandable
    71  func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    72  	var diags tfdiags.Diagnostics
    73  
    74  	count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
    75  	diags = diags.Append(countDiags)
    76  	if countDiags.HasErrors() {
    77  		return nil, diags.Err()
    78  	}
    79  
    80  	forEachMap, forEachDiags := evaluateResourceForEachExpression(n.Config.ForEach, ctx)
    81  	if forEachDiags.HasErrors() {
    82  		return nil, diags.Err()
    83  	}
    84  
    85  	// Next we need to potentially rename an instance address in the state
    86  	// if we're transitioning whether "count" is set at all.
    87  	fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
    88  
    89  	// Our graph transformers require access to the full state, so we'll
    90  	// temporarily lock it while we work on this.
    91  	state := ctx.State().Lock()
    92  	defer ctx.State().Unlock()
    93  
    94  	// The concrete resource factory we'll use
    95  	concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
    96  		// Add the config and state since we don't do that via transforms
    97  		a.Config = n.Config
    98  		a.ResolvedProvider = n.ResolvedProvider
    99  		a.Schema = n.Schema
   100  		a.ProvisionerSchemas = n.ProvisionerSchemas
   101  
   102  		return &NodePlannableResourceInstance{
   103  			NodeAbstractResourceInstance: a,
   104  
   105  			// By the time we're walking, we've figured out whether we need
   106  			// to force on CreateBeforeDestroy due to dependencies on other
   107  			// nodes that have it.
   108  			ForceCreateBeforeDestroy: n.CreateBeforeDestroy(),
   109  		}
   110  	}
   111  
   112  	// The concrete resource factory we'll use for orphans
   113  	concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
   114  		// Add the config and state since we don't do that via transforms
   115  		a.Config = n.Config
   116  		a.ResolvedProvider = n.ResolvedProvider
   117  		a.Schema = n.Schema
   118  		a.ProvisionerSchemas = n.ProvisionerSchemas
   119  
   120  		return &NodePlannableResourceInstanceOrphan{
   121  			NodeAbstractResourceInstance: a,
   122  		}
   123  	}
   124  
   125  	// Start creating the steps
   126  	steps := []GraphTransformer{
   127  		// Expand the count or for_each (if present)
   128  		&ResourceCountTransformer{
   129  			Concrete: concreteResource,
   130  			Schema:   n.Schema,
   131  			Count:    count,
   132  			ForEach:  forEachMap,
   133  			Addr:     n.ResourceAddr(),
   134  		},
   135  
   136  		// Add the count/for_each orphans
   137  		&OrphanResourceCountTransformer{
   138  			Concrete: concreteResourceOrphan,
   139  			Count:    count,
   140  			ForEach:  forEachMap,
   141  			Addr:     n.ResourceAddr(),
   142  			State:    state,
   143  		},
   144  
   145  		// Attach the state
   146  		&AttachStateTransformer{State: state},
   147  
   148  		// Targeting
   149  		&TargetsTransformer{Targets: n.Targets},
   150  
   151  		// Connect references so ordering is correct
   152  		&ReferenceTransformer{},
   153  
   154  		// Make sure there is a single root
   155  		&RootTransformer{},
   156  	}
   157  
   158  	// Build the graph
   159  	b := &BasicGraphBuilder{
   160  		Steps:    steps,
   161  		Validate: true,
   162  		Name:     "NodePlannableResource",
   163  	}
   164  	graph, diags := b.Build(ctx.Path())
   165  	return graph, diags.ErrWithWarnings()
   166  }