diff --git a/command/init.go b/command/init.go index 6b7fbaf21d25..54a244cdd74c 100644 --- a/command/init.go +++ b/command/init.go @@ -30,6 +30,7 @@ func (c *InitCommand) Run(args []string) int { cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") + cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { @@ -223,6 +224,10 @@ Options: times. The backend type must be in the configuration itself. + -force-copy Suppress prompts about copying state data. This is + equivalent to providing a "yes" to all confirmation + prompts. + -get=true Download any modules for this configuration. -input=true Ask for input if necessary. If false, will error if @@ -234,9 +239,7 @@ Options: -no-color If specified, output won't contain any color. - -force-copy Suppress prompts about copying state data. This is - equivalent to providing a "yes" to all confirmation - prompts. + -reconfigure Reconfigure the backend, ignoring any saved configuration. ` return strings.TrimSpace(helpText) } diff --git a/command/meta.go b/command/meta.go index c494d9697707..0b9375f7200c 100644 --- a/command/meta.go +++ b/command/meta.go @@ -95,6 +95,8 @@ type Meta struct { // // forceInitCopy suppresses confirmation for copying state data during // init. + // + // reconfigure forces init to ignore any stored configuration. statePath string stateOutPath string backupPath string @@ -104,6 +106,7 @@ type Meta struct { stateLock bool stateLockTimeout time.Duration forceInitCopy bool + reconfigure bool } // initStatePaths is used to initialize the default values for diff --git a/command/meta_backend.go b/command/meta_backend.go index 415efa02c2ba..6f75acc772dd 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -352,6 +352,13 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) { s = terraform.NewState() } + // if we want to force reconfiguration of the backend, we set the backend + // state to nil on this copy. This will direct us through the correct + // configuration path in the switch statement below. + if m.reconfigure { + s.Backend = nil + } + // Upon return, we want to set the state we're using in-memory so that // we can access it for commands. m.backendState = nil diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index ffb6e3b61fc4..aa4a02d2c4f6 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -983,6 +983,59 @@ func TestMetaBackend_configuredChange(t *testing.T) { } } +// Reconfiguring with an already configured backend. +// This should ignore the existing backend config, and configure the new +// backend is if this is the first time. +func TestMetaBackend_reconfigureChange(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("backend-change-single-to-single"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // Register the single-state backend + backendinit.Set("local-single", backendlocal.TestNewLocalSingle) + defer backendinit.Set("local-single", nil) + + // Setup the meta + m := testMetaBackend(t, nil) + + // this should not ask for input + m.input = false + + // cli flag -reconfigure + m.reconfigure = true + + // Get the backend + b, err := m.Backend(&BackendOpts{Init: true}) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s, err := b.State(backend.DefaultStateName) + if err != nil { + t.Fatalf("bad: %s", err) + } + if err := s.RefreshState(); err != nil { + t.Fatalf("bad: %s", err) + } + newState := s.State() + if newState != nil || !newState.Empty() { + t.Fatal("state should be nil/empty after forced reconfiguration") + } + + // verify that the old state is still there + s = (&state.LocalState{Path: "local-state.tfstate"}) + if err := s.RefreshState(); err != nil { + t.Fatal(err) + } + oldState := s.State() + if oldState == nil || oldState.Empty() { + t.Fatal("original state should be untouched") + } +} + // Changing a configured backend, copying state func TestMetaBackend_configuredChangeCopy(t *testing.T) { // Create a temporary working directory that is empty diff --git a/website/source/docs/commands/init.html.markdown b/website/source/docs/commands/init.html.markdown index 1222b46c738d..57aeaed89706 100644 --- a/website/source/docs/commands/init.html.markdown +++ b/website/source/docs/commands/init.html.markdown @@ -49,6 +49,9 @@ The command-line flags are all optional. The list of available flags are: for the backend. This can be specified multiple times. Flags specified later in the line override those specified earlier if they conflict. +* `-force-copy` - Suppress prompts about copying state data. This is equivalent + to providing a "yes" to all confirmation prompts. + * `-get=true` - Download any modules for this configuration. * `-input=true` - Ask for input interactively if necessary. If this is false @@ -60,8 +63,7 @@ The command-line flags are all optional. The list of available flags are: * `-no-color` - If specified, output won't contain any color. -* `-force-copy` - Suppress prompts about copying state data. This is equivalent - to providing a "yes" to all confirmation prompts. +* `-reconfigure` - Reconfigure the backend, ignoring any saved configuration. ## Backend Config