diff --git a/command/init.go b/command/init.go index ecc4590919b6..4e57f4a6cdd3 100644 --- a/command/init.go +++ b/command/init.go @@ -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) diff --git a/command/meta_backend.go b/command/meta_backend.go index 067f5cc8cd17..7cfabc0e921d 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -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 diff --git a/command/meta_backend_migrate.go b/command/meta_backend_migrate.go index be37896d9152..663cbf346425 100644 --- a/command/meta_backend_migrate.go +++ b/command/meta_backend_migrate.go @@ -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