mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Signed-off-by: ollevche <ollevche@gmail.com>
This commit is contained in:
parent
82fdce1c7d
commit
8fd25f3346
@ -1,5 +1,7 @@
|
||||
## 1.8.8 (unreleased)
|
||||
|
||||
BUG FIXES:
|
||||
* `tofu test` now removes outputs of destroyed modules between different test runs. ([#2274](https://github.com/opentofu/opentofu/pull/2274))
|
||||
|
||||
|
||||
## 1.8.7
|
||||
|
@ -6,6 +6,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
@ -28,6 +29,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/moduletest"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/states/statefile"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/opentofu/opentofu/internal/tofu"
|
||||
)
|
||||
@ -455,6 +457,26 @@ func (runner *TestFileRunner) ExecuteTestFile(file *moduletest.File) {
|
||||
|
||||
state, updatedState := runner.ExecuteTestRun(run, file, runner.States[key].State, config)
|
||||
if updatedState {
|
||||
var err error
|
||||
|
||||
// We need to simulate state serialization between multiple runs
|
||||
// due to its side effects. One of such side effects is removal
|
||||
// of destroyed non-root module outputs. This is not handled
|
||||
// during graph walk since those values are not stored in the
|
||||
// state file. This is more of a weird workaround instead of a
|
||||
// proper fix, unfortunately.
|
||||
state, err = simulateStateSerialization(state)
|
||||
if err != nil {
|
||||
run.Diagnostics = run.Diagnostics.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Failure during state serialization",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
|
||||
// We cannot reuse state later so that's a hard stop.
|
||||
return
|
||||
}
|
||||
|
||||
// Only update the most recent run and state if the state was
|
||||
// actually updated by this change. We want to use the run that
|
||||
// most recently updated the tracked state as the cleanup
|
||||
@ -1261,3 +1283,24 @@ func (runner *TestFileRunner) prepareInputVariablesForAssertions(config *configs
|
||||
config.Module.Variables = currentVars
|
||||
}, diags
|
||||
}
|
||||
|
||||
// simulateStateSerialization takes a state, serializes it, deserializes it
|
||||
// and then returns. This is useful for state writing side effects without
|
||||
// actually writing a state file.
|
||||
func simulateStateSerialization(state *states.State) (*states.State, error) {
|
||||
buff := &bytes.Buffer{}
|
||||
|
||||
f := statefile.New(state, "", 0)
|
||||
|
||||
err := statefile.Write(f, buff, encryption.StateEncryptionDisabled())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("writing state to buffer: %w", err)
|
||||
}
|
||||
|
||||
f, err = statefile.Read(buff, encryption.StateEncryptionDisabled())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading state from buffer: %w", err)
|
||||
}
|
||||
|
||||
return f.State, nil
|
||||
}
|
||||
|
@ -820,6 +820,10 @@ func TestTest_Modules(t *testing.T) {
|
||||
expected: "main.tftest.hcl... pass\n run \"setup\"... pass\n run \"test\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
||||
code: 0,
|
||||
},
|
||||
"destroyed_mod_outputs": {
|
||||
expected: "main.tftest.hcl... pass\n run \"first_apply\"... pass\n run \"second_apply\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
||||
code: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tcs {
|
||||
|
9
internal/command/testdata/test/destroyed_mod_outputs/main.tf
vendored
Normal file
9
internal/command/testdata/test/destroyed_mod_outputs/main.tf
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
variable "numbers" {
|
||||
type = set(string)
|
||||
}
|
||||
|
||||
module "mod" {
|
||||
source = "./mod"
|
||||
for_each = var.numbers
|
||||
val = each.key
|
||||
}
|
21
internal/command/testdata/test/destroyed_mod_outputs/main.tftest.hcl
vendored
Normal file
21
internal/command/testdata/test/destroyed_mod_outputs/main.tftest.hcl
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
run "first_apply" {
|
||||
variables {
|
||||
numbers = [ "a", "b" ]
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(module.mod) == 2
|
||||
error_message = "Amount of module outputs is wrong"
|
||||
}
|
||||
}
|
||||
|
||||
run "second_apply" {
|
||||
variables {
|
||||
numbers = [ "c", "d" ]
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(module.mod) == 2
|
||||
error_message = "Amount of module outputs is wrong (persisted outputs?)"
|
||||
}
|
||||
}
|
11
internal/command/testdata/test/destroyed_mod_outputs/mod/main.tf
vendored
Normal file
11
internal/command/testdata/test/destroyed_mod_outputs/mod/main.tf
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
variable "val" {
|
||||
}
|
||||
|
||||
output "val" {
|
||||
value = "${var.val}_${test_resource.resource.id}"
|
||||
}
|
||||
|
||||
resource "test_resource" "resource" {
|
||||
id = "598318e0"
|
||||
value = var.val
|
||||
}
|
Loading…
Reference in New Issue
Block a user