github.com/medzin/terraform@v0.11.11/config/hcl2_shim_util.go (about) 1 package config 2 3 import ( 4 "fmt" 5 6 "github.com/zclconf/go-cty/cty/function/stdlib" 7 8 "github.com/hashicorp/hil/ast" 9 "github.com/hashicorp/terraform/config/hcl2shim" 10 11 hcl2 "github.com/hashicorp/hcl2/hcl" 12 "github.com/zclconf/go-cty/cty" 13 "github.com/zclconf/go-cty/cty/convert" 14 "github.com/zclconf/go-cty/cty/function" 15 ) 16 17 // --------------------------------------------------------------------------- 18 // This file contains some helper functions that are used to shim between 19 // HCL2 concepts and HCL/HIL concepts, to help us mostly preserve the existing 20 // public API that was built around HCL/HIL-oriented approaches. 21 // --------------------------------------------------------------------------- 22 23 func hcl2InterpolationFuncs() map[string]function.Function { 24 hcl2Funcs := map[string]function.Function{} 25 26 for name, hilFunc := range Funcs() { 27 hcl2Funcs[name] = hcl2InterpolationFuncShim(hilFunc) 28 } 29 30 // Some functions in the old world are dealt with inside langEvalConfig 31 // due to their legacy reliance on direct access to the symbol table. 32 // Since 0.7 they don't actually need it anymore and just ignore it, 33 // so we're cheating a bit here and exploiting that detail by passing nil. 34 hcl2Funcs["lookup"] = hcl2InterpolationFuncShim(interpolationFuncLookup(nil)) 35 hcl2Funcs["keys"] = hcl2InterpolationFuncShim(interpolationFuncKeys(nil)) 36 hcl2Funcs["values"] = hcl2InterpolationFuncShim(interpolationFuncValues(nil)) 37 38 // As a bonus, we'll provide the JSON-handling functions from the cty 39 // function library since its "jsonencode" is more complete (doesn't force 40 // weird type conversions) and HIL's type system can't represent 41 // "jsondecode" at all. The result of jsondecode will eventually be forced 42 // to conform to the HIL type system on exit into the rest of Terraform due 43 // to our shimming right now, but it should be usable for decoding _within_ 44 // an expression. 45 hcl2Funcs["jsonencode"] = stdlib.JSONEncodeFunc 46 hcl2Funcs["jsondecode"] = stdlib.JSONDecodeFunc 47 48 return hcl2Funcs 49 } 50 51 func hcl2InterpolationFuncShim(hilFunc ast.Function) function.Function { 52 spec := &function.Spec{} 53 54 for i, hilArgType := range hilFunc.ArgTypes { 55 spec.Params = append(spec.Params, function.Parameter{ 56 Type: hcl2shim.HCL2TypeForHILType(hilArgType), 57 Name: fmt.Sprintf("arg%d", i+1), // HIL args don't have names, so we'll fudge it 58 }) 59 } 60 61 if hilFunc.Variadic { 62 spec.VarParam = &function.Parameter{ 63 Type: hcl2shim.HCL2TypeForHILType(hilFunc.VariadicType), 64 Name: "varargs", // HIL args don't have names, so we'll fudge it 65 } 66 } 67 68 spec.Type = func(args []cty.Value) (cty.Type, error) { 69 return hcl2shim.HCL2TypeForHILType(hilFunc.ReturnType), nil 70 } 71 spec.Impl = func(args []cty.Value, retType cty.Type) (cty.Value, error) { 72 hilArgs := make([]interface{}, len(args)) 73 for i, arg := range args { 74 hilV := hcl2shim.HILVariableFromHCL2Value(arg) 75 76 // Although the cty function system does automatic type conversions 77 // to match the argument types, cty doesn't distinguish int and 78 // float and so we may need to adjust here to ensure that the 79 // wrapped function gets exactly the Go type it was expecting. 80 var wantType ast.Type 81 if i < len(hilFunc.ArgTypes) { 82 wantType = hilFunc.ArgTypes[i] 83 } else { 84 wantType = hilFunc.VariadicType 85 } 86 switch { 87 case hilV.Type == ast.TypeInt && wantType == ast.TypeFloat: 88 hilV.Type = wantType 89 hilV.Value = float64(hilV.Value.(int)) 90 case hilV.Type == ast.TypeFloat && wantType == ast.TypeInt: 91 hilV.Type = wantType 92 hilV.Value = int(hilV.Value.(float64)) 93 } 94 95 // HIL functions actually expect to have the outermost variable 96 // "peeled" but any nested values (in lists or maps) will 97 // still have their ast.Variable wrapping. 98 hilArgs[i] = hilV.Value 99 } 100 101 hilResult, err := hilFunc.Callback(hilArgs) 102 if err != nil { 103 return cty.DynamicVal, err 104 } 105 106 // Just as on the way in, we get back a partially-peeled ast.Variable 107 // which we need to re-wrap in order to convert it back into what 108 // we're calling a "config value". 109 rv := hcl2shim.HCL2ValueFromHILVariable(ast.Variable{ 110 Type: hilFunc.ReturnType, 111 Value: hilResult, 112 }) 113 114 return convert.Convert(rv, retType) // if result is unknown we'll force the correct type here 115 } 116 return function.New(spec) 117 } 118 119 func hcl2EvalWithUnknownVars(expr hcl2.Expression) (cty.Value, hcl2.Diagnostics) { 120 trs := expr.Variables() 121 vars := map[string]cty.Value{} 122 val := cty.DynamicVal 123 124 for _, tr := range trs { 125 name := tr.RootName() 126 vars[name] = val 127 } 128 129 ctx := &hcl2.EvalContext{ 130 Variables: vars, 131 Functions: hcl2InterpolationFuncs(), 132 } 133 return expr.Value(ctx) 134 }