diff --git a/internal/command/command_test.go b/internal/command/command_test.go index d1a43cf659..929bfb4397 100644 --- a/internal/command/command_test.go +++ b/internal/command/command_test.go @@ -716,12 +716,15 @@ func testInputMap(t *testing.T, answers map[string]string) func() { // Return the cleanup return func() { - if len(testInputResponseMap) > 0 { - t.Fatalf("expected no unused answers provided to command.testInputMap, got: %v", testInputResponseMap) - } + var unusedAnswers = testInputResponseMap + // First, clean up! test = true testInputResponseMap = nil + + if len(unusedAnswers) > 0 { + t.Fatalf("expected no unused answers provided to command.testInputMap, got: %v", unusedAnswers) + } } } diff --git a/internal/command/meta_backend.go b/internal/command/meta_backend.go index 82ecd89c78..f467579ba9 100644 --- a/internal/command/meta_backend.go +++ b/internal/command/meta_backend.go @@ -196,13 +196,19 @@ func (m *Meta) selectWorkspace(b backend.Backend) error { var list strings.Builder for i, w := range workspaces { if w == workspace { + log.Printf("[TRACE] Meta.selectWorkspace: the currently selected workspace is present in the configured backend (%s)", workspace) return nil } fmt.Fprintf(&list, "%d. %s\n", i+1, w) } - // If the selected workspace doesn't exist, ask the user to select - // a workspace from the list of existing workspaces. + // If the backend only has a single workspace, select that as the current workspace + if len(workspaces) == 1 { + log.Printf("[TRACE] Meta.selectWorkspace: automatically selecting the single workspace provided by the backend (%s)", workspaces[0]) + return m.SetWorkspace(workspaces[0]) + } + + // Otherwise, ask the user to select a workspace from the list of existing workspaces. v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{ Id: "select-workspace", Query: fmt.Sprintf( @@ -220,7 +226,9 @@ func (m *Meta) selectWorkspace(b backend.Backend) error { return fmt.Errorf("Failed to select workspace: input not a valid number") } - return m.SetWorkspace(workspaces[idx-1]) + workspace = workspaces[idx-1] + log.Printf("[TRACE] Meta.selectWorkspace: setting the current workpace according to user selection (%s)", workspace) + return m.SetWorkspace(workspace) } // BackendForPlan is similar to Backend, but uses backend settings that were diff --git a/internal/command/meta_backend_test.go b/internal/command/meta_backend_test.go index 82dbb0355a..be8fec9708 100644 --- a/internal/command/meta_backend_test.go +++ b/internal/command/meta_backend_test.go @@ -789,6 +789,84 @@ func TestMetaBackend_reconfigureChange(t *testing.T) { } } +// Initializing a backend which supports workspaces and does *not* have +// the currently selected workspace should prompt the user with a list of +// workspaces to choose from to select a valid one, if more than one workspace +// is available. +func TestMetaBackend_initSelectedWorkspaceDoesNotExist(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-multi"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // Setup the meta + m := testMetaBackend(t, nil) + + defer testInputMap(t, map[string]string{ + "select-workspace": "2", + })() + + // Get the backend + _, diags := m.Backend(&BackendOpts{Init: true}) + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + expected := "foo" + actual, err := m.Workspace() + if err != nil { + t.Fatal(err) + } + + if actual != expected { + t.Fatalf("expected selected workspace to be %q, but was %q", expected, actual) + } +} + +// Initializing a backend which supports workspaces and does *not* have the +// currently selected workspace - and which only has a single workspace - should +// automatically select that single workspace. +func TestMetaBackend_initSelectedWorkspaceDoesNotExistAutoSelect(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("init-backend-selected-workspace-doesnt-exist-single"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // Setup the meta + m := testMetaBackend(t, nil) + + // this should not ask for input + m.input = false + + // Assert test precondition: The current selected workspace is "bar" + previousName, err := m.Workspace() + if err != nil { + t.Fatal(err) + } + + if previousName != "bar" { + t.Fatalf("expected test fixture to start with 'bar' as the current selected workspace") + } + + // Get the backend + _, diags := m.Backend(&BackendOpts{Init: true}) + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + expected := "default" + actual, err := m.Workspace() + if err != nil { + t.Fatal(err) + } + + if actual != expected { + t.Fatalf("expected selected workspace to be %q, but was %q", expected, actual) + } +} + // Changing a configured backend, copying state func TestMetaBackend_configuredChangeCopy(t *testing.T) { // Create a temporary working directory that is empty @@ -1267,7 +1345,6 @@ func TestMetaBackend_configuredChangeCopy_multiToNoDefaultWithoutDefault(t *test // Ask input defer testInputMap(t, map[string]string{ "backend-migrate-multistate-to-multistate": "yes", - "select-workspace": "1", })() // Setup the meta diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/.terraform/environment b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/.terraform/environment new file mode 100644 index 0000000000..5716ca5987 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/.terraform/environment @@ -0,0 +1 @@ +bar diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/.terraform/terraform.tfstate b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/.terraform/terraform.tfstate new file mode 100644 index 0000000000..19a90cc6b8 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/.terraform/terraform.tfstate @@ -0,0 +1,23 @@ +{ + "version": 3, + "serial": 2, + "lineage": "2f3864a6-1d3e-1999-0f84-36cdb61179d3", + "backend": { + "type": "local", + "config": { + "path": null, + "workspace_dir": null + }, + "hash": 666019178 + }, + "modules": [ + { + "path": [ + "root" + ], + "outputs": {}, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/main.tf b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/main.tf new file mode 100644 index 0000000000..da6f209e14 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/main.tf @@ -0,0 +1,7 @@ +terraform { + backend "local" {} +} + +output "foo" { + value = "bar" +} diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/terraform.tfstate b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/terraform.tfstate new file mode 100644 index 0000000000..47de0a47e7 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/terraform.tfstate @@ -0,0 +1,13 @@ +{ + "version": 4, + "terraform_version": "1.1.0", + "serial": 1, + "lineage": "cc4bb587-aa35-87ad-b3b7-7abdb574f2a1", + "outputs": { + "foo": { + "value": "bar", + "type": "string" + } + }, + "resources": [] +} diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/terraform.tfstate.d/foo/terraform.tfstate b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/terraform.tfstate.d/foo/terraform.tfstate new file mode 100644 index 0000000000..70021d04ad --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-multi/terraform.tfstate.d/foo/terraform.tfstate @@ -0,0 +1,13 @@ +{ + "version": 4, + "terraform_version": "1.1.0", + "serial": 1, + "lineage": "8ad3c77d-51aa-d90a-4f12-176f538b6e8b", + "outputs": { + "foo": { + "value": "bar", + "type": "string" + } + }, + "resources": [] +} diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/.terraform/environment b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/.terraform/environment new file mode 100644 index 0000000000..5716ca5987 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/.terraform/environment @@ -0,0 +1 @@ +bar diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/.terraform/terraform.tfstate b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/.terraform/terraform.tfstate new file mode 100644 index 0000000000..19a90cc6b8 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/.terraform/terraform.tfstate @@ -0,0 +1,23 @@ +{ + "version": 3, + "serial": 2, + "lineage": "2f3864a6-1d3e-1999-0f84-36cdb61179d3", + "backend": { + "type": "local", + "config": { + "path": null, + "workspace_dir": null + }, + "hash": 666019178 + }, + "modules": [ + { + "path": [ + "root" + ], + "outputs": {}, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/main.tf b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/main.tf new file mode 100644 index 0000000000..da6f209e14 --- /dev/null +++ b/internal/command/testdata/init-backend-selected-workspace-doesnt-exist-single/main.tf @@ -0,0 +1,7 @@ +terraform { + backend "local" {} +} + +output "foo" { + value = "bar" +}