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

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/plans"
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
     9  
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  // NodePlannableResourceInstance represents a _single_ resource
    15  // instance that is plannable. This means this represents a single
    16  // count index, for example.
    17  type NodePlannableResourceInstance struct {
    18  	*NodeAbstractResourceInstance
    19  	ForceCreateBeforeDestroy bool
    20  }
    21  
    22  var (
    23  	_ GraphNodeSubPath              = (*NodePlannableResourceInstance)(nil)
    24  	_ GraphNodeReferenceable        = (*NodePlannableResourceInstance)(nil)
    25  	_ GraphNodeReferencer           = (*NodePlannableResourceInstance)(nil)
    26  	_ GraphNodeResource             = (*NodePlannableResourceInstance)(nil)
    27  	_ GraphNodeResourceInstance     = (*NodePlannableResourceInstance)(nil)
    28  	_ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstance)(nil)
    29  	_ GraphNodeAttachResourceState  = (*NodePlannableResourceInstance)(nil)
    30  	_ GraphNodeEvalable             = (*NodePlannableResourceInstance)(nil)
    31  )
    32  
    33  // GraphNodeEvalable
    34  func (n *NodePlannableResourceInstance) EvalTree() EvalNode {
    35  	addr := n.ResourceInstanceAddr()
    36  
    37  	// Eval info is different depending on what kind of resource this is
    38  	switch addr.Resource.Resource.Mode {
    39  	case addrs.ManagedResourceMode:
    40  		return n.evalTreeManagedResource(addr)
    41  	case addrs.DataResourceMode:
    42  		return n.evalTreeDataResource(addr)
    43  	default:
    44  		panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
    45  	}
    46  }
    47  
    48  func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance) EvalNode {
    49  	config := n.Config
    50  	var provider providers.Interface
    51  	var providerSchema *ProviderSchema
    52  	var change *plans.ResourceInstanceChange
    53  	var state *states.ResourceInstanceObject
    54  	var configVal cty.Value
    55  
    56  	return &EvalSequence{
    57  		Nodes: []EvalNode{
    58  			&EvalGetProvider{
    59  				Addr:   n.ResolvedProvider,
    60  				Output: &provider,
    61  				Schema: &providerSchema,
    62  			},
    63  
    64  			&EvalReadState{
    65  				Addr:           addr.Resource,
    66  				Provider:       &provider,
    67  				ProviderSchema: &providerSchema,
    68  
    69  				Output: &state,
    70  			},
    71  
    72  			// If we already have a non-planned state then we already dealt
    73  			// with this during the refresh walk and so we have nothing to do
    74  			// here.
    75  			&EvalIf{
    76  				If: func(ctx EvalContext) (bool, error) {
    77  					depChanges := false
    78  
    79  					// Check and see if any of our dependencies have changes.
    80  					changes := ctx.Changes()
    81  					for _, d := range n.StateReferences() {
    82  						ri, ok := d.(addrs.ResourceInstance)
    83  						if !ok {
    84  							continue
    85  						}
    86  						change := changes.GetResourceInstanceChange(ri.Absolute(ctx.Path()), states.CurrentGen)
    87  						if change != nil && change.Action != plans.NoOp {
    88  							depChanges = true
    89  							break
    90  						}
    91  					}
    92  
    93  					refreshed := state != nil && state.Status != states.ObjectPlanned
    94  
    95  					// If there are no dependency changes, and it's not a forced
    96  					// read because we there was no Refresh, then we don't need
    97  					// to re-read. If any dependencies have changes, it means
    98  					// our config may also have changes and we need to Read the
    99  					// data source again.
   100  					if !depChanges && refreshed {
   101  						return false, EvalEarlyExitError{}
   102  					}
   103  					return true, nil
   104  				},
   105  				Then: EvalNoop{},
   106  			},
   107  
   108  			&EvalValidateSelfRef{
   109  				Addr:           addr.Resource,
   110  				Config:         config.Config,
   111  				ProviderSchema: &providerSchema,
   112  			},
   113  
   114  			&EvalReadData{
   115  				Addr:           addr.Resource,
   116  				Config:         n.Config,
   117  				Dependencies:   n.StateReferences(),
   118  				Provider:       &provider,
   119  				ProviderAddr:   n.ResolvedProvider,
   120  				ProviderSchema: &providerSchema,
   121  				ForcePlanRead:  true, // _always_ produce a Read change, even if the config seems ready
   122  				OutputChange:   &change,
   123  				OutputValue:    &configVal,
   124  				OutputState:    &state,
   125  			},
   126  
   127  			&EvalWriteState{
   128  				Addr:           addr.Resource,
   129  				ProviderAddr:   n.ResolvedProvider,
   130  				ProviderSchema: &providerSchema,
   131  				State:          &state,
   132  			},
   133  
   134  			&EvalWriteDiff{
   135  				Addr:           addr.Resource,
   136  				ProviderSchema: &providerSchema,
   137  				Change:         &change,
   138  			},
   139  		},
   140  	}
   141  }
   142  
   143  func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance) EvalNode {
   144  	config := n.Config
   145  	var provider providers.Interface
   146  	var providerSchema *ProviderSchema
   147  	var change *plans.ResourceInstanceChange
   148  	var state *states.ResourceInstanceObject
   149  
   150  	return &EvalSequence{
   151  		Nodes: []EvalNode{
   152  			&EvalGetProvider{
   153  				Addr:   n.ResolvedProvider,
   154  				Output: &provider,
   155  				Schema: &providerSchema,
   156  			},
   157  
   158  			&EvalReadState{
   159  				Addr:           addr.Resource,
   160  				Provider:       &provider,
   161  				ProviderSchema: &providerSchema,
   162  
   163  				Output: &state,
   164  			},
   165  
   166  			&EvalValidateSelfRef{
   167  				Addr:           addr.Resource,
   168  				Config:         config.Config,
   169  				ProviderSchema: &providerSchema,
   170  			},
   171  
   172  			&EvalDiff{
   173  				Addr:                addr.Resource,
   174  				Config:              n.Config,
   175  				CreateBeforeDestroy: n.ForceCreateBeforeDestroy,
   176  				Provider:            &provider,
   177  				ProviderAddr:        n.ResolvedProvider,
   178  				ProviderSchema:      &providerSchema,
   179  				State:               &state,
   180  				OutputChange:        &change,
   181  				OutputState:         &state,
   182  			},
   183  			&EvalCheckPreventDestroy{
   184  				Addr:   addr.Resource,
   185  				Config: n.Config,
   186  				Change: &change,
   187  			},
   188  			&EvalWriteState{
   189  				Addr:           addr.Resource,
   190  				ProviderAddr:   n.ResolvedProvider,
   191  				State:          &state,
   192  				ProviderSchema: &providerSchema,
   193  			},
   194  			&EvalWriteDiff{
   195  				Addr:           addr.Resource,
   196  				ProviderSchema: &providerSchema,
   197  				Change:         &change,
   198  			},
   199  		},
   200  	}
   201  }