mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
71e989ba3e
The value-conversion machinery is also needed in the main "terraform" package to help us populate our HCL2 evaluation scope, so a subset of the shim functions move here into a new package where they can be public. Some of them remain private within the config package since they depend on some other symbols in the config package, and they are not needed by outside callers anyway.
135 lines
4.5 KiB
Go
135 lines
4.5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/zclconf/go-cty/cty/function/stdlib"
|
|
|
|
"github.com/hashicorp/hil/ast"
|
|
"github.com/hashicorp/terraform/config/hcl2shim"
|
|
|
|
hcl2 "github.com/hashicorp/hcl2/hcl"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
"github.com/zclconf/go-cty/cty/function"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// This file contains some helper functions that are used to shim between
|
|
// HCL2 concepts and HCL/HIL concepts, to help us mostly preserve the existing
|
|
// public API that was built around HCL/HIL-oriented approaches.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func hcl2InterpolationFuncs() map[string]function.Function {
|
|
hcl2Funcs := map[string]function.Function{}
|
|
|
|
for name, hilFunc := range Funcs() {
|
|
hcl2Funcs[name] = hcl2InterpolationFuncShim(hilFunc)
|
|
}
|
|
|
|
// Some functions in the old world are dealt with inside langEvalConfig
|
|
// due to their legacy reliance on direct access to the symbol table.
|
|
// Since 0.7 they don't actually need it anymore and just ignore it,
|
|
// so we're cheating a bit here and exploiting that detail by passing nil.
|
|
hcl2Funcs["lookup"] = hcl2InterpolationFuncShim(interpolationFuncLookup(nil))
|
|
hcl2Funcs["keys"] = hcl2InterpolationFuncShim(interpolationFuncKeys(nil))
|
|
hcl2Funcs["values"] = hcl2InterpolationFuncShim(interpolationFuncValues(nil))
|
|
|
|
// As a bonus, we'll provide the JSON-handling functions from the cty
|
|
// function library since its "jsonencode" is more complete (doesn't force
|
|
// weird type conversions) and HIL's type system can't represent
|
|
// "jsondecode" at all. The result of jsondecode will eventually be forced
|
|
// to conform to the HIL type system on exit into the rest of Terraform due
|
|
// to our shimming right now, but it should be usable for decoding _within_
|
|
// an expression.
|
|
hcl2Funcs["jsonencode"] = stdlib.JSONEncodeFunc
|
|
hcl2Funcs["jsondecode"] = stdlib.JSONDecodeFunc
|
|
|
|
return hcl2Funcs
|
|
}
|
|
|
|
func hcl2InterpolationFuncShim(hilFunc ast.Function) function.Function {
|
|
spec := &function.Spec{}
|
|
|
|
for i, hilArgType := range hilFunc.ArgTypes {
|
|
spec.Params = append(spec.Params, function.Parameter{
|
|
Type: hcl2shim.HCL2TypeForHILType(hilArgType),
|
|
Name: fmt.Sprintf("arg%d", i+1), // HIL args don't have names, so we'll fudge it
|
|
})
|
|
}
|
|
|
|
if hilFunc.Variadic {
|
|
spec.VarParam = &function.Parameter{
|
|
Type: hcl2shim.HCL2TypeForHILType(hilFunc.VariadicType),
|
|
Name: "varargs", // HIL args don't have names, so we'll fudge it
|
|
}
|
|
}
|
|
|
|
spec.Type = func(args []cty.Value) (cty.Type, error) {
|
|
return hcl2shim.HCL2TypeForHILType(hilFunc.ReturnType), nil
|
|
}
|
|
spec.Impl = func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
|
hilArgs := make([]interface{}, len(args))
|
|
for i, arg := range args {
|
|
hilV := hcl2shim.HILVariableFromHCL2Value(arg)
|
|
|
|
// Although the cty function system does automatic type conversions
|
|
// to match the argument types, cty doesn't distinguish int and
|
|
// float and so we may need to adjust here to ensure that the
|
|
// wrapped function gets exactly the Go type it was expecting.
|
|
var wantType ast.Type
|
|
if i < len(hilFunc.ArgTypes) {
|
|
wantType = hilFunc.ArgTypes[i]
|
|
} else {
|
|
wantType = hilFunc.VariadicType
|
|
}
|
|
switch {
|
|
case hilV.Type == ast.TypeInt && wantType == ast.TypeFloat:
|
|
hilV.Type = wantType
|
|
hilV.Value = float64(hilV.Value.(int))
|
|
case hilV.Type == ast.TypeFloat && wantType == ast.TypeInt:
|
|
hilV.Type = wantType
|
|
hilV.Value = int(hilV.Value.(float64))
|
|
}
|
|
|
|
// HIL functions actually expect to have the outermost variable
|
|
// "peeled" but any nested values (in lists or maps) will
|
|
// still have their ast.Variable wrapping.
|
|
hilArgs[i] = hilV.Value
|
|
}
|
|
|
|
hilResult, err := hilFunc.Callback(hilArgs)
|
|
if err != nil {
|
|
return cty.DynamicVal, err
|
|
}
|
|
|
|
// Just as on the way in, we get back a partially-peeled ast.Variable
|
|
// which we need to re-wrap in order to convert it back into what
|
|
// we're calling a "config value".
|
|
rv := hcl2shim.HCL2ValueFromHILVariable(ast.Variable{
|
|
Type: hilFunc.ReturnType,
|
|
Value: hilResult,
|
|
})
|
|
|
|
return convert.Convert(rv, retType) // if result is unknown we'll force the correct type here
|
|
}
|
|
return function.New(spec)
|
|
}
|
|
|
|
func hcl2EvalWithUnknownVars(expr hcl2.Expression) (cty.Value, hcl2.Diagnostics) {
|
|
trs := expr.Variables()
|
|
vars := map[string]cty.Value{}
|
|
val := cty.DynamicVal
|
|
|
|
for _, tr := range trs {
|
|
name := tr.RootName()
|
|
vars[name] = val
|
|
}
|
|
|
|
ctx := &hcl2.EvalContext{
|
|
Variables: vars,
|
|
Functions: hcl2InterpolationFuncs(),
|
|
}
|
|
return expr.Value(ctx)
|
|
}
|