github.com/pulumi/terraform@v1.4.0/pkg/terraform/node_module_expand.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/pulumi/terraform/pkg/addrs"
     7  	"github.com/pulumi/terraform/pkg/configs"
     8  	"github.com/pulumi/terraform/pkg/dag"
     9  	"github.com/pulumi/terraform/pkg/lang"
    10  	"github.com/pulumi/terraform/pkg/tfdiags"
    11  )
    12  
    13  type ConcreteModuleNodeFunc func(n *nodeExpandModule) dag.Vertex
    14  
    15  // nodeExpandModule represents a module call in the configuration that
    16  // might expand into multiple module instances depending on how it is
    17  // configured.
    18  type nodeExpandModule struct {
    19  	Addr       addrs.Module
    20  	Config     *configs.Module
    21  	ModuleCall *configs.ModuleCall
    22  }
    23  
    24  var (
    25  	_ GraphNodeExecutable       = (*nodeExpandModule)(nil)
    26  	_ GraphNodeReferencer       = (*nodeExpandModule)(nil)
    27  	_ GraphNodeReferenceOutside = (*nodeExpandModule)(nil)
    28  	_ graphNodeExpandsInstances = (*nodeExpandModule)(nil)
    29  )
    30  
    31  func (n *nodeExpandModule) expandsInstances() {}
    32  
    33  func (n *nodeExpandModule) Name() string {
    34  	return n.Addr.String() + " (expand)"
    35  }
    36  
    37  // GraphNodeModulePath implementation
    38  func (n *nodeExpandModule) ModulePath() addrs.Module {
    39  	return n.Addr
    40  }
    41  
    42  // GraphNodeReferencer implementation
    43  func (n *nodeExpandModule) References() []*addrs.Reference {
    44  	var refs []*addrs.Reference
    45  
    46  	if n.ModuleCall == nil {
    47  		return nil
    48  	}
    49  
    50  	refs = append(refs, n.DependsOn()...)
    51  
    52  	// Expansion only uses the count and for_each expressions, so this
    53  	// particular graph node only refers to those.
    54  	// Individual variable values in the module call definition might also
    55  	// refer to other objects, but that's handled by
    56  	// NodeApplyableModuleVariable.
    57  	//
    58  	// Because our Path method returns the module instance that contains
    59  	// our call, these references will be correctly interpreted as being
    60  	// in the calling module's namespace, not the namespaces of any of the
    61  	// child module instances we might expand to during our evaluation.
    62  
    63  	if n.ModuleCall.Count != nil {
    64  		countRefs, _ := lang.ReferencesInExpr(n.ModuleCall.Count)
    65  		refs = append(refs, countRefs...)
    66  	}
    67  	if n.ModuleCall.ForEach != nil {
    68  		forEachRefs, _ := lang.ReferencesInExpr(n.ModuleCall.ForEach)
    69  		refs = append(refs, forEachRefs...)
    70  	}
    71  	return refs
    72  }
    73  
    74  func (n *nodeExpandModule) DependsOn() []*addrs.Reference {
    75  	if n.ModuleCall == nil {
    76  		return nil
    77  	}
    78  
    79  	var refs []*addrs.Reference
    80  	for _, traversal := range n.ModuleCall.DependsOn {
    81  		ref, diags := addrs.ParseRef(traversal)
    82  		if diags.HasErrors() {
    83  			// We ignore this here, because this isn't a suitable place to return
    84  			// errors. This situation should be caught and rejected during
    85  			// validation.
    86  			log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err())
    87  			continue
    88  		}
    89  
    90  		refs = append(refs, ref)
    91  	}
    92  
    93  	return refs
    94  }
    95  
    96  // GraphNodeReferenceOutside
    97  func (n *nodeExpandModule) ReferenceOutside() (selfPath, referencePath addrs.Module) {
    98  	return n.Addr, n.Addr.Parent()
    99  }
   100  
   101  // GraphNodeExecutable
   102  func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
   103  	expander := ctx.InstanceExpander()
   104  	_, call := n.Addr.Call()
   105  
   106  	// nodeExpandModule itself does not have visibility into how its ancestors
   107  	// were expanded, so we use the expander here to provide all possible paths
   108  	// to our module, and register module instances with each of them.
   109  	for _, module := range expander.ExpandModule(n.Addr.Parent()) {
   110  		ctx = ctx.WithPath(module)
   111  		switch {
   112  		case n.ModuleCall.Count != nil:
   113  			count, ctDiags := evaluateCountExpression(n.ModuleCall.Count, ctx)
   114  			diags = diags.Append(ctDiags)
   115  			if diags.HasErrors() {
   116  				return diags
   117  			}
   118  			expander.SetModuleCount(module, call, count)
   119  
   120  		case n.ModuleCall.ForEach != nil:
   121  			forEach, feDiags := evaluateForEachExpression(n.ModuleCall.ForEach, ctx)
   122  			diags = diags.Append(feDiags)
   123  			if diags.HasErrors() {
   124  				return diags
   125  			}
   126  			expander.SetModuleForEach(module, call, forEach)
   127  
   128  		default:
   129  			expander.SetModuleSingle(module, call)
   130  		}
   131  	}
   132  
   133  	return diags
   134  
   135  }
   136  
   137  // nodeCloseModule represents an expanded module during apply, and is visited
   138  // after all other module instance nodes. This node will depend on all module
   139  // instance resource and outputs, and anything depending on the module should
   140  // wait on this node.
   141  // Besides providing a root node for dependency ordering, nodeCloseModule also
   142  // cleans up state after all the module nodes have been evaluated, removing
   143  // empty resources and modules from the state.
   144  // The root module instance also closes any remaining provisioner plugins which
   145  // do not have a lifecycle controlled by individual graph nodes.
   146  type nodeCloseModule struct {
   147  	Addr addrs.Module
   148  }
   149  
   150  var (
   151  	_ GraphNodeReferenceable    = (*nodeCloseModule)(nil)
   152  	_ GraphNodeReferenceOutside = (*nodeCloseModule)(nil)
   153  	_ GraphNodeExecutable       = (*nodeCloseModule)(nil)
   154  )
   155  
   156  func (n *nodeCloseModule) ModulePath() addrs.Module {
   157  	return n.Addr
   158  }
   159  
   160  func (n *nodeCloseModule) ReferenceOutside() (selfPath, referencePath addrs.Module) {
   161  	return n.Addr.Parent(), n.Addr
   162  }
   163  
   164  func (n *nodeCloseModule) ReferenceableAddrs() []addrs.Referenceable {
   165  	_, call := n.Addr.Call()
   166  	return []addrs.Referenceable{
   167  		call,
   168  	}
   169  }
   170  
   171  func (n *nodeCloseModule) Name() string {
   172  	if len(n.Addr) == 0 {
   173  		return "root"
   174  	}
   175  	return n.Addr.String() + " (close)"
   176  }
   177  
   178  func (n *nodeCloseModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
   179  	if !n.Addr.IsRoot() {
   180  		return
   181  	}
   182  
   183  	// If this is the root module, we are cleaning up the walk, so close
   184  	// any running provisioners
   185  	diags = diags.Append(ctx.CloseProvisioners())
   186  
   187  	switch op {
   188  	case walkApply, walkDestroy:
   189  		state := ctx.State().Lock()
   190  		defer ctx.State().Unlock()
   191  
   192  		for modKey, mod := range state.Modules {
   193  			// clean out any empty resources
   194  			for resKey, res := range mod.Resources {
   195  				if len(res.Instances) == 0 {
   196  					delete(mod.Resources, resKey)
   197  				}
   198  			}
   199  
   200  			// empty child modules are always removed
   201  			if len(mod.Resources) == 0 && !mod.Addr.IsRoot() {
   202  				delete(state.Modules, modKey)
   203  			}
   204  		}
   205  		return nil
   206  	default:
   207  		return nil
   208  	}
   209  }
   210  
   211  // nodeValidateModule wraps a nodeExpand module for validation, ensuring that
   212  // no expansion is attempted during evaluation, when count and for_each
   213  // expressions may not be known.
   214  type nodeValidateModule struct {
   215  	nodeExpandModule
   216  }
   217  
   218  var _ GraphNodeExecutable = (*nodeValidateModule)(nil)
   219  
   220  // GraphNodeEvalable
   221  func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
   222  	_, call := n.Addr.Call()
   223  	expander := ctx.InstanceExpander()
   224  
   225  	// Modules all evaluate to single instances during validation, only to
   226  	// create a proper context within which to evaluate. All parent modules
   227  	// will be a single instance, but still get our address in the expected
   228  	// manner anyway to ensure they've been registered correctly.
   229  	for _, module := range expander.ExpandModule(n.Addr.Parent()) {
   230  		ctx = ctx.WithPath(module)
   231  
   232  		// Validate our for_each and count expressions at a basic level
   233  		// We skip validation on known, because there will be unknown values before
   234  		// a full expansion, presuming these errors will be caught in later steps
   235  		switch {
   236  		case n.ModuleCall.Count != nil:
   237  			_, countDiags := evaluateCountExpressionValue(n.ModuleCall.Count, ctx)
   238  			diags = diags.Append(countDiags)
   239  
   240  		case n.ModuleCall.ForEach != nil:
   241  			_, forEachDiags := evaluateForEachExpressionValue(n.ModuleCall.ForEach, ctx, true)
   242  			diags = diags.Append(forEachDiags)
   243  		}
   244  
   245  		diags = diags.Append(validateDependsOn(ctx, n.ModuleCall.DependsOn))
   246  
   247  		// now set our own mode to single
   248  		expander.SetModuleSingle(module, call)
   249  	}
   250  
   251  	return diags
   252  }