diff --git a/CHANGELOG.md b/CHANGELOG.md index 34813656b7..e7fefe2e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ NEW FEATURES: * Add support for a `removed` block that allows users to remove resources or modules from the state without destroying them. ([#1158](https://github.com/opentofu/opentofu/pull/1158)) ENHANCEMENTS: +* Added support to use `.tfvars` files from tests folder. ([#1386](https://github.com/opentofu/opentofu/pull/1386)) * Added `templatestring` function that takes a string and renders it as a template using a supplied set of template variables. ([#1223](https://github.com/opentofu/opentofu/pull/1223)) * Added `base64gunzip` function that takes a base64 encoded gzip string and returns the decompressed data as a string. ([#800](https://github.com/opentofu/opentofu/issues/800)) * Added `cidrcontains` function that determines if an address belongs to a certain prefix. ([#366](https://github.com/opentofu/opentofu/issues/366)) diff --git a/internal/command/meta_vars.go b/internal/command/meta_vars.go index 0749ac4bc8..84197c6ec0 100644 --- a/internal/command/meta_vars.go +++ b/internal/command/meta_vars.go @@ -8,6 +8,7 @@ package command import ( "fmt" "os" + "path/filepath" "strings" "github.com/hashicorp/hcl/v2" @@ -62,30 +63,12 @@ func (m *Meta) collectVariableValues() (map[string]backend.UnparsedVariableValue } } - // Next up we have some implicit files that are loaded automatically - // if they are present. There's the original terraform.tfvars - // (DefaultVarsFilename) along with the later-added search for all files - // ending in .auto.tfvars. - if _, err := os.Stat(DefaultVarsFilename); err == nil { - moreDiags := m.addVarsFromFile(DefaultVarsFilename, tofu.ValueFromAutoFile, ret) - diags = diags.Append(moreDiags) - } - const defaultVarsFilenameJSON = DefaultVarsFilename + ".json" - if _, err := os.Stat(defaultVarsFilenameJSON); err == nil { - moreDiags := m.addVarsFromFile(defaultVarsFilenameJSON, tofu.ValueFromAutoFile, ret) - diags = diags.Append(moreDiags) - } - if infos, err := os.ReadDir("."); err == nil { - // "infos" is already sorted by name, so we just need to filter it here. - for _, info := range infos { - name := info.Name() - if !isAutoVarFile(name) { - continue - } - moreDiags := m.addVarsFromFile(name, tofu.ValueFromAutoFile, ret) - diags = diags.Append(moreDiags) - } - } + // Next up we load implicit files from the specified directory (first root then tests dir + // as tests dir files have higher precendence). These files are automatically loaded if present. + // There's the original terraform.tfvars (DefaultVarsFilename) along with the later-added + // search for all files ending in .auto.tfvars. + diags = diags.Append(m.addVarsFromDir(".", ret)) + diags = diags.Append(m.addVarsFromDir("tests", ret)) // Finally we process values given explicitly on the command line, either // as individual literal settings or as additional files to read. @@ -135,6 +118,33 @@ func (m *Meta) collectVariableValues() (map[string]backend.UnparsedVariableValue return ret, diags } +func (m *Meta) addVarsFromDir(currDir string, ret map[string]backend.UnparsedVariableValue) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if _, err := os.Stat(filepath.Join(currDir, DefaultVarsFilename)); err == nil { + moreDiags := m.addVarsFromFile(filepath.Join(currDir, DefaultVarsFilename), tofu.ValueFromAutoFile, ret) + diags = diags.Append(moreDiags) + } + const defaultVarsFilenameJSON = DefaultVarsFilename + ".json" + if _, err := os.Stat(filepath.Join(currDir, defaultVarsFilenameJSON)); err == nil { + moreDiags := m.addVarsFromFile(filepath.Join(currDir, defaultVarsFilenameJSON), tofu.ValueFromAutoFile, ret) + diags = diags.Append(moreDiags) + } + if infos, err := os.ReadDir(currDir); err == nil { + // "infos" is already sorted by name, so we just need to filter it here. + for _, info := range infos { + name := info.Name() + if !isAutoVarFile(name) { + continue + } + moreDiags := m.addVarsFromFile(filepath.Join(currDir, name), tofu.ValueFromAutoFile, ret) + diags = diags.Append(moreDiags) + } + } + + return diags +} + func (m *Meta) addVarsFromFile(filename string, sourceType tofu.ValueSourceType, to map[string]backend.UnparsedVariableValue) tfdiags.Diagnostics { var diags tfdiags.Diagnostics diff --git a/internal/command/test_test.go b/internal/command/test_test.go index 4a329e48c7..291ad108d6 100644 --- a/internal/command/test_test.go +++ b/internal/command/test_test.go @@ -161,6 +161,14 @@ func TestTest(t *testing.T) { expected: "1 passed, 0 failed.", code: 0, }, + "pass_with_tests_dir_variables": { + expected: "1 passed, 0 failed.", + code: 0, + }, + "override_with_tests_dir_variables": { + expected: "1 passed, 0 failed.", + code: 0, + }, } for name, tc := range tcs { t.Run(name, func(t *testing.T) { diff --git a/internal/command/testdata/test/override_with_tests_dir_variables/main.tf b/internal/command/testdata/test/override_with_tests_dir_variables/main.tf new file mode 100644 index 0000000000..ff87bac8e8 --- /dev/null +++ b/internal/command/testdata/test/override_with_tests_dir_variables/main.tf @@ -0,0 +1,7 @@ +variable "testVar" { + type = string +} + +resource "test_resource" "testRes" { + value = var.testVar +} diff --git a/internal/command/testdata/test/override_with_tests_dir_variables/terraform.tfvars b/internal/command/testdata/test/override_with_tests_dir_variables/terraform.tfvars new file mode 100644 index 0000000000..1f3399c64c --- /dev/null +++ b/internal/command/testdata/test/override_with_tests_dir_variables/terraform.tfvars @@ -0,0 +1 @@ +testVar = "ValueFROM./tfvars" diff --git a/internal/command/testdata/test/override_with_tests_dir_variables/tests/main.tftest.hcl b/internal/command/testdata/test/override_with_tests_dir_variables/tests/main.tftest.hcl new file mode 100644 index 0000000000..832360947c --- /dev/null +++ b/internal/command/testdata/test/override_with_tests_dir_variables/tests/main.tftest.hcl @@ -0,0 +1,6 @@ +run "validate_test_resource" { + assert { + condition = test_resource.testRes.value == "ValueFROMtests/tfvars" + error_message = "invalid value" + } +} diff --git a/internal/command/testdata/test/override_with_tests_dir_variables/tests/terraform.tfvars b/internal/command/testdata/test/override_with_tests_dir_variables/tests/terraform.tfvars new file mode 100644 index 0000000000..917e0d9813 --- /dev/null +++ b/internal/command/testdata/test/override_with_tests_dir_variables/tests/terraform.tfvars @@ -0,0 +1 @@ +testVar = "ValueFROMtests/tfvars" diff --git a/internal/command/testdata/test/pass_with_tests_dir_variables/main.tf b/internal/command/testdata/test/pass_with_tests_dir_variables/main.tf new file mode 100644 index 0000000000..ff87bac8e8 --- /dev/null +++ b/internal/command/testdata/test/pass_with_tests_dir_variables/main.tf @@ -0,0 +1,7 @@ +variable "testVar" { + type = string +} + +resource "test_resource" "testRes" { + value = var.testVar +} diff --git a/internal/command/testdata/test/pass_with_tests_dir_variables/tests/main.tftest.hcl b/internal/command/testdata/test/pass_with_tests_dir_variables/tests/main.tftest.hcl new file mode 100644 index 0000000000..832360947c --- /dev/null +++ b/internal/command/testdata/test/pass_with_tests_dir_variables/tests/main.tftest.hcl @@ -0,0 +1,6 @@ +run "validate_test_resource" { + assert { + condition = test_resource.testRes.value == "ValueFROMtests/tfvars" + error_message = "invalid value" + } +} diff --git a/internal/command/testdata/test/pass_with_tests_dir_variables/tests/terraform.tfvars b/internal/command/testdata/test/pass_with_tests_dir_variables/tests/terraform.tfvars new file mode 100644 index 0000000000..917e0d9813 --- /dev/null +++ b/internal/command/testdata/test/pass_with_tests_dir_variables/tests/terraform.tfvars @@ -0,0 +1 @@ +testVar = "ValueFROMtests/tfvars" diff --git a/website/docs/cli/commands/test/index.mdx b/website/docs/cli/commands/test/index.mdx index 62da5a8848..18a7989aac 100644 --- a/website/docs/cli/commands/test/index.mdx +++ b/website/docs/cli/commands/test/index.mdx @@ -244,10 +244,11 @@ can provide variables to your test run using any of the following methods: | Order | Source | |:------:|:-------------------------------------------------------------------------------------------------------------------------------| | 1 | Environment variables with the `TF_VAR_` prefix. | -| 2 | tfvar files specified: `terraform.tfvars` and `*.auto.tfvars`. | -| 3 | Commandline variables defined using the flag `-var`, and the variables defined in the files specified by the flag `-var-file`. | -| 4 | The variables from the `variables` block in a test file. | -| 5 | The variables from the `variables` block in `run` block. | +| 2 | tfvar files specified in the current directory: `terraform.tfvars` and `*.auto.tfvars`. | +| 3 | tfvar files specified in the tests directory: `tests/terraform.tfvars` and `tests/*.auto.tfvars`. | +| 4 | Commandline variables defined using the flag `-var`, and the variables defined in the files specified by the flag `-var-file`. | +| 5 | The variables from the `variables` block in a test file. | +| 6 | The variables from the `variables` block in `run` block. | OpenTofu evaluates the variables in the order listed above, so you can use it to override the previously set variable. For example: