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.
pull/21230/head
Sander van Harmelen 7 years ago
parent 20e17ec86f
commit f264cd3ec5

@ -210,7 +210,7 @@ func (c *InitCommand) Run(args []string) int {
if back == nil {
// If we didn't initialize a backend then we'll try to at least
// instantiate one. This might fail if it wasn't already initalized
// instantiate one. This might fail if it wasn't already initialized
// by a previous run, so we must still expect that "back" may be nil
// in code that follows.
back, err = c.Backend(nil)

@ -10,6 +10,7 @@ import (
"io/ioutil"
"log"
"path/filepath"
"strconv"
"strings"
"github.com/hashicorp/go-multierror"
@ -86,6 +87,11 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) {
b, err = m.backendFromPlan(opts)
} else {
b, err = m.backendFromConfig(opts)
if opts.Init && b != nil && err == nil {
// Its possible that the currently selected workspace doesn't exist, so
// we call selectWorkspace to ensure an existing workspace is selected.
err = m.selectWorkspace(b)
}
}
if err != nil {
return nil, err
@ -145,6 +151,56 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) {
return local, nil
}
// selectWorkspace gets a list of existing workspaces and then checks
// if the currently selected workspace is valid. If not, it will ask
// the user to select a workspace from the list.
func (m *Meta) selectWorkspace(b backend.Backend) error {
workspaces, err := b.States()
if err == backend.ErrNamedStatesNotSupported {
return nil
}
if err != nil {
return fmt.Errorf("Failed to get existing workspaces: %s", err)
}
if len(workspaces) == 0 {
return fmt.Errorf(errBackendNoExistingWorkspaces)
}
// Get the currently selected workspace.
workspace := m.Workspace()
// Check if any of the existing workspaces matches the selected
// workspace and create a numbered list of existing workspaces.
var list strings.Builder
for i, w := range workspaces {
if w == 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.
v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
Id: "select-workspace",
Query: fmt.Sprintf(
"[reset][bold][yellow]The currently selected workspace (%s) does not exist.[reset]",
workspace),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
})
if err != nil {
return fmt.Errorf("Error asking to select workspace: %s", err)
}
idx, err := strconv.Atoi(v)
if err != nil || (idx < 1 || idx > len(workspaces)) {
return fmt.Errorf("Error selecting workspace: input not a valid number")
}
return m.SetWorkspace(workspaces[idx-1])
}
// IsLocalBackend returns true if the backend is a local backend. We use this
// for some checks that require a remote backend.
func (m *Meta) IsLocalBackend(b backend.Backend) bool {
@ -997,7 +1053,6 @@ func (m *Meta) backend_C_r_s(
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
"[reset][green]\n"+strings.TrimSpace(successBackendSet), s.Backend.Type)))
// Return the backend
return b, nil
}
@ -1408,6 +1463,14 @@ If you'd like to run Terraform and store state locally, you can fix this
error by removing the backend configuration from your configuration.
`
const errBackendNoExistingWorkspaces = `
No existing workspaces. Use the "terraform workspace" command to create
and select a new workspace.
If the backend already contains existing workspaces, you may need to update
the workspace name or prefix in the backend configuration.
`
const errBackendRemoteRead = `
Error reading backend state: %s

@ -8,7 +8,6 @@ import (
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/hashicorp/terraform/backend"
@ -169,56 +168,7 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
}
}
// Its possible that the currently selected workspace is not migrated,
// so we call selectWorkspace to ensure a valid workspace is selected.
return m.selectWorkspace(opts.Two)
}
// selectWorkspace gets a list of migrated workspaces and then checks
// if the currently selected workspace is valid. If not, it will ask
// the user to select a workspace from the list.
func (m *Meta) selectWorkspace(b backend.Backend) error {
workspaces, err := b.States()
if err != nil {
return fmt.Errorf("Failed to get migrated workspaces: %s", err)
}
if len(workspaces) == 0 {
return fmt.Errorf(errBackendNoMigratedWorkspaces)
}
// Get the currently selected workspace.
workspace := m.Workspace()
// Check if any of the migrated workspaces match the selected workspace
// and create a numbered list with migrated workspaces.
var list strings.Builder
for i, w := range workspaces {
if w == workspace {
return nil
}
fmt.Fprintf(&list, "%d. %s\n", i+1, w)
}
// If the selected workspace is not migrated, ask the user to select
// a workspace from the list of migrated workspaces.
v, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
Id: "select-workspace",
Query: fmt.Sprintf(
"[reset][bold][yellow]The currently selected workspace (%s) is not migrated.[reset]",
workspace),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendSelectWorkspace), list.String()),
})
if err != nil {
return fmt.Errorf("Error asking to select workspace: %s", err)
}
idx, err := strconv.Atoi(v)
if err != nil || (idx < 1 || idx > len(workspaces)) {
return fmt.Errorf("Error selecting workspace: input not a valid number")
}
return m.SetWorkspace(workspaces[idx-1])
return nil
}
// Multi-state to single state.
@ -530,14 +480,6 @@ The state in the previous backend remains intact and unmodified. Please resolve
the error above and try again.
`
const errBackendNoMigratedWorkspaces = `
No workspaces are migrated. Use the "terraform workspace" command to create
and select a new workspace.
If the backend already contains existing workspaces, you may need to update
the workspace name or prefix in the backend configuration.
`
const inputBackendMigrateEmpty = `
Pre-existing state was found while migrating the previous %q backend to the
newly configured %q backend. No existing state was found in the newly

Loading…
Cancel
Save