github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/terraform/node_resource_plan.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/internal/addrs"
     9  	"github.com/hashicorp/terraform/internal/dag"
    10  	"github.com/hashicorp/terraform/internal/states"
    11  	"github.com/hashicorp/terraform/internal/tfdiags"
    12  )
    13  
    14  // nodeExpandPlannableResource handles the first layer of resource
    15  // expansion.  We need this extra layer so DynamicExpand is called twice for
    16  // the resource, the first to expand the Resource for each module instance, and
    17  // the second to expand each ResourceInstance for the expanded Resources.
    18  type nodeExpandPlannableResource struct {
    19  	*NodeAbstractResource
    20  
    21  	// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
    22  	// during graph construction, if dependencies require us to force this
    23  	// on regardless of what the configuration says.
    24  	ForceCreateBeforeDestroy *bool
    25  
    26  	// skipRefresh indicates that we should skip refreshing individual instances
    27  	skipRefresh bool
    28  
    29  	// skipPlanChanges indicates we should skip trying to plan change actions
    30  	// for any instances.
    31  	skipPlanChanges bool
    32  
    33  	// forceReplace are resource instance addresses where the user wants to
    34  	// force generating a replace action. This set isn't pre-filtered, so
    35  	// it might contain addresses that have nothing to do with the resource
    36  	// that this node represents, which the node itself must therefore ignore.
    37  	forceReplace []addrs.AbsResourceInstance
    38  
    39  	// We attach dependencies to the Resource during refresh, since the
    40  	// instances are instantiated during DynamicExpand.
    41  	dependencies []addrs.ConfigResource
    42  }
    43  
    44  var (
    45  	_ GraphNodeDestroyerCBD         = (*nodeExpandPlannableResource)(nil)
    46  	_ GraphNodeDynamicExpandable    = (*nodeExpandPlannableResource)(nil)
    47  	_ GraphNodeReferenceable        = (*nodeExpandPlannableResource)(nil)
    48  	_ GraphNodeReferencer           = (*nodeExpandPlannableResource)(nil)
    49  	_ GraphNodeConfigResource       = (*nodeExpandPlannableResource)(nil)
    50  	_ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil)
    51  	_ GraphNodeAttachDependencies   = (*nodeExpandPlannableResource)(nil)
    52  	_ GraphNodeTargetable           = (*nodeExpandPlannableResource)(nil)
    53  )
    54  
    55  func (n *nodeExpandPlannableResource) Name() string {
    56  	return n.NodeAbstractResource.Name() + " (expand)"
    57  }
    58  
    59  // GraphNodeAttachDependencies
    60  func (n *nodeExpandPlannableResource) AttachDependencies(deps []addrs.ConfigResource) {
    61  	n.dependencies = deps
    62  }
    63  
    64  // GraphNodeDestroyerCBD
    65  func (n *nodeExpandPlannableResource) CreateBeforeDestroy() bool {
    66  	if n.ForceCreateBeforeDestroy != nil {
    67  		return *n.ForceCreateBeforeDestroy
    68  	}
    69  
    70  	// If we have no config, we just assume no
    71  	if n.Config == nil || n.Config.Managed == nil {
    72  		return false
    73  	}
    74  
    75  	return n.Config.Managed.CreateBeforeDestroy
    76  }
    77  
    78  // GraphNodeDestroyerCBD
    79  func (n *nodeExpandPlannableResource) ModifyCreateBeforeDestroy(v bool) error {
    80  	n.ForceCreateBeforeDestroy = &v
    81  	return nil
    82  }
    83  
    84  func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    85  	var g Graph
    86  
    87  	expander := ctx.InstanceExpander()
    88  	moduleInstances := expander.ExpandModule(n.Addr.Module)
    89  
    90  	// Add the current expanded resource to the graph
    91  	for _, module := range moduleInstances {
    92  		resAddr := n.Addr.Resource.Absolute(module)
    93  		g.Add(&NodePlannableResource{
    94  			NodeAbstractResource:     n.NodeAbstractResource,
    95  			Addr:                     resAddr,
    96  			ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy,
    97  			dependencies:             n.dependencies,
    98  			skipRefresh:              n.skipRefresh,
    99  			skipPlanChanges:          n.skipPlanChanges,
   100  			forceReplace:             n.forceReplace,
   101  		})
   102  	}
   103  
   104  	// Lock the state while we inspect it
   105  	state := ctx.State().Lock()
   106  	defer ctx.State().Unlock()
   107  
   108  	var orphans []*states.Resource
   109  	for _, res := range state.Resources(n.Addr) {
   110  		found := false
   111  		for _, m := range moduleInstances {
   112  			if m.Equal(res.Addr.Module) {
   113  				found = true
   114  				break
   115  			}
   116  		}
   117  		// Address form state was not found in the current config
   118  		if !found {
   119  			orphans = append(orphans, res)
   120  		}
   121  	}
   122  
   123  	// The concrete resource factory we'll use for orphans
   124  	concreteResourceOrphan := func(a *NodeAbstractResourceInstance) *NodePlannableResourceInstanceOrphan {
   125  		// Add the config and state since we don't do that via transforms
   126  		a.Config = n.Config
   127  		a.ResolvedProvider = n.ResolvedProvider
   128  		a.Schema = n.Schema
   129  		a.ProvisionerSchemas = n.ProvisionerSchemas
   130  		a.ProviderMetas = n.ProviderMetas
   131  		a.Dependencies = n.dependencies
   132  
   133  		return &NodePlannableResourceInstanceOrphan{
   134  			NodeAbstractResourceInstance: a,
   135  		}
   136  	}
   137  
   138  	for _, res := range orphans {
   139  		for key := range res.Instances {
   140  			addr := res.Addr.Instance(key)
   141  			abs := NewNodeAbstractResourceInstance(addr)
   142  			abs.AttachResourceState(res)
   143  			n := concreteResourceOrphan(abs)
   144  			g.Add(n)
   145  		}
   146  	}
   147  
   148  	return &g, nil
   149  }
   150  
   151  // NodePlannableResource represents a resource that is "plannable":
   152  // it is ready to be planned in order to create a diff.
   153  type NodePlannableResource struct {
   154  	*NodeAbstractResource
   155  
   156  	Addr addrs.AbsResource
   157  
   158  	// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
   159  	// during graph construction, if dependencies require us to force this
   160  	// on regardless of what the configuration says.
   161  	ForceCreateBeforeDestroy *bool
   162  
   163  	// skipRefresh indicates that we should skip refreshing individual instances
   164  	skipRefresh bool
   165  
   166  	// skipPlanChanges indicates we should skip trying to plan change actions
   167  	// for any instances.
   168  	skipPlanChanges bool
   169  
   170  	// forceReplace are resource instance addresses where the user wants to
   171  	// force generating a replace action. This set isn't pre-filtered, so
   172  	// it might contain addresses that have nothing to do with the resource
   173  	// that this node represents, which the node itself must therefore ignore.
   174  	forceReplace []addrs.AbsResourceInstance
   175  
   176  	dependencies []addrs.ConfigResource
   177  }
   178  
   179  var (
   180  	_ GraphNodeModuleInstance       = (*NodePlannableResource)(nil)
   181  	_ GraphNodeDestroyerCBD         = (*NodePlannableResource)(nil)
   182  	_ GraphNodeDynamicExpandable    = (*NodePlannableResource)(nil)
   183  	_ GraphNodeReferenceable        = (*NodePlannableResource)(nil)
   184  	_ GraphNodeReferencer           = (*NodePlannableResource)(nil)
   185  	_ GraphNodeConfigResource       = (*NodePlannableResource)(nil)
   186  	_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
   187  )
   188  
   189  func (n *NodePlannableResource) Path() addrs.ModuleInstance {
   190  	return n.Addr.Module
   191  }
   192  
   193  func (n *NodePlannableResource) Name() string {
   194  	return n.Addr.String()
   195  }
   196  
   197  // GraphNodeExecutable
   198  func (n *NodePlannableResource) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics {
   199  	var diags tfdiags.Diagnostics
   200  
   201  	if n.Config == nil {
   202  		// Nothing to do, then.
   203  		log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", n.Name())
   204  		return diags
   205  	}
   206  
   207  	// writeResourceState is responsible for informing the expander of what
   208  	// repetition mode this resource has, which allows expander.ExpandResource
   209  	// to work below.
   210  	moreDiags := n.writeResourceState(ctx, n.Addr)
   211  	diags = diags.Append(moreDiags)
   212  	if moreDiags.HasErrors() {
   213  		return diags
   214  	}
   215  
   216  	// Before we expand our resource into potentially many resource instances,
   217  	// we'll verify that any mention of this resource in n.forceReplace is
   218  	// consistent with the repetition mode of the resource. In other words,
   219  	// we're aiming to catch a situation where naming a particular resource
   220  	// instance would require an instance key but the given address has none.
   221  	expander := ctx.InstanceExpander()
   222  	instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path()))
   223  
   224  	// If there's a number of instances other than 1 then we definitely need
   225  	// an index.
   226  	mustHaveIndex := len(instanceAddrs) != 1
   227  	// If there's only one instance then we might still need an index, if the
   228  	// instance address has one.
   229  	if len(instanceAddrs) == 1 && instanceAddrs[0].Resource.Key != addrs.NoKey {
   230  		mustHaveIndex = true
   231  	}
   232  	if mustHaveIndex {
   233  		for _, candidateAddr := range n.forceReplace {
   234  			if candidateAddr.Resource.Key == addrs.NoKey {
   235  				if n.Addr.Resource.Equal(candidateAddr.Resource.Resource) {
   236  					switch {
   237  					case len(instanceAddrs) == 0:
   238  						// In this case there _are_ no instances to replace, so
   239  						// there isn't any alternative address for us to suggest.
   240  						diags = diags.Append(tfdiags.Sourceless(
   241  							tfdiags.Warning,
   242  							"Incompletely-matched force-replace resource instance",
   243  							fmt.Sprintf(
   244  								"Your force-replace request for %s doesn't match any resource instances because this resource doesn't have any instances.",
   245  								candidateAddr,
   246  							),
   247  						))
   248  					case len(instanceAddrs) == 1:
   249  						diags = diags.Append(tfdiags.Sourceless(
   250  							tfdiags.Warning,
   251  							"Incompletely-matched force-replace resource instance",
   252  							fmt.Sprintf(
   253  								"Your force-replace request for %s doesn't match any resource instances because it lacks an instance key.\n\nTo force replacement of the single declared instance, use the following option instead:\n  -replace=%q",
   254  								candidateAddr, instanceAddrs[0],
   255  							),
   256  						))
   257  					default:
   258  						var possibleValidOptions strings.Builder
   259  						for _, addr := range instanceAddrs {
   260  							fmt.Fprintf(&possibleValidOptions, "\n  -replace=%q", addr)
   261  						}
   262  
   263  						diags = diags.Append(tfdiags.Sourceless(
   264  							tfdiags.Warning,
   265  							"Incompletely-matched force-replace resource instance",
   266  							fmt.Sprintf(
   267  								"Your force-replace request for %s doesn't match any resource instances because it lacks an instance key.\n\nTo force replacement of particular instances, use one or more of the following options instead:%s",
   268  								candidateAddr, possibleValidOptions.String(),
   269  							),
   270  						))
   271  					}
   272  				}
   273  			}
   274  		}
   275  	}
   276  	// NOTE: The actual interpretation of n.forceReplace to produce replace
   277  	// actions is in NodeAbstractResourceInstance.plan, because we must do so
   278  	// on a per-instance basis rather than for the whole resource.
   279  
   280  	return diags
   281  }
   282  
   283  // GraphNodeDestroyerCBD
   284  func (n *NodePlannableResource) CreateBeforeDestroy() bool {
   285  	if n.ForceCreateBeforeDestroy != nil {
   286  		return *n.ForceCreateBeforeDestroy
   287  	}
   288  
   289  	// If we have no config, we just assume no
   290  	if n.Config == nil || n.Config.Managed == nil {
   291  		return false
   292  	}
   293  
   294  	return n.Config.Managed.CreateBeforeDestroy
   295  }
   296  
   297  // GraphNodeDestroyerCBD
   298  func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
   299  	n.ForceCreateBeforeDestroy = &v
   300  	return nil
   301  }
   302  
   303  // GraphNodeDynamicExpandable
   304  func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
   305  	var diags tfdiags.Diagnostics
   306  
   307  	// We need to potentially rename an instance address in the state
   308  	// if we're transitioning whether "count" is set at all.
   309  	fixResourceCountSetTransition(ctx, n.Addr.Config(), n.Config.Count != nil)
   310  
   311  	// Our instance expander should already have been informed about the
   312  	// expansion of this resource and of all of its containing modules, so
   313  	// it can tell us which instance addresses we need to process.
   314  	expander := ctx.InstanceExpander()
   315  	instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path()))
   316  
   317  	// Our graph transformers require access to the full state, so we'll
   318  	// temporarily lock it while we work on this.
   319  	state := ctx.State().Lock()
   320  	defer ctx.State().Unlock()
   321  
   322  	// The concrete resource factory we'll use
   323  	concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
   324  		// Add the config and state since we don't do that via transforms
   325  		a.Config = n.Config
   326  		a.ResolvedProvider = n.ResolvedProvider
   327  		a.Schema = n.Schema
   328  		a.ProvisionerSchemas = n.ProvisionerSchemas
   329  		a.ProviderMetas = n.ProviderMetas
   330  		a.dependsOn = n.dependsOn
   331  		a.Dependencies = n.dependencies
   332  
   333  		return &NodePlannableResourceInstance{
   334  			NodeAbstractResourceInstance: a,
   335  
   336  			// By the time we're walking, we've figured out whether we need
   337  			// to force on CreateBeforeDestroy due to dependencies on other
   338  			// nodes that have it.
   339  			ForceCreateBeforeDestroy: n.CreateBeforeDestroy(),
   340  			skipRefresh:              n.skipRefresh,
   341  			skipPlanChanges:          n.skipPlanChanges,
   342  			forceReplace:             n.forceReplace,
   343  		}
   344  	}
   345  
   346  	// The concrete resource factory we'll use for orphans
   347  	concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
   348  		// Add the config and state since we don't do that via transforms
   349  		a.Config = n.Config
   350  		a.ResolvedProvider = n.ResolvedProvider
   351  		a.Schema = n.Schema
   352  		a.ProvisionerSchemas = n.ProvisionerSchemas
   353  		a.ProviderMetas = n.ProviderMetas
   354  
   355  		return &NodePlannableResourceInstanceOrphan{
   356  			NodeAbstractResourceInstance: a,
   357  			skipRefresh:                  n.skipRefresh,
   358  		}
   359  	}
   360  
   361  	// Start creating the steps
   362  	steps := []GraphTransformer{
   363  		// Expand the count or for_each (if present)
   364  		&ResourceCountTransformer{
   365  			Concrete:      concreteResource,
   366  			Schema:        n.Schema,
   367  			Addr:          n.ResourceAddr(),
   368  			InstanceAddrs: instanceAddrs,
   369  		},
   370  
   371  		// Add the count/for_each orphans
   372  		&OrphanResourceInstanceCountTransformer{
   373  			Concrete:      concreteResourceOrphan,
   374  			Addr:          n.Addr,
   375  			InstanceAddrs: instanceAddrs,
   376  			State:         state,
   377  		},
   378  
   379  		// Attach the state
   380  		&AttachStateTransformer{State: state},
   381  
   382  		// Targeting
   383  		&TargetsTransformer{Targets: n.Targets},
   384  
   385  		// Connect references so ordering is correct
   386  		&ReferenceTransformer{},
   387  
   388  		// Make sure there is a single root
   389  		&RootTransformer{},
   390  	}
   391  
   392  	// Build the graph
   393  	b := &BasicGraphBuilder{
   394  		Steps:    steps,
   395  		Validate: true,
   396  		Name:     "NodePlannableResource",
   397  	}
   398  	graph, diags := b.Build(ctx.Path())
   399  	return graph, diags.ErrWithWarnings()
   400  }