add `-force-copy` option to init command

The `-force-copy` option will suppress confirmation for copying state
data.

Modify some tests to use the option, making sure to leave coverage of
the Input code path.
This commit is contained in:
James Bardin 2017-03-21 15:05:51 -04:00
parent b7152c4405
commit 54e536cfe0
6 changed files with 117 additions and 83 deletions

View File

@ -21,11 +21,14 @@ type InitCommand struct {
func (c *InitCommand) Run(args []string) int {
var flagBackend, flagGet bool
var flagConfigExtra map[string]interface{}
args = c.Meta.process(args, false)
cmdFlags := c.flagSet("init")
cmdFlags.BoolVar(&flagBackend, "backend", true, "")
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")
cmdFlags.BoolVar(&flagGet, "get", true, "")
cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -225,6 +228,10 @@ 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.
`
return strings.TrimSpace(helpText)
}

View File

@ -270,9 +270,6 @@ func TestInit_backendUnset(t *testing.T) {
t.Fatalf("err: %s", err)
}
// Run it again
defer testInteractiveInput(t, []string{"yes", "yes"})()
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
@ -281,7 +278,7 @@ func TestInit_backendUnset(t *testing.T) {
},
}
args := []string{}
args := []string{"-force-copy"}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}

View File

@ -87,14 +87,18 @@ type Meta struct {
//
// provider is to specify specific resource providers
//
// lockState is set to false to disable state locking
statePath string
stateOutPath string
backupPath string
parallelism int
shadow bool
provider string
stateLock bool
// stateLock is set to false to disable state locking
//
// forceInitCopy suppresses confirmation for copying state data during
// init.
statePath string
stateOutPath string
backupPath string
parallelism int
shadow bool
provider string
stateLock bool
forceInitCopy bool
}
// initStatePaths is used to initialize the default values for

View File

@ -684,16 +684,20 @@ func (m *Meta) backend_c_r_S(
// Get the backend type for output
backendType := s.Backend.Type
// Confirm with the user that the copy should occur
copy, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-local",
Query: fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateLocal), s.Backend.Type),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
copy := m.forceInitCopy
if !copy {
var err error
// Confirm with the user that the copy should occur
copy, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-local",
Query: fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateLocal), s.Backend.Type),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
}
// If we're copying, perform the migration
@ -805,16 +809,19 @@ func (m *Meta) backend_c_R_S(
s := sMgr.State()
// Ask the user if they want to migrate their existing remote state
copy, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf(
"Do you want to copy the legacy remote state from %q?",
s.Remote.Type),
Description: strings.TrimSpace(inputBackendMigrateLegacyLocal),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
copy := m.forceInitCopy
if !copy {
copy, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf(
"Do you want to copy the legacy remote state from %q?",
s.Remote.Type),
Description: strings.TrimSpace(inputBackendMigrateLegacyLocal),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
}
// If the user wants a copy, copy!
@ -898,16 +905,19 @@ func (m *Meta) backend_C_R_s(
// Finally, ask the user if they want to copy the state from
// their old remote state location.
copy, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf(
"Do you want to copy the legacy remote state from %q?",
s.Remote.Type),
Description: strings.TrimSpace(inputBackendMigrateLegacy),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
copy := m.forceInitCopy
if !copy {
copy, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf(
"Do you want to copy the legacy remote state from %q?",
s.Remote.Type),
Description: strings.TrimSpace(inputBackendMigrateLegacy),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
}
// If the user wants a copy, copy!
@ -1055,14 +1065,17 @@ func (m *Meta) backend_C_r_S_changed(
}
// Check with the user if we want to migrate state
copy, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf("Do you want to copy the state from %q?", c.Type),
Description: strings.TrimSpace(fmt.Sprintf(inputBackendMigrateChange, c.Type, s.Backend.Type)),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
copy := m.forceInitCopy
if !copy {
copy, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf("Do you want to copy the state from %q?", c.Type),
Description: strings.TrimSpace(fmt.Sprintf(inputBackendMigrateChange, c.Type, s.Backend.Type)),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
}
// If we are, then we need to initialize the old backend and
@ -1198,16 +1211,19 @@ func (m *Meta) backend_C_R_S_unchanged(
}
// Ask if the user wants to move their legacy remote state
copy, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf(
"Do you want to copy the legacy remote state from %q?",
s.Remote.Type),
Description: strings.TrimSpace(inputBackendMigrateLegacy),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
copy := m.forceInitCopy
if !copy {
copy, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-to-new",
Query: fmt.Sprintf(
"Do you want to copy the legacy remote state from %q?",
s.Remote.Type),
Description: strings.TrimSpace(inputBackendMigrateLegacy),
})
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
}
// If the user wants a copy, copy!

View File

@ -162,21 +162,26 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
currentEnv := m.Env()
// Ask the user if they want to migrate their existing remote state
migrate, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-multistate-to-single",
Query: fmt.Sprintf(
"Destination state %q doesn't support environments (named states).\n"+
"Do you want to copy only your current environment?",
opts.TwoType),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateMultiToSingle),
opts.OneType, opts.TwoType, currentEnv),
})
if err != nil {
return fmt.Errorf(
"Error asking for state migration action: %s", err)
migrate := m.forceInitCopy
if !migrate {
var err error
// Ask the user if they want to migrate their existing remote state
migrate, err = m.confirm(&terraform.InputOpts{
Id: "backend-migrate-multistate-to-single",
Query: fmt.Sprintf(
"Destination state %q doesn't support environments (named states).\n"+
"Do you want to copy only your current environment?",
opts.TwoType),
Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateMultiToSingle),
opts.OneType, opts.TwoType, currentEnv),
})
if err != nil {
return fmt.Errorf(
"Error asking for state migration action: %s", err)
}
}
if !migrate {
return fmt.Errorf("Migration aborted by user.")
}
@ -295,6 +300,10 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
}
func (m *Meta) backendMigrateEmptyConfirm(one, two state.State, opts *backendMigrateOpts) (bool, error) {
if m.forceInitCopy {
return true, nil
}
inputOpts := &terraform.InputOpts{
Id: "backend-migrate-copy-to-empty",
Query: fmt.Sprintf(
@ -357,6 +366,10 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
return false, fmt.Errorf("Error saving temporary state: %s", err)
}
if m.forceInitCopy {
return true, nil
}
// Ask for confirmation
inputOpts := &terraform.InputOpts{
Id: "backend-migrate-to-backend",

View File

@ -480,11 +480,10 @@ func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Ask input
defer testInteractiveInput(t, []string{"yes"})()
// Setup the meta
m := testMetaBackend(t, nil)
// suppress input
m.forceInitCopy = true
// Get the backend
b, err := m.Backend(&BackendOpts{Init: true})
@ -722,12 +721,12 @@ func TestMetaBackend_configureNewLegacyCopy(t *testing.T) {
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Ask input
defer testInteractiveInput(t, []string{"yes", "yes"})()
// Setup the meta
m := testMetaBackend(t, nil)
// suppress input
m.forceInitCopy = true
// Get the backend
b, err := m.Backend(&BackendOpts{Init: true})
if err != nil {
@ -1593,11 +1592,9 @@ func TestMetaBackend_configuredUnchangedLegacyCopy(t *testing.T) {
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Ask input
defer testInteractiveInput(t, []string{"yes", "yes"})()
// Setup the meta
m := testMetaBackend(t, nil)
m.forceInitCopy = true
// Get the backend
b, err := m.Backend(&BackendOpts{Init: true})