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)
|
## 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
|
## 1.8.7
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -28,6 +29,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/moduletest"
|
"github.com/opentofu/opentofu/internal/moduletest"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/states"
|
"github.com/opentofu/opentofu/internal/states"
|
||||||
|
"github.com/opentofu/opentofu/internal/states/statefile"
|
||||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||||
"github.com/opentofu/opentofu/internal/tofu"
|
"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)
|
state, updatedState := runner.ExecuteTestRun(run, file, runner.States[key].State, config)
|
||||||
if updatedState {
|
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
|
// Only update the most recent run and state if the state was
|
||||||
// actually updated by this change. We want to use the run that
|
// actually updated by this change. We want to use the run that
|
||||||
// most recently updated the tracked state as the cleanup
|
// most recently updated the tracked state as the cleanup
|
||||||
@ -1261,3 +1283,24 @@ func (runner *TestFileRunner) prepareInputVariablesForAssertions(config *configs
|
|||||||
config.Module.Variables = currentVars
|
config.Module.Variables = currentVars
|
||||||
}, diags
|
}, 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",
|
expected: "main.tftest.hcl... pass\n run \"setup\"... pass\n run \"test\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
||||||
code: 0,
|
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 {
|
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