github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/node_resource_plan_instance.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sort"
     7  
     8  	"github.com/muratcelep/terraform/not-internal/plans"
     9  	"github.com/muratcelep/terraform/not-internal/states"
    10  	"github.com/muratcelep/terraform/not-internal/tfdiags"
    11  
    12  	"github.com/muratcelep/terraform/not-internal/addrs"
    13  )
    14  
    15  // NodePlannableResourceInstance represents a _single_ resource
    16  // instance that is plannable. This means this represents a single
    17  // count index, for example.
    18  type NodePlannableResourceInstance struct {
    19  	*NodeAbstractResourceInstance
    20  	ForceCreateBeforeDestroy bool
    21  
    22  	// skipRefresh indicates that we should skip refreshing individual instances
    23  	skipRefresh bool
    24  
    25  	// skipPlanChanges indicates we should skip trying to plan change actions
    26  	// for any instances.
    27  	skipPlanChanges bool
    28  
    29  	// forceReplace are resource instance addresses where the user wants to
    30  	// force generating a replace action. This set isn't pre-filtered, so
    31  	// it might contain addresses that have nothing to do with the resource
    32  	// that this node represents, which the node itself must therefore ignore.
    33  	forceReplace []addrs.AbsResourceInstance
    34  }
    35  
    36  var (
    37  	_ GraphNodeModuleInstance       = (*NodePlannableResourceInstance)(nil)
    38  	_ GraphNodeReferenceable        = (*NodePlannableResourceInstance)(nil)
    39  	_ GraphNodeReferencer           = (*NodePlannableResourceInstance)(nil)
    40  	_ GraphNodeConfigResource       = (*NodePlannableResourceInstance)(nil)
    41  	_ GraphNodeResourceInstance     = (*NodePlannableResourceInstance)(nil)
    42  	_ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstance)(nil)
    43  	_ GraphNodeAttachResourceState  = (*NodePlannableResourceInstance)(nil)
    44  	_ GraphNodeExecutable           = (*NodePlannableResourceInstance)(nil)
    45  )
    46  
    47  // GraphNodeEvalable
    48  func (n *NodePlannableResourceInstance) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics {
    49  	addr := n.ResourceInstanceAddr()
    50  
    51  	// Eval info is different depending on what kind of resource this is
    52  	switch addr.Resource.Resource.Mode {
    53  	case addrs.ManagedResourceMode:
    54  		return n.managedResourceExecute(ctx)
    55  	case addrs.DataResourceMode:
    56  		return n.dataResourceExecute(ctx)
    57  	default:
    58  		panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
    59  	}
    60  }
    61  
    62  func (n *NodePlannableResourceInstance) dataResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) {
    63  	config := n.Config
    64  	addr := n.ResourceInstanceAddr()
    65  
    66  	var change *plans.ResourceInstanceChange
    67  
    68  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
    69  	diags = diags.Append(err)
    70  	if diags.HasErrors() {
    71  		return diags
    72  	}
    73  
    74  	state, readDiags := n.readResourceInstanceState(ctx, addr)
    75  	diags = diags.Append(readDiags)
    76  	if diags.HasErrors() {
    77  		return diags
    78  	}
    79  
    80  	// We'll save a snapshot of what we just read from the state into the
    81  	// prevRunState which will capture the result read in the previous
    82  	// run, possibly tweaked by any upgrade steps that
    83  	// readResourceInstanceState might've made.
    84  	// However, note that we don't have any explicit mechanism for upgrading
    85  	// data resource results as we do for managed resources, and so the
    86  	// prevRunState might not conform to the current schema if the
    87  	// previous run was with a different provider version.
    88  	diags = diags.Append(n.writeResourceInstanceState(ctx, state, prevRunState))
    89  	if diags.HasErrors() {
    90  		return diags
    91  	}
    92  
    93  	diags = diags.Append(validateSelfRef(addr.Resource, config.Config, providerSchema))
    94  	if diags.HasErrors() {
    95  		return diags
    96  	}
    97  
    98  	change, state, planDiags := n.planDataSource(ctx, state)
    99  	diags = diags.Append(planDiags)
   100  	if diags.HasErrors() {
   101  		return diags
   102  	}
   103  
   104  	// write the data source into both the refresh state and the
   105  	// working state
   106  	diags = diags.Append(n.writeResourceInstanceState(ctx, state, refreshState))
   107  	if diags.HasErrors() {
   108  		return diags
   109  	}
   110  	diags = diags.Append(n.writeResourceInstanceState(ctx, state, workingState))
   111  	if diags.HasErrors() {
   112  		return diags
   113  	}
   114  
   115  	diags = diags.Append(n.writeChange(ctx, change, ""))
   116  	return diags
   117  }
   118  
   119  func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) {
   120  	config := n.Config
   121  	addr := n.ResourceInstanceAddr()
   122  
   123  	var change *plans.ResourceInstanceChange
   124  	var instanceRefreshState *states.ResourceInstanceObject
   125  
   126  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   127  	diags = diags.Append(err)
   128  	if diags.HasErrors() {
   129  		return diags
   130  	}
   131  
   132  	diags = diags.Append(validateSelfRef(addr.Resource, config.Config, providerSchema))
   133  	if diags.HasErrors() {
   134  		return diags
   135  	}
   136  
   137  	instanceRefreshState, readDiags := n.readResourceInstanceState(ctx, addr)
   138  	diags = diags.Append(readDiags)
   139  	if diags.HasErrors() {
   140  		return diags
   141  	}
   142  
   143  	// We'll save a snapshot of what we just read from the state into the
   144  	// prevRunState before we do anything else, since this will capture the
   145  	// result of any schema upgrading that readResourceInstanceState just did,
   146  	// but not include any out-of-band changes we might detect in in the
   147  	// refresh step below.
   148  	diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, prevRunState))
   149  	if diags.HasErrors() {
   150  		return diags
   151  	}
   152  	// Also the refreshState, because that should still reflect schema upgrades
   153  	// even if it doesn't reflect upstream changes.
   154  	diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState))
   155  	if diags.HasErrors() {
   156  		return diags
   157  	}
   158  
   159  	// In 0.13 we could be refreshing a resource with no config.
   160  	// We should be operating on managed resource, but check here to be certain
   161  	if n.Config == nil || n.Config.Managed == nil {
   162  		log.Printf("[WARN] managedResourceExecute: no Managed config value found in instance state for %q", n.Addr)
   163  	} else {
   164  		if instanceRefreshState != nil {
   165  			instanceRefreshState.CreateBeforeDestroy = n.Config.Managed.CreateBeforeDestroy || n.ForceCreateBeforeDestroy
   166  		}
   167  	}
   168  
   169  	// Refresh, maybe
   170  	if !n.skipRefresh {
   171  		s, refreshDiags := n.refresh(ctx, states.NotDeposed, instanceRefreshState)
   172  		diags = diags.Append(refreshDiags)
   173  		if diags.HasErrors() {
   174  			return diags
   175  		}
   176  
   177  		instanceRefreshState = s
   178  
   179  		if instanceRefreshState != nil {
   180  			// When refreshing we start by merging the stored dependencies and
   181  			// the configured dependencies. The configured dependencies will be
   182  			// stored to state once the changes are applied. If the plan
   183  			// results in no changes, we will re-write these dependencies
   184  			// below.
   185  			instanceRefreshState.Dependencies = mergeDeps(n.Dependencies, instanceRefreshState.Dependencies)
   186  		}
   187  
   188  		diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState))
   189  		if diags.HasErrors() {
   190  			return diags
   191  		}
   192  	}
   193  
   194  	// Plan the instance, unless we're in the refresh-only mode
   195  	if !n.skipPlanChanges {
   196  		change, instancePlanState, planDiags := n.plan(
   197  			ctx, change, instanceRefreshState, n.ForceCreateBeforeDestroy, n.forceReplace,
   198  		)
   199  		diags = diags.Append(planDiags)
   200  		if diags.HasErrors() {
   201  			return diags
   202  		}
   203  
   204  		diags = diags.Append(n.checkPreventDestroy(change))
   205  		if diags.HasErrors() {
   206  			return diags
   207  		}
   208  
   209  		diags = diags.Append(n.writeResourceInstanceState(ctx, instancePlanState, workingState))
   210  		if diags.HasErrors() {
   211  			return diags
   212  		}
   213  
   214  		// If this plan resulted in a NoOp, then apply won't have a chance to make
   215  		// any changes to the stored dependencies. Since this is a NoOp we know
   216  		// that the stored dependencies will have no effect during apply, and we can
   217  		// write them out now.
   218  		if change.Action == plans.NoOp && !depsEqual(instanceRefreshState.Dependencies, n.Dependencies) {
   219  			// the refresh state will be the final state for this resource, so
   220  			// finalize the dependencies here if they need to be updated.
   221  			instanceRefreshState.Dependencies = n.Dependencies
   222  			diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState))
   223  			if diags.HasErrors() {
   224  				return diags
   225  			}
   226  		}
   227  
   228  		diags = diags.Append(n.writeChange(ctx, change, ""))
   229  	} else {
   230  		// Even if we don't plan changes, we do still need to at least update
   231  		// the working state to reflect the refresh result. If not, then e.g.
   232  		// any output values refering to this will not react to the drift.
   233  		// (Even if we didn't actually refresh above, this will still save
   234  		// the result of any schema upgrading we did in readResourceInstanceState.)
   235  		diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, workingState))
   236  		if diags.HasErrors() {
   237  			return diags
   238  		}
   239  	}
   240  
   241  	return diags
   242  }
   243  
   244  // mergeDeps returns the union of 2 sets of dependencies
   245  func mergeDeps(a, b []addrs.ConfigResource) []addrs.ConfigResource {
   246  	switch {
   247  	case len(a) == 0:
   248  		return b
   249  	case len(b) == 0:
   250  		return a
   251  	}
   252  
   253  	set := make(map[string]addrs.ConfigResource)
   254  
   255  	for _, dep := range a {
   256  		set[dep.String()] = dep
   257  	}
   258  
   259  	for _, dep := range b {
   260  		set[dep.String()] = dep
   261  	}
   262  
   263  	newDeps := make([]addrs.ConfigResource, 0, len(set))
   264  	for _, dep := range set {
   265  		newDeps = append(newDeps, dep)
   266  	}
   267  
   268  	return newDeps
   269  }
   270  
   271  func depsEqual(a, b []addrs.ConfigResource) bool {
   272  	if len(a) != len(b) {
   273  		return false
   274  	}
   275  
   276  	less := func(s []addrs.ConfigResource) func(i, j int) bool {
   277  		return func(i, j int) bool {
   278  			return s[i].String() < s[j].String()
   279  		}
   280  	}
   281  
   282  	sort.Slice(a, less(a))
   283  	sort.Slice(b, less(b))
   284  
   285  	for i := range a {
   286  		if !a[i].Equal(b[i]) {
   287  			return false
   288  		}
   289  	}
   290  	return true
   291  }