mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 18:10:30 -04:00
145 lines
4.2 KiB
Go
145 lines
4.2 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package command
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
backendInit "github.com/hashicorp/terraform/internal/backend/init"
|
|
"github.com/hashicorp/terraform/internal/backend/local"
|
|
"github.com/hashicorp/terraform/internal/logging"
|
|
"github.com/hashicorp/terraform/internal/moduletest"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
)
|
|
|
|
// TestCleanupCommand is a command that cleans up left-over resources created
|
|
// during Terraform test runs. It basically runs the test command in cleanup mode.
|
|
type TestCleanupCommand struct {
|
|
Meta
|
|
}
|
|
|
|
func (c *TestCleanupCommand) Help() string {
|
|
helpText := `
|
|
Usage: terraform [global options] test cleanup [options]
|
|
|
|
Cleans up left-over resources in states that were created during Terraform test runs.
|
|
|
|
By default, this command ignores the skip_cleanup attributes in the manifest
|
|
file. Use the -repair flag to override this behavior, which will ensure that
|
|
resources that were intentionally left-over are exempt from cleanup.
|
|
|
|
Options:
|
|
|
|
-repair Overrides the skip_cleanup attribute in the manifest
|
|
file and attempts to clean up all resources.
|
|
|
|
-no-color If specified, output won't contain any color.
|
|
|
|
-verbose Print detailed output during the cleanup process.
|
|
`
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (c *TestCleanupCommand) Synopsis() string {
|
|
return "Clean up left-over resources created during Terraform test runs"
|
|
}
|
|
|
|
func (c *TestCleanupCommand) Run(rawArgs []string) int {
|
|
setup, diags := c.setupTestExecution(moduletest.CleanupMode, "test cleanup", rawArgs)
|
|
if diags.HasErrors() {
|
|
return 1
|
|
}
|
|
|
|
args := setup.Args
|
|
view := setup.View
|
|
config := setup.Config
|
|
variables := setup.Variables
|
|
testVariables := setup.TestVariables
|
|
opts := setup.Opts
|
|
|
|
// 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
|
|
// request to stop now. We'll cancel the current operation immediately
|
|
// even if it's a delete operation, and we won't clean up any infrastructure
|
|
// if we're halfway through a test. We'll print details explaining what was
|
|
// stopped so the user can do their best to recover from it.
|
|
|
|
runningCtx, done := context.WithCancel(context.Background())
|
|
stopCtx, stop := context.WithCancel(runningCtx)
|
|
cancelCtx, cancel := context.WithCancel(context.Background())
|
|
|
|
runner := &local.TestSuiteRunner{
|
|
BackendFactory: backendInit.Backend,
|
|
Config: config,
|
|
// The GlobalVariables are loaded from the
|
|
// main configuration directory
|
|
// The GlobalTestVariables are loaded from the
|
|
// test directory
|
|
GlobalVariables: variables,
|
|
GlobalTestVariables: testVariables,
|
|
TestingDirectory: args.TestDirectory,
|
|
Opts: opts,
|
|
View: view,
|
|
Stopped: false,
|
|
Cancelled: false,
|
|
StoppedCtx: stopCtx,
|
|
CancelledCtx: cancelCtx,
|
|
Filter: args.Filter,
|
|
Verbose: args.Verbose,
|
|
Repair: args.Repair,
|
|
CommandMode: moduletest.CleanupMode,
|
|
}
|
|
|
|
var testDiags tfdiags.Diagnostics
|
|
|
|
go func() {
|
|
defer logging.PanicHandler()
|
|
defer done()
|
|
defer stop()
|
|
defer cancel()
|
|
|
|
_, testDiags = runner.Test(c.Meta.AllowExperimentalFeatures)
|
|
}()
|
|
|
|
// Wait for the operation to complete, or for an interrupt to occur.
|
|
select {
|
|
case <-c.ShutdownCh:
|
|
// Nice request to be cancelled.
|
|
|
|
view.Interrupted()
|
|
runner.Stop()
|
|
stop()
|
|
|
|
select {
|
|
case <-c.ShutdownCh:
|
|
// The user pressed it again, now we have to get it to stop as
|
|
// fast as possible.
|
|
|
|
view.FatalInterrupt()
|
|
runner.Cancel()
|
|
cancel()
|
|
|
|
waitTime := 5 * time.Second
|
|
|
|
// We'll wait 5 seconds for this operation to finish now, regardless
|
|
// of whether it finishes successfully or not.
|
|
select {
|
|
case <-runningCtx.Done():
|
|
case <-time.After(waitTime):
|
|
}
|
|
|
|
case <-runningCtx.Done():
|
|
// The application finished nicely after the request was stopped.
|
|
}
|
|
case <-runningCtx.Done():
|
|
// tests finished normally with no interrupts.
|
|
}
|
|
|
|
view.Diagnostics(nil, nil, testDiags)
|
|
|
|
return 0
|
|
}
|