Skip to content

Commit 19540e3

Browse files
authored
terraform test: don't allow diags from executable nodes (#37193)
1 parent dd4bf55 commit 19540e3

File tree

7 files changed

+24
-46
lines changed

7 files changed

+24
-46
lines changed

internal/backend/local/test.go

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import (
77
"context"
88
"fmt"
99
"log"
10+
"maps"
1011
"path/filepath"
1112
"slices"
1213

1314
"github.com/zclconf/go-cty/cty"
1415

15-
"maps"
16-
1716
"github.com/hashicorp/terraform/internal/backend/backendrun"
1817
"github.com/hashicorp/terraform/internal/command/junit"
1918
"github.com/hashicorp/terraform/internal/command/views"
@@ -267,7 +266,7 @@ func (runner *TestFileRunner) Test(file *moduletest.File) {
267266
}
268267

269268
// walk and execute the graph
270-
diags = runner.walkGraph(g)
269+
diags = diags.Append(runner.walkGraph(g))
271270

272271
// If the graph walk was terminated, we don't want to add the diagnostics.
273272
// The error the user receives will just be:
@@ -287,15 +286,15 @@ func (runner *TestFileRunner) walkGraph(g *terraform.Graph) tfdiags.Diagnostics
287286
sem := runner.Suite.semaphore
288287

289288
// Walk the graph.
290-
walkFn := func(v dag.Vertex) (diags tfdiags.Diagnostics) {
289+
walkFn := func(v dag.Vertex) tfdiags.Diagnostics {
291290
if runner.EvalContext.Cancelled() {
292291
// If the graph walk has been cancelled, the node should just return immediately.
293292
// For now, this means a hard stop has been requested, in this case we don't
294293
// even stop to mark future test runs as having been skipped. They'll
295294
// just show up as pending in the printed summary. We will quickly
296295
// just mark the overall file status has having errored to indicate
297296
// it was interrupted.
298-
return
297+
return nil
299298
}
300299

301300
// the walkFn is called asynchronously, and needs to be recovered
@@ -312,28 +311,17 @@ func (runner *TestFileRunner) walkGraph(g *terraform.Graph) tfdiags.Diagnostics
312311
log.Printf("[ERROR] vertex %q panicked", dag.VertexName(v))
313312
panic(r) // re-panic
314313
}
315-
316-
if diags.HasErrors() {
317-
for _, diag := range diags {
318-
if diag.Severity() == tfdiags.Error {
319-
desc := diag.Description()
320-
log.Printf("[ERROR] vertex %q error: %s", dag.VertexName(v), desc.Summary)
321-
}
322-
}
323-
log.Printf("[TRACE] vertex %q: visit complete, with errors", dag.VertexName(v))
324-
} else {
325-
log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v))
326-
}
314+
log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v))
327315
}()
328316

329317
// Acquire a lock on the semaphore
330318
sem.Acquire()
331319
defer sem.Release()
332320

333321
if executable, ok := v.(graph.GraphNodeExecutable); ok {
334-
diags = executable.Execute(runner.EvalContext)
322+
executable.Execute(runner.EvalContext)
335323
}
336-
return
324+
return nil
337325
}
338326

339327
return g.AcyclicGraph.Walk(walkFn)

