mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-07 14:44:11 -06:00
36c4d4c241
Previously we had three different layers all thinking they were responsible for substituting a default value for an unset root module variable: - the local backend, via logic in backend.ParseVariableValues - the context.Plan function (and other similar functions) trying to preprocess the input variables using terraform.mergeDefaultInputVariableValues . - the newer prepareFinalInputVariableValue, which aims to centralize all of the variable preparation logic so it can be common to both root and child module variables. The second of these was also trying to handle type constraint checking, which is also the responsibility of the central function and not something we need to handle so early. Only the last of these consistently handles both root and child module variables, and so is the one we ought to keep. The others are now redundant and are causing prepareFinalInputVariableValue to get a slightly corrupted view of the caller's chosen variable values. To rectify that, here we remove the two redundant layers altogether and have unset root variables pass through as cty.NilVal all the way to the central prepareFinalInputVariableValue function, which will then handle them in a suitable way which properly respects the "nullable" setting. This commit includes some test changes in the terraform package to make those tests no longer rely on the mergeDefaultInputVariableValues logic we've removed, and to instead explicitly set cty.NilVal for all unset variables to comply with our intended contract for PlanOpts.SetVariables, and similar. (This is so that we can more easily catch bugs in callers where they _don't_ correctly handle input variables; it allows us to distinguish between the caller explicitly marking a variable as unset vs. not describing it at all, where the latter is a bug in the caller.)
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package terraform
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/providers"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestContextEval(t *testing.T) {
|
|
// This test doesn't check the "Want" value for impure funcs, so the value
|
|
// on those doesn't matter.
|
|
tests := []struct {
|
|
Input string
|
|
Want cty.Value
|
|
ImpureFunc bool
|
|
}{
|
|
{ // An impure function: allowed in the console, but the result is nondeterministic
|
|
`bcrypt("example")`,
|
|
cty.NilVal,
|
|
true,
|
|
},
|
|
{
|
|
`keys(var.map)`,
|
|
cty.ListVal([]cty.Value{
|
|
cty.StringVal("foo"),
|
|
cty.StringVal("baz"),
|
|
}),
|
|
true,
|
|
},
|
|
{
|
|
`local.result`,
|
|
cty.NumberIntVal(6),
|
|
false,
|
|
},
|
|
{
|
|
`module.child.result`,
|
|
cty.NumberIntVal(6),
|
|
false,
|
|
},
|
|
}
|
|
|
|
// This module has a little bit of everything (and if it is missing somehitng, add to it):
|
|
// resources, variables, locals, modules, output
|
|
m := testModule(t, "eval-context-basic")
|
|
p := testProvider("test")
|
|
ctx := testContext2(t, &ContextOpts{
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
|
},
|
|
})
|
|
|
|
scope, diags := ctx.Eval(m, states.NewState(), addrs.RootModuleInstance, &EvalOpts{
|
|
SetVariables: testInputValuesUnset(m.Module.Variables),
|
|
})
|
|
if diags.HasErrors() {
|
|
t.Fatalf("Eval errors: %s", diags.Err())
|
|
}
|
|
|
|
// Since we're testing 'eval' (used by terraform console), impure functions
|
|
// should be allowed by the scope.
|
|
if scope.PureOnly == true {
|
|
t.Fatal("wrong result: eval should allow impure funcs")
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.Input, func(t *testing.T) {
|
|
// Parse the test input as an expression
|
|
expr, _ := hclsyntax.ParseExpression([]byte(test.Input), "<test-input>", hcl.Pos{Line: 1, Column: 1})
|
|
got, diags := scope.EvalExpr(expr, cty.DynamicPseudoType)
|
|
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected error: %s", diags.Err())
|
|
}
|
|
|
|
if !test.ImpureFunc {
|
|
if !got.RawEquals(test.Want) {
|
|
t.Fatalf("wrong result: want %#v, got %#v", test.Want, got)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|