diff --git a/tfexec/internal/e2etest/workspace_test.go b/tfexec/internal/e2etest/workspace_test.go index d5ff3b6c..43702f39 100644 --- a/tfexec/internal/e2etest/workspace_test.go +++ b/tfexec/internal/e2etest/workspace_test.go @@ -104,6 +104,35 @@ func TestWorkspace_multiple(t *testing.T) { }) } +func TestWorkspace_deletion(t *testing.T) { + runTest(t, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + assertWorkspaceList(t, tf, "default") + + const testWorkspace = "testws" + + t.Run("create and delete workspace", func(t *testing.T) { + err := tf.WorkspaceNew(context.Background(), testWorkspace) + if err != nil { + t.Fatalf("got error creating workspace: %s", err) + } + + assertWorkspaceList(t, tf, testWorkspace, testWorkspace) + + err = tf.WorkspaceSelect(context.Background(), defaultWorkspace) + if err != nil { + t.Fatalf("got error selecting workspace: %s", err) + } + + err = tf.WorkspaceDelete(context.Background(), testWorkspace) + if err != nil { + t.Fatalf("got error deleting workspace: %s", err) + } + + assertWorkspaceList(t, tf, defaultWorkspace) + }) + }) +} + func assertWorkspaceList(t *testing.T, tf *tfexec.Terraform, expectedCurrent string, expectedWorkspaces ...string) { actualWorkspaces, actualCurrent, err := tf.WorkspaceList(context.Background()) if err != nil { diff --git a/tfexec/workspace_delete.go b/tfexec/workspace_delete.go new file mode 100644 index 00000000..52677207 --- /dev/null +++ b/tfexec/workspace_delete.go @@ -0,0 +1,81 @@ +package tfexec + +import ( + "context" + "fmt" + "os/exec" + "strconv" +) + +type workspaceDeleteConfig struct { + lock bool + lockTimeout string + force bool +} + +var defaultWorkspaceDeleteOptions = workspaceDeleteConfig{ + lock: true, + lockTimeout: "0s", +} + +// WorkspaceDeleteCmdOption represents options that are applicable to the WorkspaceDelete method. +type WorkspaceDeleteCmdOption interface { + configureWorkspaceDelete(*workspaceDeleteConfig) +} + +func (opt *LockOption) configureWorkspaceDelete(conf *workspaceDeleteConfig) { + conf.lock = opt.lock +} + +func (opt *LockTimeoutOption) configureWorkspaceDelete(conf *workspaceDeleteConfig) { + conf.lockTimeout = opt.timeout +} + +func (opt *ForceOption) configureWorkspaceDelete(conf *workspaceDeleteConfig) { + conf.force = opt.force +} + +// WorkspaceDelete represents the workspace delete subcommand to the Terraform CLI. +func (tf *Terraform) WorkspaceDelete(ctx context.Context, workspace string, opts ...WorkspaceDeleteCmdOption) error { + cmd, err := tf.workspaceDeleteCmd(ctx, workspace, opts...) + if err != nil { + return err + } + return tf.runTerraformCmd(ctx, cmd) +} + +func (tf *Terraform) workspaceDeleteCmd(ctx context.Context, workspace string, opts ...WorkspaceDeleteCmdOption) (*exec.Cmd, error) { + c := defaultWorkspaceDeleteOptions + + for _, o := range opts { + switch o.(type) { + case *LockOption, *LockTimeoutOption: + err := tf.compatible(ctx, tf0_12_0, nil) + if err != nil { + return nil, fmt.Errorf("-lock and -lock-timeout were added to workspace delete in Terraform 0.12: %w", err) + } + } + + o.configureWorkspaceDelete(&c) + } + + args := []string{"workspace", "delete", "-no-color"} + + if c.force { + args = append(args, "-force") + } + if c.lockTimeout != "" && c.lockTimeout != defaultWorkspaceDeleteOptions.lockTimeout { + // only pass if not default, so we don't need to worry about the 0.11 version check + args = append(args, "-lock-timeout="+c.lockTimeout) + } + if !c.lock { + // only pass if false, so we don't need to worry about the 0.11 version check + args = append(args, "-lock="+strconv.FormatBool(c.lock)) + } + + args = append(args, workspace) + + cmd := tf.buildTerraformCmd(ctx, nil, args...) + + return cmd, nil +} diff --git a/tfexec/workspace_delete_test.go b/tfexec/workspace_delete_test.go new file mode 100644 index 00000000..9dc90e24 --- /dev/null +++ b/tfexec/workspace_delete_test.go @@ -0,0 +1,52 @@ +package tfexec + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestWorkspaceDeleteCmd(t *testing.T) { + td := testTempDir(t) + + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest013)) + if err != nil { + t.Fatal(err) + } + + // empty env, to avoid environ mismatch in testing + tf.SetEnv(map[string]string{}) + + t.Run("defaults", func(t *testing.T) { + workspaceDeleteCmd, err := tf.workspaceDeleteCmd(context.Background(), "workspace-name") + if err != nil { + t.Fatal(err) + } + + assertCmd(t, []string{ + "workspace", "delete", + "-no-color", + "workspace-name", + }, nil, workspaceDeleteCmd) + }) + + t.Run("override all defaults", func(t *testing.T) { + workspaceDeleteCmd, err := tf.workspaceDeleteCmd(context.Background(), "workspace-name", + LockTimeout("200s"), + Force(true), + Lock(false)) + if err != nil { + t.Fatal(err) + } + + assertCmd(t, []string{ + "workspace", "delete", + "-no-color", + "-force", + "-lock-timeout=200s", + "-lock=false", + "workspace-name", + }, nil, workspaceDeleteCmd) + }) +}