mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 17:01:04 -06:00
39e609d5fd
Previously we were using the experimental HCL 2 repository, but now we'll shift over to the v2 import path within the main HCL repository as part of actually releasing HCL 2.0 as stable. This is a mechanical search/replace to the new import paths. It also switches to the v2.0.0 release of HCL, which includes some new code that Terraform didn't previously have but should not change any behavior that matters for Terraform's purposes. For the moment the experimental HCL2 repository is still an indirect dependency via terraform-config-inspect, so it remains in our go.sum and vendor directories for the moment. Because terraform-config-inspect uses a much smaller subset of the HCL2 functionality, this does still manage to prune the vendor directory a little. A subsequent release of terraform-config-inspect should allow us to completely remove that old repository in a future commit.
121 lines
3.8 KiB
Go
121 lines
3.8 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
)
|
|
|
|
// EvalSetModuleCallArguments is an EvalNode implementation that sets values
|
|
// for arguments of a child module call, for later retrieval during
|
|
// expression evaluation.
|
|
type EvalSetModuleCallArguments struct {
|
|
Module addrs.ModuleCallInstance
|
|
Values map[string]cty.Value
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalSetModuleCallArguments) Eval(ctx EvalContext) (interface{}, error) {
|
|
ctx.SetModuleCallArguments(n.Module, n.Values)
|
|
return nil, nil
|
|
}
|
|
|
|
// EvalModuleCallArgument is an EvalNode implementation that produces the value
|
|
// for a particular variable as will be used by a child module instance.
|
|
//
|
|
// The result is written into the map given in Values, with its key
|
|
// set to the local name of the variable, disregarding the module instance
|
|
// address. Any existing values in that map are deleted first. This weird
|
|
// interface is a result of trying to be convenient for use with
|
|
// EvalContext.SetModuleCallArguments, which expects a map to merge in with
|
|
// any existing arguments.
|
|
type EvalModuleCallArgument struct {
|
|
Addr addrs.InputVariable
|
|
Config *configs.Variable
|
|
Expr hcl.Expression
|
|
|
|
// If this flag is set, any diagnostics are discarded and this operation
|
|
// will always succeed, though may produce an unknown value in the
|
|
// event of an error.
|
|
IgnoreDiagnostics bool
|
|
|
|
Values map[string]cty.Value
|
|
}
|
|
|
|
func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) {
|
|
// Clear out the existing mapping
|
|
for k := range n.Values {
|
|
delete(n.Values, k)
|
|
}
|
|
|
|
wantType := n.Config.Type
|
|
name := n.Addr.Name
|
|
expr := n.Expr
|
|
|
|
if expr == nil {
|
|
// Should never happen, but we'll bail out early here rather than
|
|
// crash in case it does. We set no value at all in this case,
|
|
// making a subsequent call to EvalContext.SetModuleCallArguments
|
|
// a no-op.
|
|
log.Printf("[ERROR] attempt to evaluate %s with nil expression", n.Addr.String())
|
|
return nil, nil
|
|
}
|
|
|
|
val, diags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
|
|
|
|
// We intentionally passed DynamicPseudoType to EvaluateExpr above because
|
|
// now we can do our own local type conversion and produce an error message
|
|
// with better context if it fails.
|
|
var convErr error
|
|
val, convErr = convert.Convert(val, wantType)
|
|
if convErr != nil {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid value for module argument",
|
|
Detail: fmt.Sprintf(
|
|
"The given value is not suitable for child module variable %q defined at %s: %s.",
|
|
name, n.Config.DeclRange.String(), convErr,
|
|
),
|
|
Subject: expr.Range().Ptr(),
|
|
})
|
|
// We'll return a placeholder unknown value to avoid producing
|
|
// redundant downstream errors.
|
|
val = cty.UnknownVal(wantType)
|
|
}
|
|
|
|
n.Values[name] = val
|
|
if n.IgnoreDiagnostics {
|
|
return nil, nil
|
|
}
|
|
return nil, diags.ErrWithWarnings()
|
|
}
|
|
|
|
// hclTypeName returns the name of the type that would represent this value in
|
|
// a config file, or falls back to the Go type name if there's no corresponding
|
|
// HCL type. This is used for formatted output, not for comparing types.
|
|
func hclTypeName(i interface{}) string {
|
|
switch k := reflect.Indirect(reflect.ValueOf(i)).Kind(); k {
|
|
case reflect.Bool:
|
|
return "boolean"
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
|
|
return "number"
|
|
case reflect.Array, reflect.Slice:
|
|
return "list"
|
|
case reflect.Map:
|
|
return "map"
|
|
case reflect.String:
|
|
return "string"
|
|
default:
|
|
// fall back to the Go type if there's no match
|
|
return k.String()
|
|
}
|
|
}
|