github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/eval_variable.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/hcl/v2" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 10 "github.com/zclconf/go-cty/cty" 11 "github.com/zclconf/go-cty/cty/convert" 12 ) 13 14 // EvalSetModuleCallArguments is an EvalNode implementation that sets values 15 // for arguments of a child module call, for later retrieval during 16 // expression evaluation. 17 type EvalSetModuleCallArguments struct { 18 Module addrs.ModuleCallInstance 19 Values map[string]cty.Value 20 } 21 22 // TODO: test 23 func (n *EvalSetModuleCallArguments) Eval(ctx EvalContext) (interface{}, error) { 24 ctx.SetModuleCallArguments(n.Module, n.Values) 25 return nil, nil 26 } 27 28 // EvalModuleCallArgument is an EvalNode implementation that produces the value 29 // for a particular variable as will be used by a child module instance. 30 // 31 // The result is written into the map given in Values, with its key 32 // set to the local name of the variable, disregarding the module instance 33 // address. Any existing values in that map are deleted first. This weird 34 // interface is a result of trying to be convenient for use with 35 // EvalContext.SetModuleCallArguments, which expects a map to merge in with 36 // any existing arguments. 37 type EvalModuleCallArgument struct { 38 Addr addrs.InputVariable 39 Config *configs.Variable 40 Expr hcl.Expression 41 42 // If this flag is set, any diagnostics are discarded and this operation 43 // will always succeed, though may produce an unknown value in the 44 // event of an error. 45 IgnoreDiagnostics bool 46 47 Values map[string]cty.Value 48 } 49 50 func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) { 51 // Clear out the existing mapping 52 for k := range n.Values { 53 delete(n.Values, k) 54 } 55 56 wantType := n.Config.Type 57 name := n.Addr.Name 58 expr := n.Expr 59 60 if expr == nil { 61 // Should never happen, but we'll bail out early here rather than 62 // crash in case it does. We set no value at all in this case, 63 // making a subsequent call to EvalContext.SetModuleCallArguments 64 // a no-op. 65 log.Printf("[ERROR] attempt to evaluate %s with nil expression", n.Addr.String()) 66 return nil, nil 67 } 68 69 val, diags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) 70 71 // We intentionally passed DynamicPseudoType to EvaluateExpr above because 72 // now we can do our own local type conversion and produce an error message 73 // with better context if it fails. 74 var convErr error 75 val, convErr = convert.Convert(val, wantType) 76 if convErr != nil { 77 diags = diags.Append(&hcl.Diagnostic{ 78 Severity: hcl.DiagError, 79 Summary: "Invalid value for module argument", 80 Detail: fmt.Sprintf( 81 "The given value is not suitable for child module variable %q defined at %s: %s.", 82 name, n.Config.DeclRange.String(), convErr, 83 ), 84 Subject: expr.Range().Ptr(), 85 }) 86 // We'll return a placeholder unknown value to avoid producing 87 // redundant downstream errors. 88 val = cty.UnknownVal(wantType) 89 } 90 91 n.Values[name] = val 92 if n.IgnoreDiagnostics { 93 return nil, nil 94 } 95 return nil, diags.ErrWithWarnings() 96 }