no longer automatically attempt state migration
Add `init -migrate-state` flag to indicate automatic state migration is desired. This flag will be implied by the `-force-copy` flag, since that would indicate state migration is expected. If `init` encounters a change to the stored backend configuration, it will now always return an error when neither `-reconfigure` or `-migrate-state` is supplied. Turn the most common legacy output strings into diagnostics, removing the "see above text" error output.
This commit is contained in:
parent
73d07e28c0
commit
edc2695d18
|
@ -45,6 +45,7 @@ func (c *InitCommand) Run(args []string) int {
|
|||
cmdFlags.BoolVar(&flagGet, "get", true, "")
|
||||
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
|
||||
cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure")
|
||||
cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state")
|
||||
cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "")
|
||||
cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory")
|
||||
cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode")
|
||||
|
@ -53,6 +54,12 @@ func (c *InitCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
// Copying the state only happens during backend migration, so setting
|
||||
// -force-copy implies -migrate-state
|
||||
if c.forceInitCopy {
|
||||
c.migrateState = true
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
if len(flagPluginPath) > 0 {
|
||||
|
|
|
@ -412,7 +412,7 @@ func TestInit_backendConfigFile(t *testing.T) {
|
|||
View: view,
|
||||
},
|
||||
}
|
||||
args := []string{"-backend-config="}
|
||||
args := []string{"-backend-config=", "-migrate-state"}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||
}
|
||||
|
@ -555,7 +555,7 @@ func TestInit_backendConfigFileChange(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
args := []string{"-backend-config", "input.config"}
|
||||
args := []string{"-backend-config", "input.config", "-migrate-state"}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||
}
|
||||
|
@ -644,7 +644,7 @@ func TestInit_backendConfigKVReInit(t *testing.T) {
|
|||
}
|
||||
|
||||
// override the -backend-config options by settings
|
||||
args = []string{"-input=false", "-backend-config", ""}
|
||||
args = []string{"-input=false", "-backend-config", "", "-migrate-state"}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||
}
|
||||
|
@ -906,7 +906,7 @@ func TestInit_inputFalse(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
args = []string{"-input=false", "-backend-config=path=bar"}
|
||||
args = []string{"-input=false", "-backend-config=path=bar", "-migrate-state"}
|
||||
if code := c.Run(args); code == 0 {
|
||||
t.Fatal("init should have failed", ui.OutputWriter)
|
||||
}
|
||||
|
|
|
@ -204,6 +204,9 @@ type Meta struct {
|
|||
//
|
||||
// reconfigure forces init to ignore any stored configuration.
|
||||
//
|
||||
// migrateState confirms the user wishes to migrate from the prior backend
|
||||
// configuration to a new configuration.
|
||||
//
|
||||
// compactWarnings (-compact-warnings) selects a more compact presentation
|
||||
// of warnings in the output when they are not accompanied by errors.
|
||||
statePath string
|
||||
|
@ -214,6 +217,7 @@ type Meta struct {
|
|||
stateLockTimeout time.Duration
|
||||
forceInitCopy bool
|
||||
reconfigure bool
|
||||
migrateState bool
|
||||
compactWarnings bool
|
||||
|
||||
// Used with the import command to allow import of state when no matching config exists.
|
||||
|
|
|
@ -6,7 +6,6 @@ package command
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
@ -514,12 +513,19 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
|
|||
// We're unsetting a backend (moving from backend => local)
|
||||
case c == nil && !s.Backend.Empty():
|
||||
log.Printf("[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config", s.Backend.Type)
|
||||
|
||||
initReason := fmt.Sprintf("Unsetting the previously set backend %q", s.Backend.Type)
|
||||
if !opts.Init {
|
||||
initReason := fmt.Sprintf(
|
||||
"Unsetting the previously set backend %q",
|
||||
s.Backend.Type)
|
||||
m.backendInitRequired(initReason)
|
||||
diags = diags.Append(errBackendInitRequired)
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Backend initialization required, please run \"terraform init\"",
|
||||
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if !m.migrateState {
|
||||
diags = diags.Append(migrateOrReconfigDiag)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
|
@ -529,11 +535,12 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
|
|||
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 {
|
||||
initReason := fmt.Sprintf(
|
||||
"Initial configuration of the requested backend %q",
|
||||
c.Type)
|
||||
m.backendInitRequired(initReason)
|
||||
diags = diags.Append(errBackendInitRequired)
|
||||
initReason := fmt.Sprintf("Initial configuration of the requested backend %q", c.Type)
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Backend initialization required, please run \"terraform init\"",
|
||||
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
|
@ -558,12 +565,22 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
|
|||
}
|
||||
log.Printf("[TRACE] Meta.Backend: backend configuration has changed (from type %q to type %q)", s.Backend.Type, c.Type)
|
||||
|
||||
initReason := fmt.Sprintf("Backend configuration changed for %q", c.Type)
|
||||
if s.Backend.Type != c.Type {
|
||||
initReason = fmt.Sprintf("Backend configuration changed from %q to %q", s.Backend.Type, c.Type)
|
||||
}
|
||||
|
||||
if !opts.Init {
|
||||
initReason := fmt.Sprintf(
|
||||
"Backend configuration changed for %q",
|
||||
c.Type)
|
||||
m.backendInitRequired(initReason)
|
||||
diags = diags.Append(errBackendInitRequired)
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Backend initialization required, please run \"terraform init\"",
|
||||
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if !m.migrateState {
|
||||
diags = diags.Append(migrateOrReconfigDiag)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
|
@ -1138,11 +1155,6 @@ func (m *Meta) remoteBackendVersionCheck(b backend.Backend, workspace string) tf
|
|||
// Output constants and initialization code
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
// errBackendInitRequired is the final error message shown when reinit
|
||||
// is required for some reason. The error message includes the reason.
|
||||
var errBackendInitRequired = errors.New(
|
||||
"Initialization required. Please see the error message above.")
|
||||
|
||||
const errBackendLocalRead = `
|
||||
Error reading local state: %s
|
||||
|
||||
|
@ -1205,8 +1217,7 @@ and try again.
|
|||
`
|
||||
|
||||
const errBackendInit = `
|
||||
[reset][bold][yellow]Backend reinitialization required. Please run "terraform init".[reset]
|
||||
[yellow]Reason: %s
|
||||
Reason: %s
|
||||
|
||||
The "backend" is the interface that Terraform uses to store state,
|
||||
perform operations, etc. If this message is showing up, it means that the
|
||||
|
@ -1214,8 +1225,9 @@ Terraform configuration you're using is using a custom configuration for
|
|||
the Terraform backend.
|
||||
|
||||
Changes to backend configurations require reinitialization. This allows
|
||||
Terraform to set up the new configuration, copy existing state, etc. This is
|
||||
only done during "terraform init". Please run that command now then try again.
|
||||
Terraform to set up the new configuration, copy existing state, etc. Please run
|
||||
"terraform init" with either the "-reconfigure" or "-migrate-state" flags to
|
||||
use the current configuration.
|
||||
|
||||
If the change reason above is incorrect, please verify your configuration
|
||||
hasn't changed and try again. At this point, no changes to your existing
|
||||
|
@ -1254,3 +1266,10 @@ const successBackendSet = `
|
|||
Successfully configured the backend %q! Terraform will automatically
|
||||
use this backend unless the backend configuration changes.
|
||||
`
|
||||
|
||||
var migrateOrReconfigDiag = tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Backend configuration changed",
|
||||
"A change in the backend configuration has been detected, which may require migrating existing state.\n\n"+
|
||||
"If you wish to attempt automatic migration of the state, use \"terraform init -migrate-state\".\n"+
|
||||
`If you wish to store the current configuration with no changes to the state, use "terraform init -reconfigure".`)
|
||||
|
|
|
@ -314,6 +314,10 @@ func TestMetaBackend_configureNewWithState(t *testing.T) {
|
|||
// Setup the meta
|
||||
m := testMetaBackend(t, nil)
|
||||
|
||||
// This combination should not require the extra -migrate-state flag, since
|
||||
// there is no existing backend config
|
||||
m.migrateState = false
|
||||
|
||||
// Get the backend
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
|
@ -1884,5 +1888,8 @@ func testMetaBackend(t *testing.T, args []string) *Meta {
|
|||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
// metaBackend tests are verifying migrate actions
|
||||
m.migrateState = true
|
||||
|
||||
return &m
|
||||
}
|
||||
|
|
|
@ -400,7 +400,7 @@ func TestStateRm_needsInit(t *testing.T) {
|
|||
t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String())
|
||||
}
|
||||
|
||||
if !strings.Contains(ui.ErrorWriter.String(), "Initialization") {
|
||||
if !strings.Contains(ui.ErrorWriter.String(), "Backend initialization") {
|
||||
t.Fatalf("expected initialization error, got:\n%s", ui.ErrorWriter.String())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue