command/meta_backend: Prompt to select workspace before saving backend 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.
This commit is contained in:
Luces Huayhuaca 2021-12-01 08:53:47 -08:00 committed by GitHub
parent d8a127994c
commit d72a413ef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 41 additions and 17 deletions

View File

@ -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
}
@ -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)
@ -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 {
@ -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...
@ -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
@ -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
}
@ -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(
@ -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
@ -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
@ -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
@ -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