Skip to content

Commit

Permalink
command/meta_backend: Prompt to select workspace before saving backen…
Browse files Browse the repository at this point in the history
…d config (#29945)

When terraform detects that a user has no workspaces that map to their current configuration, it will prompt the user to create a new workspace and enter a value name. If the user ignores the prompt and exits it, the legacy backend (terraform.tfstate) will be left in a awkward state:

1. This saved backend config will show a diff for the JSON attributes "serial", "tags" and "hash"
2. "Terraform workspace list" will show an empty list
3. "Terraform apply" will run successfully using the previous workspace, from the previous config, not the one from the current saved backend config
4. The cloud config is not reflective of the current working directory

Solution: If the user exits the prompt, the saved backend config should not be updated because they did not select a new workspace. They are back at the beginning where they are force to re run the init cmd again before proceeding with new changes.
  • Loading branch information
uturunku1 committed Dec 1, 2021
1 parent d8a1279 commit d72a413
Showing 1 changed file with 41 additions and 17 deletions.
58 changes: 41 additions & 17 deletions internal/command/meta_backend.go
Expand Up @@ -97,14 +97,6 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics
b, backendDiags = m.backendFromConfig(opts)
diags = diags.Append(backendDiags)

if opts.Init && b != nil && !diags.HasErrors() {
// Its possible that the currently selected workspace doesn't exist, so
// we call selectWorkspace to ensure an existing workspace is selected.
if err := m.selectWorkspace(b); err != nil {
diags = diags.Append(err)
}
}

if diags.HasErrors() {
return nil, diags
}
Expand Down Expand Up @@ -232,8 +224,11 @@ func (m *Meta) selectWorkspace(b backend.Backend) error {
Query: "\n[reset][bold][yellow]No workspaces found.[reset]",
Description: fmt.Sprintf(inputCloudInitCreateWorkspace, strings.Join(c.WorkspaceMapping.Tags, ", ")),
})
if err != nil {
return fmt.Errorf("Couldn't create initial workspace: %w", err)
}
name = strings.TrimSpace(name)
if err != nil || name == "" {
if name == "" {
return fmt.Errorf("Couldn't create initial workspace: no name provided")
}
log.Printf("[TRACE] Meta.selectWorkspace: selecting the new TFC workspace requested by the user (%s)", name)
Expand Down Expand Up @@ -610,7 +605,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di

return m.backend_c_r_S(c, cHash, sMgr, true)

// Configuring a backend for the first time.
// Configuring a backend for the first time or -reconfigure flag was used
case c != nil && s.Backend.Empty():
log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
if !opts.Init {
Expand All @@ -631,9 +626,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
}
return nil, diags
}

return m.backend_C_r_s(c, cHash, sMgr)

return m.backend_C_r_s(c, cHash, sMgr, opts)
// Potentially changing a backend configuration
case c != nil && !s.Backend.Empty():
// We are not going to migrate if...
Expand All @@ -643,7 +636,15 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
// AND we're not providing any overrides. An override can mean a change overriding an unchanged backend block (indicated by the hash value).
if (uint64(cHash) == s.Backend.Hash) && (!opts.Init || opts.ConfigOverride == nil) {
log.Printf("[TRACE] Meta.Backend: using already-initialized, unchanged %q backend configuration", c.Type)
return m.savedBackend(sMgr)
savedBackend, diags := m.savedBackend(sMgr)
// Verify that selected workspace exist. Otherwise prompt user to create one
if opts.Init && savedBackend != nil {
if err := m.selectWorkspace(savedBackend); err != nil {
diags = diags.Append(err)
return nil, diags
}
}
return savedBackend, diags
}

// If our configuration (the result of both the literal configuration and given
Expand All @@ -665,6 +666,13 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
if moreDiags.HasErrors() {
return nil, diags
}
// Verify that selected workspace exist. Otherwise prompt user to create one
if opts.Init && savedBackend != nil {
if err := m.selectWorkspace(savedBackend); err != nil {
diags = diags.Append(err)
return nil, diags
}
}

return savedBackend, diags
}
Expand All @@ -685,7 +693,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
}

log.Printf("[WARN] backend config has changed since last init")
return m.backend_C_r_S_changed(c, cHash, sMgr, true)
return m.backend_C_r_S_changed(c, cHash, sMgr, true, opts)

default:
diags = diags.Append(fmt.Errorf(
Expand Down Expand Up @@ -897,7 +905,7 @@ func (m *Meta) backend_c_r_S(c *configs.Backend, cHash int, sMgr *clistate.Local
}

// Configuring a backend for the first time.
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState) (backend.Backend, tfdiags.Diagnostics) {
func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.LocalState, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

// Grab a purely local backend to get the local state if it exists
Expand Down Expand Up @@ -1017,6 +1025,14 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
Hash: uint64(cHash),
}

// Verify that selected workspace exist. Otherwise prompt user to create one
if opts.Init && b != nil {
if err := m.selectWorkspace(b); err != nil {
diags = diags.Append(err)
return nil, diags
}
}

if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
return nil, diags
Expand All @@ -1037,7 +1053,7 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
}

// Changing a previously saved backend.
func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool) (backend.Backend, tfdiags.Diagnostics) {
func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clistate.LocalState, output bool, opts *BackendOpts) (backend.Backend, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

// Get the old state
Expand Down Expand Up @@ -1133,6 +1149,14 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
Hash: uint64(cHash),
}

// Verify that selected workspace exist. Otherwise prompt user to create one
if opts.Init && b != nil {
if err := m.selectWorkspace(b); err != nil {
diags = diags.Append(err)
return b, diags
}
}

if err := sMgr.WriteState(s); err != nil {
diags = diags.Append(fmt.Errorf(errBackendWriteSaved, err))
return nil, diags
Expand Down

0 comments on commit d72a413

Please sign in to comment.