github.com/ezh/terraform@v0.11.12-beta1/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  }