Merge pull request #34699 from hashicorp/terraform-test-use-vars-within-blocks

test: allow using global variables in suite-level variable definitions
pull/34724/head
Daniel Schmidt 2 years ago committed by GitHub
commit 8564e934aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -244,7 +244,15 @@ type TestFileRunner struct {
// variables within run blocks.
PriorOutputs map[addrs.Run]cty.Value
globalVariables map[string]backend.UnparsedVariableValue
// globalVariables are globally defined variables, e.g. through tfvars or CLI flags
globalVariables terraform.InputValues
// fileVariables are defined in the variables section of a test file
fileVariables terraform.InputValues
// fileVariableExpressions are the hcl expressions for the fileVariables
fileVariableExpressions map[string]hcl.Expression
// globalAndFileVariables is a combination of globalVariables and fileVariables
// created for convenience
globalAndFileVariables terraform.InputValues
}
// TestFileState is a helper struct that just maps a run block to the state that
@ -260,6 +268,14 @@ func (runner *TestFileRunner) Test(file *moduletest.File) {
// First thing, initialise the global variables for the file
runner.initVariables(file)
vars := make(terraform.InputValues)
for name, value := range runner.globalVariables {
vars[name] = value
}
for name, value := range runner.fileVariables {
vars[name] = value
}
// The file validation only returns warnings so we'll just add them without
// checking anything about them.
file.Diagnostics = file.Diagnostics.Append(file.Config.Validate(runner.Suite.Config))
@ -379,7 +395,7 @@ func (runner *TestFileRunner) run(run *moduletest.Run, file *moduletest.File, st
}
runner.gatherProviders(key, config)
resetConfig, configDiags := configtest.TransformConfigForTest(config, run, file, runner.globalVariables, runner.PriorOutputs, runner.Suite.configProviders[key])
resetConfig, configDiags := configtest.TransformConfigForTest(config, run, file, runner.globalAndFileVariables, runner.PriorOutputs, runner.Suite.configProviders[key])
defer resetConfig()
run.Diagnostics = run.Diagnostics.Append(configDiags)
@ -944,7 +960,7 @@ func (runner *TestFileRunner) cleanup(file *moduletest.File) {
key = state.Run.Config.Module.Source.String()
}
reset, configDiags := configtest.TransformConfigForTest(config, state.Run, file, runner.globalVariables, runner.PriorOutputs, runner.Suite.configProviders[key])
reset, configDiags := configtest.TransformConfigForTest(config, state.Run, file, runner.globalAndFileVariables, runner.PriorOutputs, runner.Suite.configProviders[key])
diags = diags.Append(configDiags)
updated := state.State
@ -992,21 +1008,31 @@ func (runner *TestFileRunner) GetVariables(config *configs.Config, run *modulete
}
}
// Second, we'll check to see which variables the run block variables
// themselves reference. We might be processing variables just for the file
// so the run block itself could be nil.
for _, expr := range run.Config.Variables {
for _, variable := range expr.Variables() {
reference, referenceDiags := addrs.ParseRefFromTestingScope(variable)
diags = diags.Append(referenceDiags)
if reference != nil {
if addr, ok := reference.Subject.(addrs.InputVariable); ok {
relevantVariables[addr.Name] = true
getRelevantVariables := func(src map[string]hcl.Expression) tfdiags.Diagnostics {
var getVarsDiags tfdiags.Diagnostics
for _, expr := range src {
for _, variable := range expr.Variables() {
reference, referenceDiags := addrs.ParseRefFromTestingScope(variable)
getVarsDiags = getVarsDiags.Append(referenceDiags)
if reference != nil {
if addr, ok := reference.Subject.(addrs.InputVariable); ok {
relevantVariables[addr.Name] = true
}
}
}
}
return getVarsDiags
}
// Second, we'll check to see which variables the file variables
// themselves reference.
diags = diags.Append(getRelevantVariables(runner.fileVariableExpressions))
// Third, we'll check to see which variables the run block variables
// themselves reference. We might be processing variables just for the file
// so the run block itself could be nil.
diags = diags.Append(getRelevantVariables(run.Config.Variables))
// Finally, we'll check to see which variables are actually defined within
// the configuration.
for name := range config.Module.Variables {
@ -1023,71 +1049,31 @@ func (runner *TestFileRunner) GetVariables(config *configs.Config, run *modulete
values := make(terraform.InputValues)
// First, let's look at the global variables.
for name, variable := range runner.globalVariables {
for name, value := range runner.globalVariables {
if !relevantVariables[name] {
// Then this run block doesn't need this value.
continue
}
// By default, we parse global variables as HCL inputs.
parsingMode := configs.VariableParseHCL
cfg, exists := config.Module.Variables[name]
if exists {
// Unless we have some configuration that can actually tell us
// what parsing mode to use.
parsingMode = cfg.ParsingMode
}
value, valueDiags := variable.ParseVariableValue(parsingMode)
diags = diags.Append(valueDiags)
if diags.HasErrors() {
// We still add a value for this variable even though we couldn't
// parse it as we don't want to compound errors later. For example,
// the system would report this variable didn't have a value which
// would confuse the user because it does have a value, it's just
// not a valid value. We have added the diagnostics so the user
// will be informed about the error, and the test won't run. We'll
// just report only the relevant errors.
values[name] = &terraform.InputValue{
Value: cty.NilVal,
}
continue
}
values[name] = value
}
// Second, we'll check the run level variables.
// This is a bit more complicated, as the run level variables can reference
// previously defined variables.
// Preload the available expressions, we're going to validate them when we
// build the context.
var exprs []hcl.Expression
for _, expr := range run.Config.Variables {
exprs = append(exprs, expr)
}
// Preformat the variables we've processed already - these will be made
// available to the eval context.
variables := make(map[string]cty.Value)
for name, value := range values {
variables[name] = value.Value
// We don't care if the file level variables are relevant or not
ignoreRelevance := func(name string, expr hcl.Expression) (diags tfdiags.Diagnostics) {
return diags
}
ctx, ctxDiags := hcltest.EvalContext(hcltest.TargetRunBlock, exprs, variables, runner.PriorOutputs)
diags = diags.Append(ctxDiags)
var failedContext bool
if ctxDiags.HasErrors() {
// If we couldn't build the context, we won't actually process these
// variables. Instead, we'll fill them with an empty value but still
// make a note that the user did provide them.
failedContext = true
// Second, we'll check the file level variables
// This is a bit more complicated, as the file and run level variables can reference
// previously defined variables.
fileValues, fileDiags := runner.getVariablesFromConfiguration(values, ignoreRelevance, runner.fileVariableExpressions)
diags = diags.Append(fileDiags)
for name, value := range fileValues {
values[name] = value
}
for name, expr := range run.Config.Variables {
// We want to make sure every variable declared in the run block is actually relevant.
validateRelevance := func(name string, expr hcl.Expression) (diags tfdiags.Diagnostics) {
if !relevantVariables[name] {
// We'll add a warning for this. Since we're right in the run block
// users shouldn't be defining variables that are not relevant.
@ -1097,21 +1083,15 @@ func (runner *TestFileRunner) GetVariables(config *configs.Config, run *modulete
Detail: fmt.Sprintf("The module under test does not declare a variable named %q, but it is declared in run block %q.", name, run.Name),
Subject: expr.Range().Ptr(),
})
continue
}
value := cty.NilVal
if !failedContext {
var valueDiags hcl.Diagnostics
value, valueDiags = expr.Value(ctx)
diags = diags.Append(valueDiags)
}
return diags
}
values[name] = &terraform.InputValue{
Value: value,
SourceType: terraform.ValueFromConfig,
SourceRange: tfdiags.SourceRangeFromHCL(expr.Range()),
}
// Third, we'll check the run level variables.
runValues, runDiags := runner.getVariablesFromConfiguration(values, validateRelevance, run.Config.Variables)
diags = diags.Append(runDiags)
for name, value := range runValues {
values[name] = value
}
// Finally, we check the configuration again. This is where we'll discover
@ -1149,12 +1129,94 @@ func (runner *TestFileRunner) GetVariables(config *configs.Config, run *modulete
SourceRange: tfdiags.SourceRangeFromHCL(variable.DeclRange),
}
}
}
return values, diags
}
func (runner *TestFileRunner) getGlobalVariable(name string, variable backend.UnparsedVariableValue, config *configs.Config) *terraform.InputValue {
// By default, we parse global variables as HCL inputs.
parsingMode := configs.VariableParseHCL
cfg, exists := config.Module.Variables[name]
if exists {
// Unless we have some configuration that can actually tell us
// what parsing mode to use.
parsingMode = cfg.ParsingMode
}
value, diags := variable.ParseVariableValue(parsingMode)
if diags.HasErrors() {
// We still add a value for this variable even though we couldn't
// parse it as we don't want to compound errors later. For example,
// the system would report this variable didn't have a value which
// would confuse the user because it does have a value, it's just
// not a valid value. We have added the diagnostics so the user
// will be informed about the error, and the test won't run. We'll
// just report only the relevant errors.
return &terraform.InputValue{
Value: cty.NilVal,
}
}
return value
}
// getVariablesFromConfiguration will process the variables from the configuration
// and return a map of the variables and their values.
func (runner *TestFileRunner) getVariablesFromConfiguration(knownVariables terraform.InputValues, validateRelevance func(string, hcl.Expression) tfdiags.Diagnostics, variableConfig map[string]hcl.Expression) (terraform.InputValues, tfdiags.Diagnostics) {
var exprs []hcl.Expression
var diags tfdiags.Diagnostics
variableValues := make(terraform.InputValues)
// Preload the available expressions, we're going to validate them when we
// build the context.
for _, expr := range variableConfig {
exprs = append(exprs, expr)
}
// Preformat the variables we've processed already - these will be made
// available to the eval context.
variables := make(map[string]cty.Value)
for name, value := range knownVariables {
variables[name] = value.Value
}
ctx, ctxDiags := hcltest.EvalContext(hcltest.TargetRunBlock, exprs, variables, runner.PriorOutputs)
diags = diags.Append(ctxDiags)
var failedContext bool
if ctxDiags.HasErrors() {
// If we couldn't build the context, we won't actually process these
// variables. Instead, we'll fill them with an empty value but still
// make a note that the user did provide them.
failedContext = true
}
for name, expr := range variableConfig {
relevanceDiags := validateRelevance(name, expr)
diags = diags.Append(relevanceDiags)
if len(relevanceDiags) > 0 {
continue
}
value := cty.NilVal
if !failedContext {
var valueDiags hcl.Diagnostics
value, valueDiags = expr.Value(ctx)
diags = diags.Append(valueDiags)
}
variableValues[name] = &terraform.InputValue{
Value: value,
SourceType: terraform.ValueFromConfig,
SourceRange: tfdiags.SourceRangeFromHCL(expr.Range()),
}
}
return variableValues, diags
}
// FilterVariablesToModule splits the provided values into two disjoint maps:
// moduleVars contains the ones that correspond with declarations in the root
// module of the given configuration, while testOnlyVars contains any others
@ -1248,20 +1310,44 @@ func (runner *TestFileRunner) AddVariablesToConfig(config *configs.Config, varia
// merging the global variables from the test suite into the variables from
// the file.
func (runner *TestFileRunner) initVariables(file *moduletest.File) {
runner.globalVariables = make(map[string]backend.UnparsedVariableValue)
// First, we get the global variables from the suite and test suite
runner.globalVariables = make(terraform.InputValues)
for name, value := range runner.Suite.GlobalVariables {
runner.globalVariables[name] = value
runner.globalVariables[name] = runner.getGlobalVariable(name, value, runner.Suite.Config)
}
if filepath.Dir(file.Name) == runner.Suite.TestingDirectory {
// If the file is in the testing directory, then also include any
// variables that are defined within the default variable file also in
// the test directory.
for name, value := range runner.Suite.GlobalTestVariables {
runner.globalVariables[name] = value
runner.globalVariables[name] = runner.getGlobalVariable(name, value, runner.Suite.Config)
}
}
// Second, we collect the variable expressions so they can later be used to
// check for references to variables that are also relevant
runner.fileVariableExpressions = make(map[string]hcl.Expression)
for name, expr := range file.Config.Variables {
runner.globalVariables[name] = unparsedTestVariableValue{expr}
runner.fileVariableExpressions[name] = expr
}
// Third, we get the variables from the file
runner.fileVariables = make(terraform.InputValues)
fileValues, fileDiags := runner.getVariablesFromConfiguration(runner.globalVariables, func(s string, e hcl.Expression) tfdiags.Diagnostics { return tfdiags.Diagnostics{} }, runner.fileVariableExpressions)
for name, value := range fileValues {
runner.fileVariables[name] = value
}
file.Diagnostics = file.Diagnostics.Append(fileDiags)
// Finally, we merge the global and file variables together to get all
// available variables outside the run specific ones
runner.globalAndFileVariables = make(terraform.InputValues)
for name, value := range runner.globalVariables {
runner.globalAndFileVariables[name] = value
}
for name, value := range runner.fileVariables {
runner.globalAndFileVariables[name] = value
}
}

@ -26,7 +26,7 @@ func TestTest_Runs(t *testing.T) {
override string
args []string
expectedOut string
expectedErr string
expectedErr []string
expectedResourceCount int
code int
initCode int
@ -59,13 +59,13 @@ func TestTest_Runs(t *testing.T) {
"simple_pass_bad_test_directory": {
override: "simple_pass",
args: []string{"-test-directory", "../tests"},
expectedErr: "Invalid testing directory",
expectedErr: []string{"Invalid testing directory"},
code: 1,
},
"simple_pass_bad_test_directory_abs": {
override: "simple_pass",
args: []string{"-test-directory", "/home/username/config/tests"},
expectedErr: "Invalid testing directory",
expectedErr: []string{"Invalid testing directory"},
code: 1,
},
"pass_with_locals": {
@ -118,32 +118,32 @@ func TestTest_Runs(t *testing.T) {
override: "variables",
args: []string{"-var=input=foo"},
expectedOut: "1 passed, 1 failed",
expectedErr: `invalid value`,
expectedErr: []string{`invalid value`},
code: 1,
},
"simple_fail": {
expectedOut: "0 passed, 1 failed.",
expectedErr: "invalid value",
expectedErr: []string{"invalid value"},
code: 1,
},
"custom_condition_checks": {
expectedOut: "0 passed, 1 failed.",
expectedErr: "this really should fail",
expectedErr: []string{"this really should fail"},
code: 1,
},
"custom_condition_inputs": {
expectedOut: "0 passed, 1 failed.",
expectedErr: "this should definitely fail",
expectedErr: []string{"this should definitely fail"},
code: 1,
},
"custom_condition_outputs": {
expectedOut: "0 passed, 1 failed.",
expectedErr: "this should fail",
expectedErr: []string{"this should fail"},
code: 1,
},
"custom_condition_resources": {
expectedOut: "0 passed, 1 failed.",
expectedErr: "this really should fail",
expectedErr: []string{"this really should fail"},
code: 1,
},
"no_providers_in_main": {
@ -190,7 +190,7 @@ func TestTest_Runs(t *testing.T) {
},
"destroy_fail": {
expectedOut: "1 passed, 0 failed.",
expectedErr: `Terraform left the following resources in state`,
expectedErr: []string{`Terraform left the following resources in state`},
code: 1,
expectedResourceCount: 1,
},
@ -217,7 +217,7 @@ func TestTest_Runs(t *testing.T) {
code: 0,
},
"mocking-invalid": {
expectedErr: "Invalid outputs attribute",
expectedErr: []string{"Invalid outputs attribute"},
initCode: 1,
},
"dangling_data_block": {
@ -234,9 +234,13 @@ func TestTest_Runs(t *testing.T) {
},
"global_var_refs": {
expectedOut: "2 failed, 1 skipped.",
expectedErr: "Variables may not be used here.",
expectedErr: []string{"The input variable \"env_var_input\" is not available to the current context", "The input variable \"setup\" is not available to the current context"},
code: 1,
},
"global_var_ref_in_suite_var": {
expectedOut: "1 passed, 0 failed.",
code: 0,
},
}
for name, tc := range tcs {
t.Run(name, func(t *testing.T) {
@ -289,9 +293,13 @@ func TestTest_Runs(t *testing.T) {
t.Errorf("output didn't contain expected string:\n\n%s", stdout)
}
if !strings.Contains(stderr, tc.expectedErr) {
t.Errorf("error didn't contain expected string:\n\n%s", stderr)
} else if tc.expectedErr == "" && stderr != "" {
if len(tc.expectedErr) > 0 {
for _, expectedErr := range tc.expectedErr {
if !strings.Contains(stderr, expectedErr) {
t.Errorf("error didn't contain expected string:\n\n%s", stderr)
}
}
} else if stderr != "" {
t.Errorf("unexpected stderr output\n%s", stderr)
}
@ -316,9 +324,13 @@ func TestTest_Runs(t *testing.T) {
t.Errorf("output didn't contain expected string:\n\n%s", output.Stdout())
}
if !strings.Contains(output.Stderr(), tc.expectedErr) {
t.Errorf("error didn't contain expected string:\n\n%s", output.Stderr())
} else if tc.expectedErr == "" && output.Stderr() != "" {
if len(tc.expectedErr) > 0 {
for _, expectedErr := range tc.expectedErr {
if !strings.Contains(output.Stderr(), expectedErr) {
t.Errorf("error didn't contain expected string:\n\n%s", output.Stderr())
}
}
} else if output.Stderr() != "" {
t.Errorf("unexpected stderr output\n%s", output.Stderr())
}
@ -1161,7 +1173,7 @@ requested in the configuration may have been ignored and the output values
may not be fully updated. Run the following command to verify that no other
changes are pending:
terraform plan
Note that the -target option is not suitable for routine use, and is provided
only for exceptional situations such as recovering from errors or mistakes,
or when Terraform specifically suggests to use it as part of an error
@ -1240,9 +1252,10 @@ Error: Reference to unavailable variable
on main.tftest.hcl line 15, in run "test":
15: input_one = var.notreal
The input variable "notreal" is not available to the current run block. You
can only reference variables defined at the file or global levels when
populating the variables block within a run block.
The input variable "notreal" is not available to the current context. Within
the variables block of a run block you can only reference variables defined
at the file or global levels; within the variables block of a suite you can
only reference variables defined at the global levels.
Error: Reference to unavailable run block
@ -1267,9 +1280,10 @@ Error: Reference to unavailable variable
on providers.tftest.hcl line 3, in provider "test":
3: resource_prefix = var.default
The input variable "default" is not available to the current run block. You
can only reference variables defined at the file or global levels when
populating the variables block within a run block.
The input variable "default" is not available to the current context. Within
the variables block of a run block you can only reference variables defined
at the file or global levels; within the variables block of a suite you can
only reference variables defined at the global levels.
`
actualErr := output.Stderr()
if diff := cmp.Diff(actualErr, expectedErr); len(diff) > 0 {

@ -0,0 +1,10 @@
variable "input" {
default = null
type = object({
organization_name = string
})
}
output "value" {
value = var.input
}

@ -0,0 +1,13 @@
variables {
input = {
organization_name = var.org_name
}
}
run "execute" {
assert {
condition = output.value.organization_name == "my-org"
error_message = "bad output value"
}
}

@ -10,10 +10,10 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/moduletest"
hcltest "github.com/hashicorp/terraform/internal/moduletest/hcl"
"github.com/hashicorp/terraform/internal/terraform"
)
// TransformConfigForTest transforms the provided configuration ready for the
@ -27,7 +27,7 @@ import (
// We also return a reset function that should be called to return the
// configuration to it's original state before the next run block or test file
// needs to use it.
func TransformConfigForTest(config *configs.Config, run *moduletest.Run, file *moduletest.File, availableVariables map[string]backend.UnparsedVariableValue, availableRunOutputs map[addrs.Run]cty.Value, requiredProviders map[string]bool) (func(), hcl.Diagnostics) {
func TransformConfigForTest(config *configs.Config, run *moduletest.Run, file *moduletest.File, availableVariables terraform.InputValues, availableRunOutputs map[addrs.Run]cty.Value, requiredProviders map[string]bool) (func(), hcl.Diagnostics) {
var diags hcl.Diagnostics
// Currently, we only need to override the provider settings.
@ -91,7 +91,6 @@ func TransformConfigForTest(config *configs.Config, run *moduletest.Run, file *m
Version: testProvider.Version,
Config: &hcltest.ProviderConfig{
Original: testProvider.Config,
ConfigVariables: config.Module.Variables,
AvailableVariables: availableVariables,
AvailableRunOutputs: availableRunOutputs,
},
@ -119,7 +118,6 @@ func TransformConfigForTest(config *configs.Config, run *moduletest.Run, file *m
Version: provider.Version,
Config: &hcltest.ProviderConfig{
Original: provider.Config,
ConfigVariables: config.Module.Variables,
AvailableVariables: availableVariables,
AvailableRunOutputs: availableRunOutputs,
},

@ -132,7 +132,7 @@ func EvalContext(target EvalContextTarget, expressions []hcl.Expression, availab
if _, exists := availableVariables[addr.Name]; !exists {
// This variable reference doesn't exist.
detail := fmt.Sprintf("The input variable %q is not available to the current run block. You can only reference variables defined at the file or global levels when populating the variables block within a run block.", addr.Name)
detail := fmt.Sprintf("The input variable %q is not available to the current context. Within the variables block of a run block you can only reference variables defined at the file or global levels; within the variables block of a suite you can only reference variables defined at the global levels.", addr.Name)
if availableRunOutputs == nil {
detail = fmt.Sprintf("The input variable %q is not available to the current provider configuration. You can only reference variables defined at the file or global levels within provider configurations.", addr.Name)
}

@ -8,9 +8,8 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/terraform"
)
var _ hcl.Body = (*ProviderConfig)(nil)
@ -26,8 +25,7 @@ var _ hcl.Body = (*ProviderConfig)(nil)
type ProviderConfig struct {
Original hcl.Body
ConfigVariables map[string]*configs.Variable
AvailableVariables map[string]backend.UnparsedVariableValue
AvailableVariables terraform.InputValues
AvailableRunOutputs map[addrs.Run]cty.Value
}
@ -52,7 +50,7 @@ func (p *ProviderConfig) PartialContent(schema *hcl.BodySchema) (*hcl.BodyConten
Attributes: attrs,
Blocks: p.transformBlocks(content.Blocks),
MissingItemRange: content.MissingItemRange,
}, &ProviderConfig{rest, p.ConfigVariables, p.AvailableVariables, p.AvailableRunOutputs}, diags
}, &ProviderConfig{rest, p.AvailableVariables, p.AvailableRunOutputs}, diags
}
func (p *ProviderConfig) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
@ -88,17 +86,7 @@ func (p *ProviderConfig) transformAttributes(originals hcl.Attributes) (hcl.Attr
continue
}
if variable, exists := p.AvailableVariables[addr.Name]; exists {
// Then we have a value for this variable! So we think we'll
// be able to process it - let's parse it now.
parsingMode := configs.VariableParseHCL
if config, exists := p.ConfigVariables[addr.Name]; exists {
parsingMode = config.ParsingMode
}
value, valueDiags := variable.ParseVariableValue(parsingMode)
diags = append(diags, valueDiags.ToHCL()...)
if value, exists := p.AvailableVariables[addr.Name]; exists {
if value != nil {
availableVariables[addr.Name] = value.Value
}
@ -137,7 +125,7 @@ func (p *ProviderConfig) transformBlocks(originals hcl.Blocks) hcl.Blocks {
blocks[name] = &hcl.Block{
Type: block.Type,
Labels: block.Labels,
Body: &ProviderConfig{block.Body, p.ConfigVariables, p.AvailableVariables, p.AvailableRunOutputs},
Body: &ProviderConfig{block.Body, p.AvailableVariables, p.AvailableRunOutputs},
DefRange: block.DefRange,
TypeRange: block.TypeRange,
LabelRanges: block.LabelRanges,

@ -71,7 +71,7 @@ func TestProviderConfig(t *testing.T) {
"input": cty.StringVal("string"),
},
expectedErrors: []string{
"The input variable \"missing\" is not available to the current run block. You can only reference variables defined at the file or global levels when populating the variables block within a run block.",
"The input variable \"missing\" is not available to the current context. Within the variables block of a run block you can only reference variables defined at the file or global levels; within the variables block of a suite you can only reference variables defined at the global levels.",
},
validate: func(t *testing.T, content *hcl.BodyContent) {
if len(content.Attributes) > 0 {
@ -174,19 +174,12 @@ func TestProviderConfig(t *testing.T) {
config := ProviderConfig{
Original: file.Body,
ConfigVariables: func() map[string]*configs.Variable {
variables := make(map[string]*configs.Variable)
for variable := range tc.variables {
variables[variable] = &configs.Variable{
Name: variable,
}
}
return variables
}(),
AvailableVariables: func() map[string]backend.UnparsedVariableValue {
variables := make(map[string]backend.UnparsedVariableValue)
AvailableVariables: func() terraform.InputValues {
variables := make(terraform.InputValues)
for name, value := range tc.variables {
variables[name] = &variable{value}
variables[name] = &terraform.InputValue{
Value: value,
}
}
return variables
}(),

@ -183,7 +183,7 @@ func (c *Context) ApplyAndEval(plan *plans.Plan, config *configs.Config, opts *A
"Applied changes may be incomplete",
`The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values may not be fully updated. Run the following command to verify that no other changes are pending:
terraform plan
Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`,
))
}

@ -8,7 +8,7 @@ description: >-
-> **Note:** This testing framework is available in Terraform v1.6.0 and later.
Terraform tests let authors validate that module configuration updates do not introduce breaking changes. Tests run against test-specific, short-lived resources, preventing any risk to your existing infrastructure or state.
Terraform tests let authors validate that module configuration updates do not introduce breaking changes. Tests run against test-specific, short-lived resources, preventing any risk to your existing infrastructure or state.
## Integration or Unit testing
@ -174,7 +174,7 @@ For tests defined in a test directory, any variable values defined in automatic
### Variable References
Variables you define within `run` blocks can refer to outputs from modules executed in earlier `run` blocks and variables defined at higher precedence levels.
Variables you define within `run` blocks can refer to outputs from modules executed in earlier `run` blocks and variables defined at higher precedence levels. Variables defined within the file level `variables` block can only refer to global variables.
For example, the following code block shows how a variable can refer to higher precedence variables and previous run blocks:

Loading…
Cancel
Save