Skip to content

Commit f264cd3

Browse files
author
Sander van Harmelen
committed
Always try to select a workspace after initialization
There are a number of use cases that can require a user to select a workspace after initializing Terraform. To make sure we cover all these use cases, we will always call the `selectWorkspace` method to verify a valid workspace is already selected or (if needed) offer to select one before moving on.
1 parent 20e17ec commit f264cd3

3 files changed

Lines changed: 66 additions & 61 deletions

File tree

command/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func (c *InitCommand) Run(args []string) int {
210210

211211
if back == nil {
212212
// If we didn't initialize a backend then we'll try to at least
213-
// instantiate one. This might fail if it wasn't already initalized
213+
// instantiate one. This might fail if it wasn't already initialized
214214
// by a previous run, so we must still expect that "back" may be nil
215215
// in code that follows.
216216
back, err = c.Backend(nil)

command/meta_backend.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"io/ioutil"
1111
"log"
1212
"path/filepath"
13+
"strconv"
1314
"strings"
1415

1516
"github.com/hashicorp/go-multierror"
@@ -86,6 +87,11 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) {
8687
b, err = m.backendFromPlan(opts)
8788
} else {
8889
b, err = m.backendFromConfig(opts)
90+
if opts.Init && b != nil && err == nil {
91+
// Its possible that the currently selected workspace doesn't exist, so
92+
// we call selectWorkspace to ensure an existing workspace is selected.
93+
err = m.selectWorkspace(b)
94+
}
8995
}
9096
if err != nil {
9197
return nil, err
@@ -145,6 +151,56 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) {
145151
return local, nil
146152
}
147153

154+
// selectWorkspace gets a list of existing workspaces and then checks
155+
// if the currently selected workspace is valid. If not, it will ask
156+
// the user to select a workspace from the list.
157+
func (m *Meta) selectWorkspace(b backend.Backend) error {
158+
workspaces, err := b.States()
159+
if err == backend.ErrNamedStatesNotSupported {
160+
return nil
161+
}
162+
if err != nil {
163+
return fmt.Errorf("Failed to get existing workspaces: %s", err)
164+
}
165+
if len(workspaces) == 0 {
166+
return fmt.Errorf(errBackendNoExistingWorkspaces)
167+
}
168+
169+
// Get the currently selected workspace.
170+
workspace := m.Workspace()
171+
172+
// Check if any of the existing workspaces matches the selected
173+
// workspace and create a numbered list of existing workspaces.
174+
var list strings.Builder
175+
for i, w := range workspaces {
176+
if w == workspace {
177+
return nil
178+
}
179+
fmt.Fprintf(&list, "%d. %s\n", i+1, w)
180+
}
181+
182+
// If the selected workspace doesn't exist, ask the user to select
183+
// a workspace from the list of existing workspaces.
184+
v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
185+
Id: "select-workspace",
186+
Query: fmt.Sprintf(
187+
"[reset][bold][yellow]The currently selected workspace (%s) does not exist.[reset]",
188+
workspace),
189+
Description: fmt.Sprintf(
190+
strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
191+
})
192+
if err != nil {
193+
return fmt.Errorf("Error asking to select workspace: %s", err)
194+
}
195+
196+
idx, err := strconv.Atoi(v)
197+
if err != nil || (idx < 1 || idx > len(workspaces)) {
198+
return fmt.Errorf("Error selecting workspace: input not a valid number")
199+
}
200+
201+
return m.SetWorkspace(workspaces[idx-1])
202+
}
203+
148204
// IsLocalBackend returns true if the backend is a local backend. We use this
149205
// for some checks that require a remote backend.
150206
func (m *Meta) IsLocalBackend(b backend.Backend) bool {
@@ -997,7 +1053,6 @@ func (m *Meta) backend_C_r_s(
9971053
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
9981054
"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
9991055

1000-
// Return the backend
10011056
return b, nil
10021057
}
10031058

@@ -1408,6 +1463,14 @@ If you'd like to run Terraform and store state locally, you can fix this
14081463
error by removing the backend configuration from your configuration.
14091464
`
14101465

1466+
const errBackendNoExistingWorkspaces = `
1467+
No existing workspaces. Use the "terraform workspace" command to create
1468+
and select a new workspace.
1469+
1470+
If the backend already contains existing workspaces, you may need to update
1471+
the workspace name or prefix in the backend configuration.
1472+
`
1473+
14111474
const errBackendRemoteRead = `
14121475
Error reading backend state: %s
14131476

command/meta_backend_migrate.go

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"os"
99
"path/filepath"
1010
"sort"
11-
"strconv"
1211
"strings"
1312

1413
"github.com/hashicorp/terraform/backend"
@@ -169,56 +168,7 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
169168
}
170169
}
171170

172-
// Its possible that the currently selected workspace is not migrated,
173-
// so we call selectWorkspace to ensure a valid workspace is selected.
174-
return m.selectWorkspace(opts.Two)
175-
}
176-
177-
// selectWorkspace gets a list of migrated workspaces and then checks
178-
// if the currently selected workspace is valid. If not, it will ask
179-
// the user to select a workspace from the list.
180-
func (m *Meta) selectWorkspace(b backend.Backend) error {
181-
workspaces, err := b.States()
182-
if err != nil {
183-
return fmt.Errorf("Failed to get migrated workspaces: %s", err)
184-
}
185-
if len(workspaces) == 0 {
186-
return fmt.Errorf(errBackendNoMigratedWorkspaces)
187-
}
188-
189-
// Get the currently selected workspace.
190-
workspace := m.Workspace()
191-
192-
// Check if any of the migrated workspaces match the selected workspace
193-
// and create a numbered list with migrated workspaces.
194-
var list strings.Builder
195-
for i, w := range workspaces {
196-
if w == workspace {
197-
return nil
198-
}
199-
fmt.Fprintf(&list, "%d. %s\n", i+1, w)
200-
}
201-
202-
// If the selected workspace is not migrated, ask the user to select
203-
// a workspace from the list of migrated workspaces.
204-
v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
205-
Id: "select-workspace",
206-
Query: fmt.Sprintf(
207-
"[reset][bold][yellow]The currently selected workspace (%s) is not migrated.[reset]",
208-
workspace),
209-
Description: fmt.Sprintf(
210-
strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
211-
})
212-
if err != nil {
213-
return fmt.Errorf("Error asking to select workspace: %s", err)
214-
}
215-
216-
idx, err := strconv.Atoi(v)
217-
if err != nil || (idx < 1 || idx > len(workspaces)) {
218-
return fmt.Errorf("Error selecting workspace: input not a valid number")
219-
}
220-
221-
return m.SetWorkspace(workspaces[idx-1])
171+
return nil
222172
}
223173

224174
// Multi-state to single state.
@@ -530,14 +480,6 @@ The state in the previous backend remains intact and unmodified. Please resolve
530480
the error above and try again.
531481
`
532482

533-
const errBackendNoMigratedWorkspaces = `
534-
No workspaces are migrated. Use the "terraform workspace" command to create
535-
and select a new workspace.
536-
537-
If the backend already contains existing workspaces, you may need to update
538-
the workspace name or prefix in the backend configuration.
539-
`
540-
541483
const inputBackendMigrateEmpty = `
542484
Pre-existing state was found while migrating the previous %q backend to the
543485
newly configured %q backend. No existing state was found in the newly

0 commit comments

Comments
 (0)