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(&flagGet, "get", true, "")
|
||||||
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
|
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
|
||||||
cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure")
|
cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure")
|
||||||
|
cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state")
|
||||||
cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "")
|
cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "")
|
||||||
cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory")
|
cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory")
|
||||||
cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode")
|
cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode")
|
||||||
|
@ -53,6 +54,12 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
return 1
|
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
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
if len(flagPluginPath) > 0 {
|
if len(flagPluginPath) > 0 {
|
||||||
|
|
|
@ -412,7 +412,7 @@ func TestInit_backendConfigFile(t *testing.T) {
|
||||||
View: view,
|
View: view,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
args := []string{"-backend-config="}
|
args := []string{"-backend-config=", "-migrate-state"}
|
||||||
if code := c.Run(args); code != 0 {
|
if code := c.Run(args); code != 0 {
|
||||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
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 {
|
if code := c.Run(args); code != 0 {
|
||||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
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
|
// 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 {
|
if code := c.Run(args); code != 0 {
|
||||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
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 {
|
if code := c.Run(args); code == 0 {
|
||||||
t.Fatal("init should have failed", ui.OutputWriter)
|
t.Fatal("init should have failed", ui.OutputWriter)
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,6 +204,9 @@ type Meta struct {
|
||||||
//
|
//
|
||||||
// reconfigure forces init to ignore any stored configuration.
|
// 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
|
// compactWarnings (-compact-warnings) selects a more compact presentation
|
||||||
// of warnings in the output when they are not accompanied by errors.
|
// of warnings in the output when they are not accompanied by errors.
|
||||||
statePath string
|
statePath string
|
||||||
|
@ -214,6 +217,7 @@ type Meta struct {
|
||||||
stateLockTimeout time.Duration
|
stateLockTimeout time.Duration
|
||||||
forceInitCopy bool
|
forceInitCopy bool
|
||||||
reconfigure bool
|
reconfigure bool
|
||||||
|
migrateState bool
|
||||||
compactWarnings bool
|
compactWarnings bool
|
||||||
|
|
||||||
// Used with the import command to allow import of state when no matching config exists.
|
// Used with the import command to allow import of state when no matching config exists.
|
||||||
|
|
|
@ -6,7 +6,6 @@ package command
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"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)
|
// We're unsetting a backend (moving from backend => local)
|
||||||
case c == nil && !s.Backend.Empty():
|
case c == nil && !s.Backend.Empty():
|
||||||
log.Printf("[TRACE] Meta.Backend: previously-initialized %q backend is no longer present in config", s.Backend.Type)
|
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 {
|
if !opts.Init {
|
||||||
initReason := fmt.Sprintf(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
"Unsetting the previously set backend %q",
|
tfdiags.Error,
|
||||||
s.Backend.Type)
|
"Backend initialization required, please run \"terraform init\"",
|
||||||
m.backendInitRequired(initReason)
|
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
|
||||||
diags = diags.Append(errBackendInitRequired)
|
))
|
||||||
|
return nil, diags
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.migrateState {
|
||||||
|
diags = diags.Append(migrateOrReconfigDiag)
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,11 +535,12 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
|
||||||
case c != nil && s.Backend.Empty():
|
case c != nil && s.Backend.Empty():
|
||||||
log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
|
log.Printf("[TRACE] Meta.Backend: moving from default local state only to %q backend", c.Type)
|
||||||
if !opts.Init {
|
if !opts.Init {
|
||||||
initReason := fmt.Sprintf(
|
initReason := fmt.Sprintf("Initial configuration of the requested backend %q", c.Type)
|
||||||
"Initial configuration of the requested backend %q",
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
c.Type)
|
tfdiags.Error,
|
||||||
m.backendInitRequired(initReason)
|
"Backend initialization required, please run \"terraform init\"",
|
||||||
diags = diags.Append(errBackendInitRequired)
|
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
|
||||||
|
))
|
||||||
return nil, diags
|
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)
|
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 {
|
if !opts.Init {
|
||||||
initReason := fmt.Sprintf(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
"Backend configuration changed for %q",
|
tfdiags.Error,
|
||||||
c.Type)
|
"Backend initialization required, please run \"terraform init\"",
|
||||||
m.backendInitRequired(initReason)
|
fmt.Sprintf(strings.TrimSpace(errBackendInit), initReason),
|
||||||
diags = diags.Append(errBackendInitRequired)
|
))
|
||||||
|
return nil, diags
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.migrateState {
|
||||||
|
diags = diags.Append(migrateOrReconfigDiag)
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,11 +1155,6 @@ func (m *Meta) remoteBackendVersionCheck(b backend.Backend, workspace string) tf
|
||||||
// Output constants and initialization code
|
// 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 = `
|
const errBackendLocalRead = `
|
||||||
Error reading local state: %s
|
Error reading local state: %s
|
||||||
|
|
||||||
|
@ -1205,8 +1217,7 @@ and try again.
|
||||||
`
|
`
|
||||||
|
|
||||||
const errBackendInit = `
|
const errBackendInit = `
|
||||||
[reset][bold][yellow]Backend reinitialization required. Please run "terraform init".[reset]
|
Reason: %s
|
||||||
[yellow]Reason: %s
|
|
||||||
|
|
||||||
The "backend" is the interface that Terraform uses to store state,
|
The "backend" is the interface that Terraform uses to store state,
|
||||||
perform operations, etc. If this message is showing up, it means that the
|
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.
|
the Terraform backend.
|
||||||
|
|
||||||
Changes to backend configurations require reinitialization. This allows
|
Changes to backend configurations require reinitialization. This allows
|
||||||
Terraform to set up the new configuration, copy existing state, etc. This is
|
Terraform to set up the new configuration, copy existing state, etc. Please run
|
||||||
only done during "terraform init". Please run that command now then try again.
|
"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
|
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
|
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
|
Successfully configured the backend %q! Terraform will automatically
|
||||||
use this backend unless the backend configuration changes.
|
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
|
// Setup the meta
|
||||||
m := testMetaBackend(t, nil)
|
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
|
// Get the backend
|
||||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
|
@ -1884,5 +1888,8 @@ func testMetaBackend(t *testing.T, args []string) *Meta {
|
||||||
t.Fatalf("unexpected error: %s", err)
|
t.Fatalf("unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// metaBackend tests are verifying migrate actions
|
||||||
|
m.migrateState = true
|
||||||
|
|
||||||
return &m
|
return &m
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,7 +400,7 @@ func TestStateRm_needsInit(t *testing.T) {
|
||||||
t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String())
|
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())
|
t.Fatalf("expected initialization error, got:\n%s", ui.ErrorWriter.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue