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