Run block variable references (#1129)

Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
Christian Mesh 2024-01-17 06:57:14 -05:00 committed by GitHub
parent 4961996f51
commit 249ed42fb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 72 additions and 6 deletions

View File

@ -223,7 +223,7 @@ func (m *Meta) addVarsFromFile(filename string, sourceType tofu.ValueSourceType,
return diags
}
// unparsedVariableValueLiteral is a backend.UnparsedVariableValue
// unparsedVariableValueExpression is a backend.UnparsedVariableValue
// implementation that was actually already parsed (!). This is
// intended to deal with expressions inside "tfvars" files.
type unparsedVariableValueExpression struct {

View File

@ -501,7 +501,7 @@ func (runner *TestFileRunner) ExecuteTestRun(run *moduletest.Run, file *modulete
return state, false
}
variables, resetVariables, variableDiags := prepareInputVariablesForAssertions(config, run, file, runner.Suite.GlobalVariables)
variables, resetVariables, variableDiags := runner.prepareInputVariablesForAssertions(config, run, file, runner.Suite.GlobalVariables)
defer resetVariables()
run.Diagnostics = run.Diagnostics.Append(variableDiags)
@ -574,7 +574,7 @@ func (runner *TestFileRunner) ExecuteTestRun(run *moduletest.Run, file *modulete
return updated, true
}
variables, resetVariables, variableDiags := prepareInputVariablesForAssertions(config, run, file, runner.Suite.GlobalVariables)
variables, resetVariables, variableDiags := runner.prepareInputVariablesForAssertions(config, run, file, runner.Suite.GlobalVariables)
defer resetVariables()
run.Diagnostics = run.Diagnostics.Append(variableDiags)
@ -1023,6 +1023,26 @@ func buildInputVariablesForTest(run *moduletest.Run, file *moduletest.File, conf
return backend.ParseVariableValues(variables, config.Module.Variables)
}
type testVariableValueExpression struct {
expr hcl.Expression
sourceType tofu.ValueSourceType
ctx *hcl.EvalContext
}
func (v testVariableValueExpression) ParseVariableValue(mode configs.VariableParsingMode) (*tofu.InputValue, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
val, hclDiags := v.expr.Value(v.ctx)
diags = diags.Append(hclDiags)
rng := tfdiags.SourceRangeFromHCL(v.expr.Range())
return &tofu.InputValue{
Value: val,
SourceType: v.sourceType,
SourceRange: rng,
}, diags
}
// prepareInputVariablesForAssertions creates a tofu.InputValues mapping
// that contains all the variables defined for a given run and file, alongside
// any unset variables that have defaults within the provided config.
@ -1032,17 +1052,36 @@ func buildInputVariablesForTest(run *moduletest.Run, file *moduletest.File, conf
// within the config. This allows the assertions to refer to variables defined
// solely within the test file, and not only those within the configuration.
//
// It also allows references to previously run test module's outputs as variable
// expressions. This relies upon the evaluation order and will not sort the test cases
// to run in the dependent order.
//
// In addition, it modifies the provided config so that any variables that are
// available are also defined in the config. It returns a function that resets
// the config which must be called so the config can be reused going forward.
func prepareInputVariablesForAssertions(config *configs.Config, run *moduletest.Run, file *moduletest.File, globals map[string]backend.UnparsedVariableValue) (tofu.InputValues, func(), tfdiags.Diagnostics) {
func (runner *TestFileRunner) prepareInputVariablesForAssertions(config *configs.Config, run *moduletest.Run, file *moduletest.File, globals map[string]backend.UnparsedVariableValue) (tofu.InputValues, func(), tfdiags.Diagnostics) {
runCtx := make(map[string]cty.Value)
for _, state := range runner.States {
if state.Run == nil {
continue
}
outputs := make(map[string]cty.Value)
mod := state.State.Modules[""] // Empty string is what is used by the module in the test runner
for outName, out := range mod.OutputValues {
outputs[outName] = out.Value
}
runCtx[state.Run.Name] = cty.ObjectVal(outputs)
}
ctx := &hcl.EvalContext{Variables: map[string]cty.Value{"run": cty.ObjectVal(runCtx)}}
variables := make(map[string]backend.UnparsedVariableValue)
if run != nil {
for name, expr := range run.Config.Variables {
variables[name] = unparsedVariableValueExpression{
variables[name] = testVariableValueExpression{
expr: expr,
sourceType: tofu.ValueFromConfig,
ctx: ctx,
}
}
}
@ -1054,9 +1093,10 @@ func prepareInputVariablesForAssertions(config *configs.Config, run *moduletest.
// that value to take precedence.
continue
}
variables[name] = unparsedVariableValueExpression{
variables[name] = testVariableValueExpression{
expr: expr,
sourceType: tofu.ValueFromConfig,
ctx: ctx,
}
}
}

View File

@ -798,6 +798,10 @@ func TestTest_Modules(t *testing.T) {
expected: "main.tftest.hcl... pass\n run \"first\"... pass\n run \"second\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
code: 0,
},
"variables_reference": {
expected: "main.tftest.hcl... pass\n run \"setup\"... pass\n run \"test\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
code: 0,
},
}
for name, tc := range tcs {

View File

@ -0,0 +1,15 @@
variables {
content = "some value"
}
run "setup" {
module {
source = "./setup"
}
}
run "test" {
variables {
file_name = run.setup.file_name
}
}

View File

@ -0,0 +1,7 @@
variable "content" {
type = string
}
output "file_name" {
value = "output_value"
}