github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/transform_module_variable.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/internal/addrs"
     7  	"github.com/hashicorp/terraform/internal/tfdiags"
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/hashicorp/hcl/v2"
    11  	"github.com/hashicorp/terraform/internal/configs"
    12  )
    13  
    14  // ModuleVariableTransformer is a GraphTransformer that adds all the variables
    15  // in the configuration to the graph.
    16  //
    17  // Any "variable" block present in any non-root module is included here, even
    18  // if a particular variable is not referenced from anywhere.
    19  //
    20  // The transform will produce errors if a call to a module does not conform
    21  // to the expected set of arguments, but this transformer is not in a good
    22  // position to return errors and so the validate walk should include specific
    23  // steps for validating module blocks, separate from this transform.
    24  type ModuleVariableTransformer struct {
    25  	Config *configs.Config
    26  }
    27  
    28  func (t *ModuleVariableTransformer) Transform(g *Graph) error {
    29  	return t.transform(g, nil, t.Config)
    30  }
    31  
    32  func (t *ModuleVariableTransformer) transform(g *Graph, parent, c *configs.Config) error {
    33  	// We can have no variables if we have no configuration.
    34  	if c == nil {
    35  		return nil
    36  	}
    37  
    38  	// Transform all the children first.
    39  	for _, cc := range c.Children {
    40  		if err := t.transform(g, c, cc); err != nil {
    41  			return err
    42  		}
    43  	}
    44  
    45  	// If we're processing anything other than the root module then we'll
    46  	// add graph nodes for variables defined inside. (Variables for the root
    47  	// module are dealt with in RootVariableTransformer).
    48  	// If we have a parent, we can determine if a module variable is being
    49  	// used, so we transform this.
    50  	if parent != nil {
    51  		if err := t.transformSingle(g, parent, c); err != nil {
    52  			return err
    53  		}
    54  	}
    55  
    56  	return nil
    57  }
    58  
    59  func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs.Config) error {
    60  	_, call := c.Path.Call()
    61  
    62  	// Find the call in the parent module configuration, so we can get the
    63  	// expressions given for each input variable at the call site.
    64  	callConfig, exists := parent.Module.ModuleCalls[call.Name]
    65  	if !exists {
    66  		// This should never happen, since it indicates an improperly-constructed
    67  		// configuration tree.
    68  		panic(fmt.Errorf("no module call block found for %s", c.Path))
    69  	}
    70  
    71  	// We need to construct a schema for the expected call arguments based on
    72  	// the configured variables in our config, which we can then use to
    73  	// decode the content of the call block.
    74  	schema := &hcl.BodySchema{}
    75  	for _, v := range c.Module.Variables {
    76  		schema.Attributes = append(schema.Attributes, hcl.AttributeSchema{
    77  			Name:     v.Name,
    78  			Required: v.Default == cty.NilVal,
    79  		})
    80  	}
    81  
    82  	content, contentDiags := callConfig.Config.Content(schema)
    83  	if contentDiags.HasErrors() {
    84  		// Validation code elsewhere should deal with any errors before we
    85  		// get in here, but we'll report them out here just in case, to
    86  		// avoid crashes.
    87  		var diags tfdiags.Diagnostics
    88  		diags = diags.Append(contentDiags)
    89  		return diags.Err()
    90  	}
    91  
    92  	for _, v := range c.Module.Variables {
    93  		var expr hcl.Expression
    94  		if attr := content.Attributes[v.Name]; attr != nil {
    95  			expr = attr.Expr
    96  		}
    97  
    98  		// Add a plannable node, as the variable may expand
    99  		// during module expansion
   100  		node := &nodeExpandModuleVariable{
   101  			Addr: addrs.InputVariable{
   102  				Name: v.Name,
   103  			},
   104  			Module: c.Path,
   105  			Config: v,
   106  			Expr:   expr,
   107  		}
   108  		g.Add(node)
   109  	}
   110  
   111  	return nil
   112  }