internal/moduletest/graph/node_state_cleanup.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ func (n *NodeStateCleanup) Name() string {
3434
// This function should never return non-fatal error diagnostics, as that would
3535
// prevent further cleanup from happening. Instead, the diagnostics
3636
// will be rendered directly.
37-
func (n *NodeStateCleanup) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
37+
func (n *NodeStateCleanup) Execute(evalCtx *EvalContext) {
3838
file := n.opts.File
3939
state := evalCtx.GetFileState(n.stateKey)
4040
log.Printf("[TRACE] TestStateManager: cleaning up state for %s", file.Name)
4141

4242
if evalCtx.Cancelled() {
4343
// Don't try and clean anything up if the execution has been cancelled.
4444
log.Printf("[DEBUG] TestStateManager: skipping state cleanup for %s due to cancellation", file.Name)
45-
return nil
45+
return
4646
}
4747

4848
empty := true
@@ -61,7 +61,7 @@ func (n *NodeStateCleanup) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
6161
// The state can be empty for a run block that just executed a plan
6262
// command, or a run block that only read data sources. We'll just
6363
// skip empty run blocks.
64-
return nil
64+
return
6565
}
6666

6767
if state.Run == nil {
@@ -78,7 +78,7 @@ func (n *NodeStateCleanup) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
7878
evalCtx.Renderer().DestroySummary(diags, nil, file, state.State)
7979

8080
// intentionally return nil to allow further cleanup
81-
return nil
81+
return
8282
}
8383
TransformConfigForRun(evalCtx, state.Run, file)
8484

@@ -100,7 +100,6 @@ func (n *NodeStateCleanup) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
100100
file.UpdateStatus(moduletest.Error)
101101
}
102102
evalCtx.Renderer().DestroySummary(destroyDiags, state.Run, file, updated)
103-
return nil
104103
}
105104

106105
func (n *NodeStateCleanup) destroy(ctx *EvalContext, runNode *NodeTestRun, waiter *operationWaiter) (*states.State, tfdiags.Diagnostics) {

internal/moduletest/graph/node_test_run.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,9 @@ func (n *NodeTestRun) References() []*addrs.Reference {
4444

4545
// Execute executes the test run block and update the status of the run block
4646
// based on the result of the execution.
47-
func (n *NodeTestRun) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
47+
func (n *NodeTestRun) Execute(evalCtx *EvalContext) {
4848
log.Printf("[TRACE] TestFileRunner: executing run block %s/%s", n.File().Name, n.run.Name)
4949
startTime := time.Now().UTC()
50-
var diags tfdiags.Diagnostics
5150
file, run := n.File(), n.run
5251

5352
// At the end of the function, we'll update the status of the file based on
@@ -62,21 +61,21 @@ func (n *NodeTestRun) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
6261
// execute tests. Instead, we mark all remaining run blocks as
6362
// skipped, print the status, and move on.
6463
run.Status = moduletest.Skip
65-
return diags
64+
return
6665
}
6766
if evalCtx.Cancelled() {
6867
// A cancellation signal has been received.
6968
// Don't do anything, just give up and return immediately.
7069
// The surrounding functions should stop this even being called, but in
7170
// case of race conditions or something we can still verify this.
72-
return diags
71+
return
7372
}
7473

7574
if evalCtx.Stopped() {
7675
// Then the test was requested to be stopped, so we just mark each
7776
// following test as skipped, print the status, and move on.
7877
run.Status = moduletest.Skip
79-
return diags
78+
return
8079
}
8180

8281
// Create a waiter which handles waiting for terraform operations to complete.
@@ -94,7 +93,7 @@ func (n *NodeTestRun) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
9493
})
9594

9695
if cancelled {
97-
diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Test interrupted", "The test operation could not be completed due to an interrupt signal. Please read the remaining diagnostics carefully for any sign of failed state cleanup or dangling resources."))
96+
n.run.Diagnostics = n.run.Diagnostics.Append(tfdiags.Sourceless(tfdiags.Error, "Test interrupted", "The test operation could not be completed due to an interrupt signal. Please read the remaining diagnostics carefully for any sign of failed state cleanup or dangling resources."))
9897
}
9998

10099
// If we got far enough to actually attempt to execute the run then
@@ -103,7 +102,6 @@ func (n *NodeTestRun) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
103102
Start: startTime,
104103
Duration: time.Since(startTime),
105104
}
106-
return diags
107105
}
108106

