testing framework: add trace and debug statements during test executions (#33554)

This commit is contained in:
Liam Cervante 2023-07-26 10:11:27 +02:00 committed by GitHub
parent 222676390c
commit 083c9afd5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,6 +3,7 @@ package command
import (
"context"
"fmt"
"log"
"path"
"sort"
"strings"
@ -93,6 +94,9 @@ func (c *TestCommand) Run(rawArgs []string) int {
return 1
}
runCount := 0
fileCount := 0
var fileDiags tfdiags.Diagnostics
suite := moduletest.Suite{
Files: func() map[string]*moduletest.File {
@ -112,6 +116,8 @@ func (c *TestCommand) Run(rawArgs []string) int {
continue
}
fileCount++
var runs []*moduletest.Run
for ix, run := range file.Runs {
runs = append(runs, &moduletest.Run{
@ -120,6 +126,8 @@ func (c *TestCommand) Run(rawArgs []string) int {
Name: run.Name,
})
}
runCount += len(runs)
files[name] = &moduletest.File{
Config: file,
Name: name,
@ -132,6 +140,8 @@ func (c *TestCommand) Run(rawArgs []string) int {
// Otherwise, we'll just do all the tests in the directory!
for name, file := range config.Module.Tests {
fileCount++
var runs []*moduletest.Run
for ix, run := range file.Runs {
runs = append(runs, &moduletest.Run{
@ -140,6 +150,8 @@ func (c *TestCommand) Run(rawArgs []string) int {
Name: run.Name,
})
}
runCount += len(runs)
files[name] = &moduletest.File{
Config: file,
Name: name,
@ -150,6 +162,8 @@ func (c *TestCommand) Run(rawArgs []string) int {
}(),
}
log.Printf("[DEBUG] TestCommand: found %d files with %d run blocks", fileCount, runCount)
diags = diags.Append(fileDiags)
if fileDiags.HasErrors() {
view.Diagnostics(nil, nil, diags)
@ -174,6 +188,11 @@ func (c *TestCommand) Run(rawArgs []string) int {
return 1
}
// Print out all the diagnostics we have from the setup. These will just be
// warnings, and we want them out of the way before we start the actual
// testing.
view.Diagnostics(nil, nil, diags)
// We have two levels of interrupt here. A 'stop' and a 'cancel'. A 'stop'
// is a soft request to stop. We'll finish the current test, do the tidy up,
// but then skip all remaining tests and run blocks. A 'cancel' is a hard
@ -310,6 +329,8 @@ func (runner *TestRunner) Start(globals map[string]backend.UnparsedVariableValue
}
func (runner *TestRunner) ExecuteTestFile(file *moduletest.File, globals map[string]backend.UnparsedVariableValue) {
log.Printf("[TRACE] TestRunner: executing test file %s", file.Name)
mgr := new(TestStateManager)
mgr.runner = runner
mgr.State = states.NewState()
@ -360,6 +381,8 @@ func (runner *TestRunner) ExecuteTestFile(file *moduletest.File, globals map[str
}
func (runner *TestRunner) ExecuteTestRun(mgr *TestStateManager, run *moduletest.Run, file *moduletest.File, state *states.State, config *configs.Config, globals map[string]backend.UnparsedVariableValue) *states.State {
log.Printf("[TRACE] TestRunner: executing run block %s/%s", file.Name, run.Name)
if runner.Cancelled {
// Don't do anything, just give up and return immediately.
// The surrounding functions should stop this even being called, but in
@ -405,12 +428,15 @@ func (runner *TestRunner) ExecuteTestRun(mgr *TestStateManager, run *moduletest.
run.Diagnostics = run.Diagnostics.Append(diags)
if runner.Cancelled {
log.Printf("[DEBUG] TestRunner: exiting after test execution for %s/%s due to cancellation", file.Name, run.Name)
// Print out the diagnostics from the run now, since it was cancelled
// the normal set of diagnostics will not be printed otherwise.
runner.View.Diagnostics(run, file, run.Diagnostics)
run.Status = moduletest.Error
return state
}
log.Printf("[DEBUG] TestRunner: completed test execution for %s/%s", file.Name, run.Name)
if diags.HasErrors() {
run.Status = moduletest.Error
@ -470,8 +496,15 @@ func (runner *TestRunner) ExecuteTestRun(mgr *TestStateManager, run *moduletest.
// The command argument decides whether it executes only a plan or also applies
// the plan it creates during the planning.
func (runner *TestRunner) execute(mgr *TestStateManager, run *moduletest.Run, file *moduletest.File, config *configs.Config, state *states.State, opts *terraform.PlanOpts, command configs.TestCommand, globals map[string]backend.UnparsedVariableValue) (*terraform.Context, *plans.Plan, *states.State, tfdiags.Diagnostics) {
identifier := file.Name
if run != nil {
identifier = fmt.Sprintf("%s/%s", identifier, run.Name)
}
log.Printf("[TRACE] TestRunner: executing %s for %s", opts.Mode, identifier)
if opts.Mode == plans.DestroyMode && state.Empty() {
// Nothing to do!
log.Printf("[DEBUG] TestRunner: nothing to destroy for %s", identifier)
return nil, nil, state, nil
}
@ -521,7 +554,10 @@ func (runner *TestRunner) execute(mgr *TestStateManager, run *moduletest.Run, fi
go func() {
defer logging.PanicHandler()
defer done()
log.Printf("[DEBUG] TestRunner: starting plan for %s", identifier)
plan, planDiags = tfCtx.Plan(config, state, opts)
log.Printf("[DEBUG] TestRunner: completed plan for %s", identifier)
}()
waitDiags, cancelled := runner.wait(tfCtx, runningCtx, mgr, run, file, nil)
planDiags = planDiags.Append(waitDiags)
@ -535,6 +571,7 @@ func (runner *TestRunner) execute(mgr *TestStateManager, run *moduletest.Run, fi
}
if cancelled {
log.Printf("[DEBUG] TestRunner: skipping apply stage for %s due to cancellation", identifier)
// If the execution was cancelled during the plan, we'll exit here to
// stop the plan being applied and using more time.
return tfCtx, plan, state, diags
@ -587,7 +624,9 @@ func (runner *TestRunner) execute(mgr *TestStateManager, run *moduletest.Run, fi
go func() {
defer logging.PanicHandler()
defer done()
log.Printf("[DEBUG] TestRunner: starting apply for %s", identifier)
updated, applyDiags = tfCtx.Apply(plan, config)
log.Printf("[DEBUG] TestRunner: completed apply for %s", identifier)
}()
waitDiags, _ = runner.wait(tfCtx, runningCtx, mgr, run, file, created)
applyDiags = applyDiags.Append(waitDiags)
@ -597,12 +636,18 @@ func (runner *TestRunner) execute(mgr *TestStateManager, run *moduletest.Run, fi
}
func (runner *TestRunner) wait(ctx *terraform.Context, runningCtx context.Context, mgr *TestStateManager, run *moduletest.Run, file *moduletest.File, created []*plans.ResourceInstanceChangeSrc) (diags tfdiags.Diagnostics, cancelled bool) {
identifier := file.Name
if run != nil {
identifier = fmt.Sprintf("%s/%s", identifier, run.Name)
}
log.Printf("[TRACE] TestRunner: waiting for execution during %s", identifier)
// This function handles what happens when the user presses the second
// interrupt. This is a "hard cancel", we are going to stop doing whatever
// it is we're doing. This means even if we're halfway through creating or
// destroying infrastructure we just give up.
handleCancelled := func() {
log.Printf("[DEBUG] TestRunner: test execution cancelled during %s", identifier)
states := make(map[*moduletest.Run]*states.State)
states[nil] = mgr.State
@ -624,6 +669,8 @@ func (runner *TestRunner) wait(ctx *terraform.Context, runningCtx context.Contex
// anything but just wait for things to finish safely. But, we do listen
// for the crucial second interrupt which will prompt a hard stop / cancel.
handleStopped := func() {
log.Printf("[DEBUG] TestRunner: test execution stopped during %s", identifier)
select {
case <-runner.CancelledCtx.Done():
// We've been asked again. This time we stop whatever we're doing
@ -680,8 +727,11 @@ type TestModuleState struct {
}
func (manager *TestStateManager) cleanupStates(file *moduletest.File, globals map[string]backend.UnparsedVariableValue) {
log.Printf("[TRACE] TestStateManager: cleaning up state for %s", file.Name)
if manager.runner.Cancelled {
// Don't try and clean anything up if the execution has been cancelled.
log.Printf("[DEBUG] TestStateManager: skipping state cleanup for %s due to cancellation", file.Name)
return
}
@ -701,9 +751,12 @@ func (manager *TestStateManager) cleanupStates(file *moduletest.File, globals ma
for ix := len(manager.States); ix > 0; ix-- {
module := manager.States[ix-1]
log.Printf("[DEBUG] TestStateManager: cleaning up state for %s/%s", file.Name, module.Run.Name)
if manager.runner.Cancelled {
// In case the cancellation came while a previous state was being
// destroyed.
log.Printf("[DEBUG] TestStateManager: skipping state cleanup for %s/%s due to cancellation", file.Name, module.Run.Name)
return
}