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

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/muratcelep/terraform/not-internal/addrs"
     9  	"github.com/muratcelep/terraform/not-internal/dag"
    10  	"github.com/muratcelep/terraform/not-internal/states"
    11  	"github.com/muratcelep/terraform/not-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  			skipRefresh:                  n.skipRefresh,
   136  			skipPlanChanges:              n.skipPlanChanges,
   137  		}
   138  	}
   139  
   140  	for _, res := range orphans {
   141  		for key := range res.Instances {
   142  			addr := res.Addr.Instance(key)
   143  			abs := NewNodeAbstractResourceInstance(addr)
   144  			abs.AttachResourceState(res)
   145  			n := concreteResourceOrphan(abs)
   146  			g.Add(n)
   147  		}
   148  	}
   149  
   150  	return &g, nil
   151  }
   152  
   153  // NodePlannableResource represents a resource that is "plannable":
   154  // it is ready to be planned in order to create a diff.
   155  type NodePlannableResource struct {
   156  	*NodeAbstractResource
   157  
   158  	Addr addrs.AbsResource
   159  
   160  	// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
   161  	// during graph construction, if dependencies require us to force this
   162  	// on regardless of what the configuration says.
   163  	ForceCreateBeforeDestroy *bool
   164  
   165  	// skipRefresh indicates that we should skip refreshing individual instances
   166  	skipRefresh bool
   167  
   168  	// skipPlanChanges indicates we should skip trying to plan change actions
   169  	// for any instances.
   170  	skipPlanChanges bool
   171  
   172  	// forceReplace are resource instance addresses where the user wants to
   173  	// force generating a replace action. This set isn't pre-filtered, so
   174  	// it might contain addresses that have nothing to do with the resource
   175  	// that this node represents, which the node itself must therefore ignore.
   176  	forceReplace []addrs.AbsResourceInstance
   177  
   178  	dependencies []addrs.ConfigResource
   179  }
   180  
   181  var (
   182  	_ GraphNodeModuleInstance       = (*NodePlannableResource)(nil)
   183  	_ GraphNodeDestroyerCBD         = (*NodePlannableResource)(nil)
   184  	_ GraphNodeDynamicExpandable    = (*NodePlannableResource)(nil)
   185  	_ GraphNodeReferenceable        = (*NodePlannableResource)(nil)
   186  	_ GraphNodeReferencer           = (*NodePlannableResource)(nil)
   187  	_ GraphNodeConfigResource       = (*NodePlannableResource)(nil)
   188  	_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
   189  )
   190  
   191  func (n *NodePlannableResource) Path() addrs.ModuleInstance {
   192  	return n.Addr.Module
   193  }
   194  
   195  func (n *NodePlannableResource) Name() string {
   196  	return n.Addr.String()
   197  }
   198  
   199  // GraphNodeExecutable
   200  func (n *NodePlannableResource) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics {
   201  	var diags tfdiags.Diagnostics
   202  
   203  	if n.Config == nil {
   204  		// Nothing to do, then.
   205  		log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", n.Name())
   206  		return diags
   207  	}
   208  
   209  	// writeResourceState is responsible for informing the expander of what
   210  	// repetition mode this resource has, which allows expander.ExpandResource
   211  	// to work below.
   212  	moreDiags := n.writeResourceState(ctx, n.Addr)
   213  	diags = diags.Append(moreDiags)
   214  	if moreDiags.HasErrors() {
   215  		return diags
   216  	}
   217  
   218  	// Before we expand our resource into potentially many resource instances,
   219  	// we'll verify that any mention of this resource in n.forceReplace is
   220  	// consistent with the repetition mode of the resource. In other words,
   221  	// we're aiming to catch a situation where naming a particular resource
   222  	// instance would require an instance key but the given address has none.
   223  	expander := ctx.InstanceExpander()
   224  	instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path()))
   225  
   226  	// If there's a number of instances other than 1 then we definitely need
   227  	// an index.
   228  	mustHaveIndex := len(instanceAddrs) != 1
   229  	// If there's only one instance then we might still need an index, if the
   230  	// instance address has one.
   231  	if len(instanceAddrs) == 1 && instanceAddrs[0].Resource.Key != addrs.NoKey {
   232  		mustHaveIndex = true
   233  	}
   234  	if mustHaveIndex {
   235  		for _, candidateAddr := range n.forceReplace {
   236  			if candidateAddr.Resource.Key == addrs.NoKey {
   237  				if n.Addr.Resource.Equal(candidateAddr.Resource.Resource) {
   238  					switch {
   239  					case len(instanceAddrs) == 0:
   240  						// In this case there _are_ no instances to replace, so
   241  						// there isn't any alternative address for us to suggest.
   242  						diags = diags.Append(tfdiags.Sourceless(
   243  							tfdiags.Warning,
   244  							"Incompletely-matched force-replace resource instance",
   245  							fmt.Sprintf(
   246  								"Your force-replace request for %s doesn't match any resource instances because this resource doesn't have any instances.",
   247  								candidateAddr,
   248  							),
   249  						))
   250  					case len(instanceAddrs) == 1:
   251  						diags = diags.Append(tfdiags.Sourceless(
   252  							tfdiags.Warning,
   253  							"Incompletely-matched force-replace resource instance",
   254  							fmt.Sprintf(
   255  								"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",
   256  								candidateAddr, instanceAddrs[0],
   257  							),
   258  						))
   259  					default:
   260  						var possibleValidOptions strings.Builder
   261  						for _, addr := range instanceAddrs {
   262  							fmt.Fprintf(&possibleValidOptions, "\n  -replace=%q", addr)
   263  						}
   264  
   265  						diags = diags.Append(tfdiags.Sourceless(
   266  							tfdiags.Warning,
   267  							"Incompletely-matched force-replace resource instance",
   268  							fmt.Sprintf(
   269  								"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",
   270  								candidateAddr, possibleValidOptions.String(),
   271  							),
   272  						))
   273  					}
   274  				}
   275  			}
   276  		}
   277  	}
   278  	// NOTE: The actual interpretation of n.forceReplace to produce replace
   279  	// actions is in NodeAbstractResourceInstance.plan, because we must do so
   280  	// on a per-instance basis rather than for the whole resource.
   281  
   282  	return diags
   283  }
   284  
   285  // GraphNodeDestroyerCBD
   286  func (n *NodePlannableResource) CreateBeforeDestroy() bool {
   287  	if n.ForceCreateBeforeDestroy != nil {
   288  		return *n.ForceCreateBeforeDestroy
   289  	}
   290  
   291  	// If we have no config, we just assume no
   292  	if n.Config == nil || n.Config.Managed == nil {
   293  		return false
   294  	}
   295  
   296  	return n.Config.Managed.CreateBeforeDestroy
   297  }
   298  
   299  // GraphNodeDestroyerCBD
   300  func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
   301  	n.ForceCreateBeforeDestroy = &v
   302  	return nil
   303  }
   304  
   305  // GraphNodeDynamicExpandable
   306  func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
   307  	var diags tfdiags.Diagnostics
   308  
   309  	// Our instance expander should already have been informed about the
   310  	// expansion of this resource and of all of its containing modules, so
   311  	// it can tell us which instance addresses we need to process.
   312  	expander := ctx.InstanceExpander()
   313  	instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path()))
   314  
   315  	// Our graph transformers require access to the full state, so we'll
   316  	// temporarily lock it while we work on this.
   317  	state := ctx.State().Lock()
   318  	defer ctx.State().Unlock()
   319  
   320  	// The concrete resource factory we'll use
   321  	concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
   322  		// Add the config and state since we don't do that via transforms
   323  		a.Config = n.Config
   324  		a.ResolvedProvider = n.ResolvedProvider
   325  		a.Schema = n.Schema
   326  		a.ProvisionerSchemas = n.ProvisionerSchemas
   327  		a.ProviderMetas = n.ProviderMetas
   328  		a.dependsOn = n.dependsOn
   329  		a.Dependencies = n.dependencies
   330  
   331  		return &NodePlannableResourceInstance{
   332  			NodeAbstractResourceInstance: a,
   333  
   334  			// By the time we're walking, we've figured out whether we need
   335  			// to force on CreateBeforeDestroy due to dependencies on other
   336  			// nodes that have it.
   337  			ForceCreateBeforeDestroy: n.CreateBeforeDestroy(),
   338  			skipRefresh:              n.skipRefresh,
   339  			skipPlanChanges:          n.skipPlanChanges,
   340  			forceReplace:             n.forceReplace,
   341  		}
   342  	}
   343  
   344  	// The concrete resource factory we'll use for orphans
   345  	concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
   346  		// Add the config and state since we don't do that via transforms
   347  		a.Config = n.Config
   348  		a.ResolvedProvider = n.ResolvedProvider
   349  		a.Schema = n.Schema
   350  		a.ProvisionerSchemas = n.ProvisionerSchemas
   351  		a.ProviderMetas = n.ProviderMetas
   352  
   353  		return &NodePlannableResourceInstanceOrphan{
   354  			NodeAbstractResourceInstance: a,
   355  			skipRefresh:                  n.skipRefresh,
   356  			skipPlanChanges:              n.skipPlanChanges,
   357  		}
   358  	}
   359  
   360  	// Start creating the steps
   361  	steps := []GraphTransformer{
   362  		// Expand the count or for_each (if present)
   363  		&ResourceCountTransformer{
   364  			Concrete:      concreteResource,
   365  			Schema:        n.Schema,
   366  			Addr:          n.ResourceAddr(),
   367  			InstanceAddrs: instanceAddrs,
   368  		},
   369  
   370  		// Add the count/for_each orphans
   371  		&OrphanResourceInstanceCountTransformer{
   372  			Concrete:      concreteResourceOrphan,
   373  			Addr:          n.Addr,
   374  			InstanceAddrs: instanceAddrs,
   375  			State:         state,
   376  		},
   377  
   378  		// Attach the state
   379  		&AttachStateTransformer{State: state},
   380  
   381  		// Targeting
   382  		&TargetsTransformer{Targets: n.Targets},
   383  
   384  		// Connect references so ordering is correct
   385  		&ReferenceTransformer{},
   386  
   387  		// Make sure there is a single root
   388  		&RootTransformer{},
   389  	}
   390  
   391  	// Build the graph
   392  	b := &BasicGraphBuilder{
   393  		Steps:    steps,
   394  		Validate: true,
   395  		Name:     "NodePlannableResource",
   396  	}
   397  	graph, diags := b.Build(ctx.Path())
   398  	return graph, diags.ErrWithWarnings()
   399  }