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

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/muratcelep/terraform/not-internal/addrs"
     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  
    13  // NodePlannableResourceInstanceOrphan represents a resource that is "applyable":
    14  // it is ready to be applied and is represented by a diff.
    15  type NodePlannableResourceInstanceOrphan struct {
    16  	*NodeAbstractResourceInstance
    17  
    18  	// skipRefresh indicates that we should skip refreshing individual instances
    19  	skipRefresh bool
    20  
    21  	// skipPlanChanges indicates we should skip trying to plan change actions
    22  	// for any instances.
    23  	skipPlanChanges bool
    24  }
    25  
    26  var (
    27  	_ GraphNodeModuleInstance       = (*NodePlannableResourceInstanceOrphan)(nil)
    28  	_ GraphNodeReferenceable        = (*NodePlannableResourceInstanceOrphan)(nil)
    29  	_ GraphNodeReferencer           = (*NodePlannableResourceInstanceOrphan)(nil)
    30  	_ GraphNodeConfigResource       = (*NodePlannableResourceInstanceOrphan)(nil)
    31  	_ GraphNodeResourceInstance     = (*NodePlannableResourceInstanceOrphan)(nil)
    32  	_ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstanceOrphan)(nil)
    33  	_ GraphNodeAttachResourceState  = (*NodePlannableResourceInstanceOrphan)(nil)
    34  	_ GraphNodeExecutable           = (*NodePlannableResourceInstanceOrphan)(nil)
    35  	_ GraphNodeProviderConsumer     = (*NodePlannableResourceInstanceOrphan)(nil)
    36  )
    37  
    38  func (n *NodePlannableResourceInstanceOrphan) Name() string {
    39  	return n.ResourceInstanceAddr().String() + " (orphan)"
    40  }
    41  
    42  // GraphNodeExecutable
    43  func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics {
    44  	addr := n.ResourceInstanceAddr()
    45  
    46  	// Eval info is different depending on what kind of resource this is
    47  	switch addr.Resource.Resource.Mode {
    48  	case addrs.ManagedResourceMode:
    49  		return n.managedResourceExecute(ctx)
    50  	case addrs.DataResourceMode:
    51  		return n.dataResourceExecute(ctx)
    52  	default:
    53  		panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
    54  	}
    55  }
    56  
    57  func (n *NodePlannableResourceInstanceOrphan) ProvidedBy() (addr addrs.ProviderConfig, exact bool) {
    58  	if n.Addr.Resource.Resource.Mode == addrs.DataResourceMode {
    59  		// indicate that this node does not require a configured provider
    60  		return nil, true
    61  	}
    62  	return n.NodeAbstractResourceInstance.ProvidedBy()
    63  }
    64  
    65  func (n *NodePlannableResourceInstanceOrphan) dataResourceExecute(ctx EvalContext) tfdiags.Diagnostics {
    66  	// A data source that is no longer in the config is removed from the state
    67  	log.Printf("[TRACE] NodePlannableResourceInstanceOrphan: removing state object for %s", n.Addr)
    68  
    69  	// we need to update both the refresh state to refresh the current data
    70  	// source, and the working state for plan-time evaluations.
    71  	refreshState := ctx.RefreshState()
    72  	refreshState.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
    73  
    74  	workingState := ctx.State()
    75  	workingState.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
    76  	return nil
    77  }
    78  
    79  func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) {
    80  	addr := n.ResourceInstanceAddr()
    81  
    82  	oldState, readDiags := n.readResourceInstanceState(ctx, addr)
    83  	diags = diags.Append(readDiags)
    84  	if diags.HasErrors() {
    85  		return diags
    86  	}
    87  
    88  	// Note any upgrades that readResourceInstanceState might've done in the
    89  	// prevRunState, so that it'll conform to current schema.
    90  	diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, prevRunState))
    91  	if diags.HasErrors() {
    92  		return diags
    93  	}
    94  	// Also the refreshState, because that should still reflect schema upgrades
    95  	// even if not refreshing.
    96  	diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, refreshState))
    97  	if diags.HasErrors() {
    98  		return diags
    99  	}
   100  
   101  	if !n.skipRefresh {
   102  		// Refresh this instance even though it is going to be destroyed, in
   103  		// order to catch missing resources. If this is a normal plan,
   104  		// providers expect a Read request to remove missing resources from the
   105  		// plan before apply, and may not handle a missing resource during
   106  		// Delete correctly.  If this is a simple refresh, Terraform is
   107  		// expected to remove the missing resource from the state entirely
   108  		refreshedState, refreshDiags := n.refresh(ctx, states.NotDeposed, oldState)
   109  		diags = diags.Append(refreshDiags)
   110  		if diags.HasErrors() {
   111  			return diags
   112  		}
   113  
   114  		diags = diags.Append(n.writeResourceInstanceState(ctx, refreshedState, refreshState))
   115  		if diags.HasErrors() {
   116  			return diags
   117  		}
   118  
   119  		// If we refreshed then our subsequent planning should be in terms of
   120  		// the new object, not the original object.
   121  		oldState = refreshedState
   122  	}
   123  
   124  	if !n.skipPlanChanges {
   125  		var change *plans.ResourceInstanceChange
   126  		change, destroyPlanDiags := n.planDestroy(ctx, oldState, "")
   127  		diags = diags.Append(destroyPlanDiags)
   128  		if diags.HasErrors() {
   129  			return diags
   130  		}
   131  
   132  		diags = diags.Append(n.checkPreventDestroy(change))
   133  		if diags.HasErrors() {
   134  			return diags
   135  		}
   136  
   137  		// We might be able to offer an approximate reason for why we are
   138  		// planning to delete this object. (This is best-effort; we might
   139  		// sometimes not have a reason.)
   140  		change.ActionReason = n.deleteActionReason(ctx)
   141  
   142  		diags = diags.Append(n.writeChange(ctx, change, ""))
   143  		if diags.HasErrors() {
   144  			return diags
   145  		}
   146  
   147  		diags = diags.Append(n.writeResourceInstanceState(ctx, nil, workingState))
   148  	} else {
   149  		// The working state should at least be updated with the result
   150  		// of upgrading and refreshing from above.
   151  		diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, workingState))
   152  	}
   153  
   154  	return diags
   155  }
   156  
   157  func (n *NodePlannableResourceInstanceOrphan) deleteActionReason(ctx EvalContext) plans.ResourceInstanceChangeActionReason {
   158  	cfg := n.Config
   159  	if cfg == nil {
   160  		// NOTE: We'd ideally detect if the containing module is what's missing
   161  		// and then use ResourceInstanceDeleteBecauseNoModule for that case,
   162  		// but we don't currently have access to the full configuration here,
   163  		// so we need to be less specific.
   164  		return plans.ResourceInstanceDeleteBecauseNoResourceConfig
   165  	}
   166  
   167  	switch n.Addr.Resource.Key.(type) {
   168  	case nil: // no instance key at all
   169  		if cfg.Count != nil || cfg.ForEach != nil {
   170  			return plans.ResourceInstanceDeleteBecauseWrongRepetition
   171  		}
   172  	case addrs.IntKey:
   173  		if cfg.Count == nil {
   174  			// This resource isn't using "count" at all, then
   175  			return plans.ResourceInstanceDeleteBecauseWrongRepetition
   176  		}
   177  
   178  		expander := ctx.InstanceExpander()
   179  		if expander == nil {
   180  			break // only for tests that produce an incomplete MockEvalContext
   181  		}
   182  		insts := expander.ExpandResource(n.Addr.ContainingResource())
   183  
   184  		declared := false
   185  		for _, inst := range insts {
   186  			if n.Addr.Equal(inst) {
   187  				declared = true
   188  			}
   189  		}
   190  		if !declared {
   191  			// This instance key is outside of the configured range
   192  			return plans.ResourceInstanceDeleteBecauseCountIndex
   193  		}
   194  	case addrs.StringKey:
   195  		if cfg.ForEach == nil {
   196  			// This resource isn't using "for_each" at all, then
   197  			return plans.ResourceInstanceDeleteBecauseWrongRepetition
   198  		}
   199  
   200  		expander := ctx.InstanceExpander()
   201  		if expander == nil {
   202  			break // only for tests that produce an incomplete MockEvalContext
   203  		}
   204  		insts := expander.ExpandResource(n.Addr.ContainingResource())
   205  
   206  		declared := false
   207  		for _, inst := range insts {
   208  			if n.Addr.Equal(inst) {
   209  				declared = true
   210  			}
   211  		}
   212  		if !declared {
   213  			// This instance key is outside of the configured range
   214  			return plans.ResourceInstanceDeleteBecauseEachKey
   215  		}
   216  	}
   217  
   218  	// If we get here then the instance key type matches the configured
   219  	// repetition mode, and so we need to consider whether the key itself
   220  	// is within the range of the repetition construct.
   221  	if expander := ctx.InstanceExpander(); expander != nil { // (sometimes nil in MockEvalContext in tests)
   222  		// First we'll check whether our containing module instance still
   223  		// exists, so we can talk about that differently in the reason.
   224  		declared := false
   225  		for _, inst := range expander.ExpandModule(n.Addr.Module.Module()) {
   226  			if n.Addr.Module.Equal(inst) {
   227  				declared = true
   228  				break
   229  			}
   230  		}
   231  		if !declared {
   232  			return plans.ResourceInstanceDeleteBecauseNoModule
   233  		}
   234  
   235  		// Now we've proven that we're in a still-existing module instance,
   236  		// we'll see if our instance key matches something actually declared.
   237  		declared = false
   238  		for _, inst := range expander.ExpandResource(n.Addr.ContainingResource()) {
   239  			if n.Addr.Equal(inst) {
   240  				declared = true
   241  				break
   242  			}
   243  		}
   244  		if !declared {
   245  			// Because we already checked that the key _type_ was correct
   246  			// above, we can assume that any mismatch here is a range error,
   247  			// and thus we just need to decide which of the two range
   248  			// errors we're going to return.
   249  			switch n.Addr.Resource.Key.(type) {
   250  			case addrs.IntKey:
   251  				return plans.ResourceInstanceDeleteBecauseCountIndex
   252  			case addrs.StringKey:
   253  				return plans.ResourceInstanceDeleteBecauseEachKey
   254  			}
   255  		}
   256  	}
   257  
   258  	// If we didn't find any specific reason to report, we'll report "no reason"
   259  	// as a fallback, which means the UI should just state it'll be deleted
   260  	// without any explicit reasoning.
   261  	return plans.ResourceInstanceChangeNoReason
   262  }