github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/node_resource_abstract.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/configs"
     9  	"github.com/hashicorp/terraform/internal/configs/configschema"
    10  	"github.com/hashicorp/terraform/internal/dag"
    11  	"github.com/hashicorp/terraform/internal/lang"
    12  	"github.com/hashicorp/terraform/internal/states"
    13  	"github.com/hashicorp/terraform/internal/tfdiags"
    14  )
    15  
    16  // ConcreteResourceNodeFunc is a callback type used to convert an
    17  // abstract resource to a concrete one of some type.
    18  type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex
    19  
    20  // GraphNodeConfigResource is implemented by any nodes that represent a resource.
    21  // The type of operation cannot be assumed, only that this node represents
    22  // the given resource.
    23  type GraphNodeConfigResource interface {
    24  	ResourceAddr() addrs.ConfigResource
    25  }
    26  
    27  // ConcreteResourceInstanceNodeFunc is a callback type used to convert an
    28  // abstract resource instance to a concrete one of some type.
    29  type ConcreteResourceInstanceNodeFunc func(*NodeAbstractResourceInstance) dag.Vertex
    30  
    31  // GraphNodeResourceInstance is implemented by any nodes that represent
    32  // a resource instance. A single resource may have multiple instances if,
    33  // for example, the "count" or "for_each" argument is used for it in
    34  // configuration.
    35  type GraphNodeResourceInstance interface {
    36  	ResourceInstanceAddr() addrs.AbsResourceInstance
    37  
    38  	// StateDependencies returns any inter-resource dependencies that are
    39  	// stored in the state.
    40  	StateDependencies() []addrs.ConfigResource
    41  }
    42  
    43  // NodeAbstractResource represents a resource that has no associated
    44  // operations. It registers all the interfaces for a resource that common
    45  // across multiple operation types.
    46  type NodeAbstractResource struct {
    47  	Addr addrs.ConfigResource
    48  
    49  	// The fields below will be automatically set using the Attach
    50  	// interfaces if you're running those transforms, but also be explicitly
    51  	// set if you already have that information.
    52  
    53  	Schema        *configschema.Block // Schema for processing the configuration body
    54  	SchemaVersion uint64              // Schema version of "Schema", as decided by the provider
    55  	Config        *configs.Resource   // Config is the resource in the config
    56  
    57  	// ProviderMetas is the provider_meta configs for the module this resource belongs to
    58  	ProviderMetas map[addrs.Provider]*configs.ProviderMeta
    59  
    60  	ProvisionerSchemas map[string]*configschema.Block
    61  
    62  	// Set from GraphNodeTargetable
    63  	Targets []addrs.Targetable
    64  
    65  	// Set from AttachDataResourceDependsOn
    66  	dependsOn      []addrs.ConfigResource
    67  	forceDependsOn bool
    68  
    69  	// The address of the provider this resource will use
    70  	ResolvedProvider addrs.AbsProviderConfig
    71  	// storedProviderConfig is the provider address retrieved from the
    72  	// state. This is defined here for access within the ProvidedBy method, but
    73  	// will be set from the embedding instance type when the state is attached.
    74  	storedProviderConfig addrs.AbsProviderConfig
    75  
    76  	// This resource may expand into instances which need to be imported.
    77  	importTargets []*ImportTarget
    78  }
    79  
    80  var (
    81  	_ GraphNodeReferenceable               = (*NodeAbstractResource)(nil)
    82  	_ GraphNodeReferencer                  = (*NodeAbstractResource)(nil)
    83  	_ GraphNodeProviderConsumer            = (*NodeAbstractResource)(nil)
    84  	_ GraphNodeProvisionerConsumer         = (*NodeAbstractResource)(nil)
    85  	_ GraphNodeConfigResource              = (*NodeAbstractResource)(nil)
    86  	_ GraphNodeAttachResourceConfig        = (*NodeAbstractResource)(nil)
    87  	_ GraphNodeAttachResourceSchema        = (*NodeAbstractResource)(nil)
    88  	_ GraphNodeAttachProvisionerSchema     = (*NodeAbstractResource)(nil)
    89  	_ GraphNodeAttachProviderMetaConfigs   = (*NodeAbstractResource)(nil)
    90  	_ GraphNodeTargetable                  = (*NodeAbstractResource)(nil)
    91  	_ graphNodeAttachDataResourceDependsOn = (*NodeAbstractResource)(nil)
    92  	_ dag.GraphNodeDotter                  = (*NodeAbstractResource)(nil)
    93  )
    94  
    95  // NewNodeAbstractResource creates an abstract resource graph node for
    96  // the given absolute resource address.
    97  func NewNodeAbstractResource(addr addrs.ConfigResource) *NodeAbstractResource {
    98  	return &NodeAbstractResource{
    99  		Addr: addr,
   100  	}
   101  }
   102  
   103  var (
   104  	_ GraphNodeModuleInstance            = (*NodeAbstractResourceInstance)(nil)
   105  	_ GraphNodeReferenceable             = (*NodeAbstractResourceInstance)(nil)
   106  	_ GraphNodeReferencer                = (*NodeAbstractResourceInstance)(nil)
   107  	_ GraphNodeProviderConsumer          = (*NodeAbstractResourceInstance)(nil)
   108  	_ GraphNodeProvisionerConsumer       = (*NodeAbstractResourceInstance)(nil)
   109  	_ GraphNodeConfigResource            = (*NodeAbstractResourceInstance)(nil)
   110  	_ GraphNodeResourceInstance          = (*NodeAbstractResourceInstance)(nil)
   111  	_ GraphNodeAttachResourceState       = (*NodeAbstractResourceInstance)(nil)
   112  	_ GraphNodeAttachResourceConfig      = (*NodeAbstractResourceInstance)(nil)
   113  	_ GraphNodeAttachResourceSchema      = (*NodeAbstractResourceInstance)(nil)
   114  	_ GraphNodeAttachProvisionerSchema   = (*NodeAbstractResourceInstance)(nil)
   115  	_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResourceInstance)(nil)
   116  	_ GraphNodeTargetable                = (*NodeAbstractResourceInstance)(nil)
   117  	_ dag.GraphNodeDotter                = (*NodeAbstractResourceInstance)(nil)
   118  )
   119  
   120  func (n *NodeAbstractResource) Name() string {
   121  	return n.ResourceAddr().String()
   122  }
   123  
   124  // GraphNodeModulePath
   125  func (n *NodeAbstractResource) ModulePath() addrs.Module {
   126  	return n.Addr.Module
   127  }
   128  
   129  // GraphNodeReferenceable
   130  func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable {
   131  	return []addrs.Referenceable{n.Addr.Resource}
   132  }
   133  
   134  func (n *NodeAbstractResource) Import(addr *ImportTarget) {
   135  
   136  }
   137  
   138  // GraphNodeReferencer
   139  func (n *NodeAbstractResource) References() []*addrs.Reference {
   140  	// If we have a config then we prefer to use that.
   141  	if c := n.Config; c != nil {
   142  		var result []*addrs.Reference
   143  
   144  		result = append(result, n.DependsOn()...)
   145  
   146  		if n.Schema == nil {
   147  			// Should never happen, but we'll log if it does so that we can
   148  			// see this easily when debugging.
   149  			log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
   150  		}
   151  
   152  		refs, _ := lang.ReferencesInExpr(c.Count)
   153  		result = append(result, refs...)
   154  		refs, _ = lang.ReferencesInExpr(c.ForEach)
   155  		result = append(result, refs...)
   156  
   157  		for _, expr := range c.TriggersReplacement {
   158  			refs, _ = lang.ReferencesInExpr(expr)
   159  			result = append(result, refs...)
   160  		}
   161  
   162  		// ReferencesInBlock() requires a schema
   163  		if n.Schema != nil {
   164  			refs, _ = lang.ReferencesInBlock(c.Config, n.Schema)
   165  			result = append(result, refs...)
   166  		}
   167  
   168  		if c.Managed != nil {
   169  			if c.Managed.Connection != nil {
   170  				refs, _ = lang.ReferencesInBlock(c.Managed.Connection.Config, connectionBlockSupersetSchema)
   171  				result = append(result, refs...)
   172  			}
   173  
   174  			for _, p := range c.Managed.Provisioners {
   175  				if p.When != configs.ProvisionerWhenCreate {
   176  					continue
   177  				}
   178  				if p.Connection != nil {
   179  					refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema)
   180  					result = append(result, refs...)
   181  				}
   182  
   183  				schema := n.ProvisionerSchemas[p.Type]
   184  				if schema == nil {
   185  					log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name())
   186  				}
   187  				refs, _ = lang.ReferencesInBlock(p.Config, schema)
   188  				result = append(result, refs...)
   189  			}
   190  		}
   191  
   192  		for _, check := range c.Preconditions {
   193  			refs, _ := lang.ReferencesInExpr(check.Condition)
   194  			result = append(result, refs...)
   195  			refs, _ = lang.ReferencesInExpr(check.ErrorMessage)
   196  			result = append(result, refs...)
   197  		}
   198  		for _, check := range c.Postconditions {
   199  			refs, _ := lang.ReferencesInExpr(check.Condition)
   200  			result = append(result, refs...)
   201  			refs, _ = lang.ReferencesInExpr(check.ErrorMessage)
   202  			result = append(result, refs...)
   203  		}
   204  
   205  		return result
   206  	}
   207  
   208  	// Otherwise, we have no references.
   209  	return nil
   210  }
   211  
   212  func (n *NodeAbstractResource) DependsOn() []*addrs.Reference {
   213  	var result []*addrs.Reference
   214  	if c := n.Config; c != nil {
   215  
   216  		for _, traversal := range c.DependsOn {
   217  			ref, diags := addrs.ParseRef(traversal)
   218  			if diags.HasErrors() {
   219  				// We ignore this here, because this isn't a suitable place to return
   220  				// errors. This situation should be caught and rejected during
   221  				// validation.
   222  				log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err())
   223  				continue
   224  			}
   225  
   226  			result = append(result, ref)
   227  		}
   228  	}
   229  	return result
   230  }
   231  
   232  func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) {
   233  	n.ResolvedProvider = p
   234  }
   235  
   236  // GraphNodeProviderConsumer
   237  func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) {
   238  	// Once the provider is fully resolved, we can return the known value.
   239  	if n.ResolvedProvider.Provider.Type != "" {
   240  		return n.ResolvedProvider, true
   241  	}
   242  
   243  	// If we have a config we prefer that above all else
   244  	if n.Config != nil {
   245  		relAddr := n.Config.ProviderConfigAddr()
   246  		return addrs.LocalProviderConfig{
   247  			LocalName: relAddr.LocalName,
   248  			Alias:     relAddr.Alias,
   249  		}, false
   250  	}
   251  
   252  	// See if we have a valid provider config from the state.
   253  	if n.storedProviderConfig.Provider.Type != "" {
   254  		// An address from the state must match exactly, since we must ensure
   255  		// we refresh/destroy a resource with the same provider configuration
   256  		// that created it.
   257  		return n.storedProviderConfig, true
   258  	}
   259  
   260  	// No provider configuration found; return a default address
   261  	return addrs.AbsProviderConfig{
   262  		Provider: n.Provider(),
   263  		Module:   n.ModulePath(),
   264  	}, false
   265  }
   266  
   267  // GraphNodeProviderConsumer
   268  func (n *NodeAbstractResource) Provider() addrs.Provider {
   269  	if n.Config != nil {
   270  		return n.Config.Provider
   271  	}
   272  	if n.storedProviderConfig.Provider.Type != "" {
   273  		return n.storedProviderConfig.Provider
   274  	}
   275  	return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ImpliedProvider())
   276  }
   277  
   278  // GraphNodeProvisionerConsumer
   279  func (n *NodeAbstractResource) ProvisionedBy() []string {
   280  	// If we have no configuration, then we have no provisioners
   281  	if n.Config == nil || n.Config.Managed == nil {
   282  		return nil
   283  	}
   284  
   285  	// Build the list of provisioners we need based on the configuration.
   286  	// It is okay to have duplicates here.
   287  	result := make([]string, len(n.Config.Managed.Provisioners))
   288  	for i, p := range n.Config.Managed.Provisioners {
   289  		result[i] = p.Type
   290  	}
   291  
   292  	return result
   293  }
   294  
   295  // GraphNodeProvisionerConsumer
   296  func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *configschema.Block) {
   297  	if n.ProvisionerSchemas == nil {
   298  		n.ProvisionerSchemas = make(map[string]*configschema.Block)
   299  	}
   300  	n.ProvisionerSchemas[name] = schema
   301  }
   302  
   303  // GraphNodeResource
   304  func (n *NodeAbstractResource) ResourceAddr() addrs.ConfigResource {
   305  	return n.Addr
   306  }
   307  
   308  // GraphNodeTargetable
   309  func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) {
   310  	n.Targets = targets
   311  }
   312  
   313  // graphNodeAttachDataResourceDependsOn
   314  func (n *NodeAbstractResource) AttachDataResourceDependsOn(deps []addrs.ConfigResource, force bool) {
   315  	n.dependsOn = deps
   316  	n.forceDependsOn = force
   317  }
   318  
   319  // GraphNodeAttachResourceConfig
   320  func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource) {
   321  	n.Config = c
   322  }
   323  
   324  // GraphNodeAttachResourceSchema impl
   325  func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) {
   326  	n.Schema = schema
   327  	n.SchemaVersion = version
   328  }
   329  
   330  // GraphNodeAttachProviderMetaConfigs impl
   331  func (n *NodeAbstractResource) AttachProviderMetaConfigs(c map[addrs.Provider]*configs.ProviderMeta) {
   332  	n.ProviderMetas = c
   333  }
   334  
   335  // GraphNodeDotter impl.
   336  func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   337  	return &dag.DotNode{
   338  		Name: name,
   339  		Attrs: map[string]string{
   340  			"label": n.Name(),
   341  			"shape": "box",
   342  		},
   343  	}
   344  }
   345  
   346  // writeResourceState ensures that a suitable resource-level state record is
   347  // present in the state, if that's required for the "each mode" of that
   348  // resource.
   349  //
   350  // This is important primarily for the situation where count = 0, since this
   351  // eval is the only change we get to set the resource "each mode" to list
   352  // in that case, allowing expression evaluation to see it as a zero-element list
   353  // rather than as not set at all.
   354  func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.AbsResource) (diags tfdiags.Diagnostics) {
   355  	state := ctx.State()
   356  
   357  	// We'll record our expansion decision in the shared "expander" object
   358  	// so that later operations (i.e. DynamicExpand and expression evaluation)
   359  	// can refer to it. Since this node represents the abstract module, we need
   360  	// to expand the module here to create all resources.
   361  	expander := ctx.InstanceExpander()
   362  
   363  	switch {
   364  	case n.Config.Count != nil:
   365  		count, countDiags := evaluateCountExpression(n.Config.Count, ctx)
   366  		diags = diags.Append(countDiags)
   367  		if countDiags.HasErrors() {
   368  			return diags
   369  		}
   370  
   371  		state.SetResourceProvider(addr, n.ResolvedProvider)
   372  		expander.SetResourceCount(addr.Module, n.Addr.Resource, count)
   373  
   374  	case n.Config.ForEach != nil:
   375  		forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx)
   376  		diags = diags.Append(forEachDiags)
   377  		if forEachDiags.HasErrors() {
   378  			return diags
   379  		}
   380  
   381  		// This method takes care of all of the business logic of updating this
   382  		// while ensuring that any existing instances are preserved, etc.
   383  		state.SetResourceProvider(addr, n.ResolvedProvider)
   384  		expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach)
   385  
   386  	default:
   387  		state.SetResourceProvider(addr, n.ResolvedProvider)
   388  		expander.SetResourceSingle(addr.Module, n.Addr.Resource)
   389  	}
   390  
   391  	return diags
   392  }
   393  
   394  // readResourceInstanceState reads the current object for a specific instance in
   395  // the state.
   396  func (n *NodeAbstractResource) readResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
   397  	var diags tfdiags.Diagnostics
   398  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   399  	if err != nil {
   400  		diags = diags.Append(err)
   401  		return nil, diags
   402  	}
   403  
   404  	log.Printf("[TRACE] readResourceInstanceState: reading state for %s", addr)
   405  
   406  	src := ctx.State().ResourceInstanceObject(addr, states.CurrentGen)
   407  	if src == nil {
   408  		// Presumably we only have deposed objects, then.
   409  		log.Printf("[TRACE] readResourceInstanceState: no state present for %s", addr)
   410  		return nil, nil
   411  	}
   412  
   413  	schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
   414  	if schema == nil {
   415  		// Shouldn't happen since we should've failed long ago if no schema is present
   416  		return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr))
   417  	}
   418  	src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion)
   419  	if n.Config != nil {
   420  		upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String())
   421  	}
   422  	diags = diags.Append(upgradeDiags)
   423  	if diags.HasErrors() {
   424  		return nil, diags
   425  	}
   426  
   427  	obj, err := src.Decode(schema.ImpliedType())
   428  	if err != nil {
   429  		diags = diags.Append(err)
   430  	}
   431  
   432  	return obj, diags
   433  }
   434  
   435  // readResourceInstanceStateDeposed reads the deposed object for a specific
   436  // instance in the state.
   437  func (n *NodeAbstractResource) readResourceInstanceStateDeposed(ctx EvalContext, addr addrs.AbsResourceInstance, key states.DeposedKey) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
   438  	var diags tfdiags.Diagnostics
   439  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   440  	if err != nil {
   441  		diags = diags.Append(err)
   442  		return nil, diags
   443  	}
   444  
   445  	if key == states.NotDeposed {
   446  		return nil, diags.Append(fmt.Errorf("readResourceInstanceStateDeposed used with no instance key; this is a bug in Terraform and should be reported"))
   447  	}
   448  
   449  	log.Printf("[TRACE] readResourceInstanceStateDeposed: reading state for %s deposed object %s", addr, key)
   450  
   451  	src := ctx.State().ResourceInstanceObject(addr, key)
   452  	if src == nil {
   453  		// Presumably we only have deposed objects, then.
   454  		log.Printf("[TRACE] readResourceInstanceStateDeposed: no state present for %s deposed object %s", addr, key)
   455  		return nil, diags
   456  	}
   457  
   458  	schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
   459  	if schema == nil {
   460  		// Shouldn't happen since we should've failed long ago if no schema is present
   461  		return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr))
   462  
   463  	}
   464  
   465  	src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion)
   466  	if n.Config != nil {
   467  		upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String())
   468  	}
   469  	diags = diags.Append(upgradeDiags)
   470  	if diags.HasErrors() {
   471  		// Note that we don't have any channel to return warnings here. We'll
   472  		// accept that for now since warnings during a schema upgrade would
   473  		// be pretty weird anyway, since this operation is supposed to seem
   474  		// invisible to the user.
   475  		return nil, diags
   476  	}
   477  
   478  	obj, err := src.Decode(schema.ImpliedType())
   479  	if err != nil {
   480  		diags = diags.Append(err)
   481  	}
   482  
   483  	return obj, diags
   484  }
   485  
   486  // graphNodesAreResourceInstancesInDifferentInstancesOfSameModule is an
   487  // annoyingly-task-specific helper function that returns true if and only if
   488  // the following conditions hold:
   489  //   - Both of the given vertices represent specific resource instances, as
   490  //     opposed to unexpanded resources or any other non-resource-related object.
   491  //   - The module instance addresses for both of the resource instances belong
   492  //     to the same static module.
   493  //   - The module instance addresses for both of the resource instances are
   494  //     not equal, indicating that they belong to different instances of the
   495  //     same module.
   496  //
   497  // This result can be used as a way to compensate for the effects of
   498  // conservative analysis passes in our graph builders which make their
   499  // decisions based only on unexpanded addresses, often so that they can behave
   500  // correctly for interactions between expanded and not-yet-expanded objects.
   501  //
   502  // Callers of this helper function will typically skip adding an edge between
   503  // the two given nodes if this function returns true.
   504  func graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(a, b dag.Vertex) bool {
   505  	aRI, aOK := a.(GraphNodeResourceInstance)
   506  	bRI, bOK := b.(GraphNodeResourceInstance)
   507  	if !(aOK && bOK) {
   508  		return false
   509  	}
   510  	aModInst := aRI.ResourceInstanceAddr().Module
   511  	bModInst := bRI.ResourceInstanceAddr().Module
   512  	aMod := aModInst.Module()
   513  	bMod := bModInst.Module()
   514  	if !aMod.Equal(bMod) {
   515  		return false
   516  	}
   517  	return !aModInst.Equal(bModInst)
   518  }