github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/node_resource_plan_orphan.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/internal/addrs"
     8  	"github.com/hashicorp/terraform/internal/plans"
     9  	"github.com/hashicorp/terraform/internal/states"
    10  	"github.com/hashicorp/terraform/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 we're skipping planning, all we need to do is write the state. If the
   125  	// refresh indicates the instance no longer exists, there is also nothing
   126  	// to plan because there is no longer any state and it doesn't exist in the
   127  	// config.
   128  	if n.skipPlanChanges || oldState == nil || oldState.Value.IsNull() {
   129  		return diags.Append(n.writeResourceInstanceState(ctx, oldState, workingState))
   130  	}
   131  
   132  	var change *plans.ResourceInstanceChange
   133  	change, destroyPlanDiags := n.planDestroy(ctx, oldState, "")
   134  	diags = diags.Append(destroyPlanDiags)
   135  	if diags.HasErrors() {
   136  		return diags
   137  	}
   138  
   139  	diags = diags.Append(n.checkPreventDestroy(change))
   140  	if diags.HasErrors() {
   141  		return diags
   142  	}
   143  
   144  	// We might be able to offer an approximate reason for why we are
   145  	// planning to delete this object. (This is best-effort; we might
   146  	// sometimes not have a reason.)
   147  	change.ActionReason = n.deleteActionReason(ctx)
   148  
   149  	diags = diags.Append(n.writeChange(ctx, change, ""))
   150  	if diags.HasErrors() {
   151  		return diags
   152  	}
   153  
   154  	return diags.Append(n.writeResourceInstanceState(ctx, nil, workingState))
   155  }
   156  
   157  func (n *NodePlannableResourceInstanceOrphan) deleteActionReason(ctx EvalContext) plans.ResourceInstanceChangeActionReason {
   158  	cfg := n.Config
   159  	if cfg == nil {
   160  		if !n.Addr.Equal(n.prevRunAddr(ctx)) {
   161  			// This means the resource was moved - see also
   162  			// ResourceInstanceChange.Moved() which calculates
   163  			// this the same way.
   164  			return plans.ResourceInstanceDeleteBecauseNoMoveTarget
   165  		}
   166  
   167  		return plans.ResourceInstanceDeleteBecauseNoResourceConfig
   168  	}
   169  
   170  	// If this is a resource instance inside a module instance that's no
   171  	// longer declared then we will have a config (because config isn't
   172  	// instance-specific) but the expander will know that our resource
   173  	// address's module path refers to an undeclared module instance.
   174  	if expander := ctx.InstanceExpander(); expander != nil { // (sometimes nil in MockEvalContext in tests)
   175  		validModuleAddr := expander.GetDeepestExistingModuleInstance(n.Addr.Module)
   176  		if len(validModuleAddr) != len(n.Addr.Module) {
   177  			// If we get here then at least one step in the resource's module
   178  			// path is to a module instance that doesn't exist at all, and
   179  			// so a missing module instance is the delete reason regardless
   180  			// of whether there might _also_ be a change to the resource
   181  			// configuration inside the module. (Conceptually the configurations
   182  			// inside the non-existing module instance don't exist at all,
   183  			// but they end up existing just as an artifact of the
   184  			// implementation detail that we detect module instance orphans
   185  			// only dynamically.)
   186  			return plans.ResourceInstanceDeleteBecauseNoModule
   187  		}
   188  	}
   189  
   190  	switch n.Addr.Resource.Key.(type) {
   191  	case nil: // no instance key at all
   192  		if cfg.Count != nil || cfg.ForEach != nil {
   193  			return plans.ResourceInstanceDeleteBecauseWrongRepetition
   194  		}
   195  	case addrs.IntKey:
   196  		if cfg.Count == nil {
   197  			// This resource isn't using "count" at all, then
   198  			return plans.ResourceInstanceDeleteBecauseWrongRepetition
   199  		}
   200  
   201  		expander := ctx.InstanceExpander()
   202  		if expander == nil {
   203  			break // only for tests that produce an incomplete MockEvalContext
   204  		}
   205  		insts := expander.ExpandResource(n.Addr.ContainingResource())
   206  
   207  		declared := false
   208  		for _, inst := range insts {
   209  			if n.Addr.Equal(inst) {
   210  				declared = true
   211  			}
   212  		}
   213  		if !declared {
   214  			// This instance key is outside of the configured range
   215  			return plans.ResourceInstanceDeleteBecauseCountIndex
   216  		}
   217  	case addrs.StringKey:
   218  		if cfg.ForEach == nil {
   219  			// This resource isn't using "for_each" at all, then
   220  			return plans.ResourceInstanceDeleteBecauseWrongRepetition
   221  		}
   222  
   223  		expander := ctx.InstanceExpander()
   224  		if expander == nil {
   225  			break // only for tests that produce an incomplete MockEvalContext
   226  		}
   227  		insts := expander.ExpandResource(n.Addr.ContainingResource())
   228  
   229  		declared := false
   230  		for _, inst := range insts {
   231  			if n.Addr.Equal(inst) {
   232  				declared = true
   233  			}
   234  		}
   235  		if !declared {
   236  			// This instance key is outside of the configured range
   237  			return plans.ResourceInstanceDeleteBecauseEachKey
   238  		}
   239  	}
   240  
   241  	// If we get here then the instance key type matches the configured
   242  	// repetition mode, and so we need to consider whether the key itself
   243  	// is within the range of the repetition construct.
   244  	if expander := ctx.InstanceExpander(); expander != nil { // (sometimes nil in MockEvalContext in tests)
   245  		// First we'll check whether our containing module instance still
   246  		// exists, so we can talk about that differently in the reason.
   247  		declared := false
   248  		for _, inst := range expander.ExpandModule(n.Addr.Module.Module()) {
   249  			if n.Addr.Module.Equal(inst) {
   250  				declared = true
   251  				break
   252  			}
   253  		}
   254  		if !declared {
   255  			return plans.ResourceInstanceDeleteBecauseNoModule
   256  		}
   257  
   258  		// Now we've proven that we're in a still-existing module instance,
   259  		// we'll see if our instance key matches something actually declared.
   260  		declared = false
   261  		for _, inst := range expander.ExpandResource(n.Addr.ContainingResource()) {
   262  			if n.Addr.Equal(inst) {
   263  				declared = true
   264  				break
   265  			}
   266  		}
   267  		if !declared {
   268  			// Because we already checked that the key _type_ was correct
   269  			// above, we can assume that any mismatch here is a range error,
   270  			// and thus we just need to decide which of the two range
   271  			// errors we're going to return.
   272  			switch n.Addr.Resource.Key.(type) {
   273  			case addrs.IntKey:
   274  				return plans.ResourceInstanceDeleteBecauseCountIndex
   275  			case addrs.StringKey:
   276  				return plans.ResourceInstanceDeleteBecauseEachKey
   277  			}
   278  		}
   279  	}
   280  
   281  	// If we didn't find any specific reason to report, we'll report "no reason"
   282  	// as a fallback, which means the UI should just state it'll be deleted
   283  	// without any explicit reasoning.
   284  	return plans.ResourceInstanceChangeNoReason
   285  }