109107
func (n *NodeTestRun) execute(ctx *EvalContext, waiter *operationWaiter) {

internal/moduletest/graph/test_graph_builder.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ func validateRunConfigs(g *terraform.Graph) error {
7373
// dynamicNode is a helper node which can be added to the graph to execute
7474
// a dynamic function at some desired point in the graph.
7575
type dynamicNode struct {
76-
eval func(*EvalContext) tfdiags.Diagnostics
76+
eval func(*EvalContext)
7777
}
7878

79-
func (n *dynamicNode) Execute(evalCtx *EvalContext) tfdiags.Diagnostics {
80-
return n.eval(evalCtx)
79+
func (n *dynamicNode) Execute(evalCtx *EvalContext) {
80+
n.eval(evalCtx)
8181
}

internal/moduletest/graph/transform_config.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ import (
77
"fmt"
88

99
"github.com/hashicorp/hcl/v2"
10+
1011
"github.com/hashicorp/terraform/internal/configs"
1112
"github.com/hashicorp/terraform/internal/dag"
1213
"github.com/hashicorp/terraform/internal/moduletest"
1314
hcltest "github.com/hashicorp/terraform/internal/moduletest/hcl"
1415
"github.com/hashicorp/terraform/internal/states"
1516
"github.com/hashicorp/terraform/internal/terraform"
16-
"github.com/hashicorp/terraform/internal/tfdiags"
1717
)
1818

1919
type GraphNodeExecutable interface {
20-
Execute(ctx *EvalContext) tfdiags.Diagnostics
20+
Execute(ctx *EvalContext)
2121
}
2222

2323
// TestFileState is a helper struct that just maps a run block to the state that
@@ -65,10 +65,8 @@ func (t *TestConfigTransformer) Transform(g *terraform.Graph) error {
6565

6666
func (t *TestConfigTransformer) addRootConfigNode(g *terraform.Graph, statesMap map[string]*TestFileState) *dynamicNode {
6767
rootConfigNode := &dynamicNode{
68-
eval: func(ctx *EvalContext) tfdiags.Diagnostics {
69-
var diags tfdiags.Diagnostics
68+
eval: func(ctx *EvalContext) {
7069
ctx.FileStates = statesMap
71-
return diags
7270
},
7371
}
7472
g.Add(rootConfigNode)

internal/moduletest/graph/transform_providers.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"github.com/hashicorp/terraform/internal/configs"
88
"github.com/hashicorp/terraform/internal/dag"
99
"github.com/hashicorp/terraform/internal/terraform"
10-
"github.com/hashicorp/terraform/internal/tfdiags"
1110
)
1211

1312
// TestProvidersTransformer is a GraphTransformer that gathers all the providers
@@ -88,11 +87,10 @@ func (t *TestProvidersTransformer) transformSingleConfig(config *configs.Config)
8887

8988
func (t *TestProvidersTransformer) createRootNode(g *terraform.Graph, providerMap map[*NodeTestRun]map[string]bool) *dynamicNode {
9089
node := &dynamicNode{
91-
eval: func(ctx *EvalContext) tfdiags.Diagnostics {
90+
eval: func(ctx *EvalContext) {
9291
for node, providers := range providerMap {
9392
ctx.SetProviders(node.run, providers)
9493
}
95-
return nil
9694
},
9795
}
9896
g.Add(node)

internal/moduletest/graph/transform_state_cleanup.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/hashicorp/terraform/internal/dag"
1010
"github.com/hashicorp/terraform/internal/moduletest"
1111
"github.com/hashicorp/terraform/internal/terraform"
12-
"github.com/hashicorp/terraform/internal/tfdiags"
1312
)
1413

1514
// TestStateCleanupTransformer is a GraphTransformer that adds a cleanup node
@@ -75,10 +74,8 @@ func (t *TestStateCleanupTransformer) Transform(g *terraform.Graph) error {
7574

7675
func (t *TestStateCleanupTransformer) addRootCleanupNode(g *terraform.Graph) *dynamicNode {
7776
rootCleanupNode := &dynamicNode{
78-
eval: func(ctx *EvalContext) tfdiags.Diagnostics {
79-
var diags tfdiags.Diagnostics
77+
eval: func(ctx *EvalContext) {
8078
ctx.Renderer().File(t.opts.File, moduletest.TearDown)
81-
return diags
8279
},
8380
}
8481
g.Add(rootCleanupNode)

0 commit comments

Comments
 (0)