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 { func (c *InitCommand) Run(args []string) int {
var flagBackend, flagGet bool var flagBackend, flagGet bool
var flagConfigExtra map[string]interface{} var flagConfigExtra map[string]interface{}
args = c.Meta.process(args, false) args = c.Meta.process(args, false)
cmdFlags := c.flagSet("init") cmdFlags := c.flagSet("init")
cmdFlags.BoolVar(&flagBackend, "backend", true, "") cmdFlags.BoolVar(&flagBackend, "backend", true, "")
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "") cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")
cmdFlags.BoolVar(&flagGet, "get", true, "") 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()) } cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
return 1 return 1
@ -225,6 +228,10 @@ Options:
-no-color If specified, output won't contain any color. -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) return strings.TrimSpace(helpText)
} }

View File

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

View File

@ -87,14 +87,18 @@ type Meta struct {
// //
// provider is to specify specific resource providers // provider is to specify specific resource providers
// //
// lockState is set to false to disable state locking // stateLock is set to false to disable state locking
statePath string //
stateOutPath string // forceInitCopy suppresses confirmation for copying state data during
backupPath string // init.
parallelism int statePath string
shadow bool stateOutPath string
provider string backupPath string
stateLock bool parallelism int
shadow bool
provider string
stateLock bool
forceInitCopy bool
} }
// initStatePaths is used to initialize the default values for // 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 // Get the backend type for output
backendType := s.Backend.Type backendType := s.Backend.Type
// Confirm with the user that the copy should occur copy := m.forceInitCopy
copy, err := m.confirm(&terraform.InputOpts{ if !copy {
Id: "backend-migrate-to-local", var err error
Query: fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type), // Confirm with the user that the copy should occur
Description: fmt.Sprintf( copy, err = m.confirm(&terraform.InputOpts{
strings.TrimSpace(inputBackendMigrateLocal), s.Backend.Type), Id: "backend-migrate-to-local",
}) Query: fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type),
if err != nil { Description: fmt.Sprintf(
return nil, fmt.Errorf( strings.TrimSpace(inputBackendMigrateLocal), s.Backend.Type),
"Error asking for state copy action: %s", err) })
if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
} }
// If we're copying, perform the migration // If we're copying, perform the migration
@ -805,16 +809,19 @@ func (m *Meta) backend_c_R_S(
s := sMgr.State() s := sMgr.State()
// Ask the user if they want to migrate their existing remote state // Ask the user if they want to migrate their existing remote state
copy, err := m.confirm(&terraform.InputOpts{ copy := m.forceInitCopy
Id: "backend-migrate-to-new", if !copy {
Query: fmt.Sprintf( copy, err = m.confirm(&terraform.InputOpts{
"Do you want to copy the legacy remote state from %q?", Id: "backend-migrate-to-new",
s.Remote.Type), Query: fmt.Sprintf(
Description: strings.TrimSpace(inputBackendMigrateLegacyLocal), "Do you want to copy the legacy remote state from %q?",
}) s.Remote.Type),
if err != nil { Description: strings.TrimSpace(inputBackendMigrateLegacyLocal),
return nil, fmt.Errorf( })
"Error asking for state copy action: %s", err) if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
} }
// If the user wants a copy, copy! // 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 // Finally, ask the user if they want to copy the state from
// their old remote state location. // their old remote state location.
copy, err := m.confirm(&terraform.InputOpts{ copy := m.forceInitCopy
Id: "backend-migrate-to-new", if !copy {
Query: fmt.Sprintf( copy, err = m.confirm(&terraform.InputOpts{
"Do you want to copy the legacy remote state from %q?", Id: "backend-migrate-to-new",
s.Remote.Type), Query: fmt.Sprintf(
Description: strings.TrimSpace(inputBackendMigrateLegacy), "Do you want to copy the legacy remote state from %q?",
}) s.Remote.Type),
if err != nil { Description: strings.TrimSpace(inputBackendMigrateLegacy),
return nil, fmt.Errorf( })
"Error asking for state copy action: %s", err) if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
} }
// If the user wants a copy, copy! // 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 // Check with the user if we want to migrate state
copy, err := m.confirm(&terraform.InputOpts{ copy := m.forceInitCopy
Id: "backend-migrate-to-new", if !copy {
Query: fmt.Sprintf("Do you want to copy the state from %q?", c.Type), copy, err = m.confirm(&terraform.InputOpts{
Description: strings.TrimSpace(fmt.Sprintf(inputBackendMigrateChange, c.Type, s.Backend.Type)), Id: "backend-migrate-to-new",
}) Query: fmt.Sprintf("Do you want to copy the state from %q?", c.Type),
if err != nil { Description: strings.TrimSpace(fmt.Sprintf(inputBackendMigrateChange, c.Type, s.Backend.Type)),
return nil, fmt.Errorf( })
"Error asking for state copy action: %s", err) 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 // 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 // Ask if the user wants to move their legacy remote state
copy, err := m.confirm(&terraform.InputOpts{ copy := m.forceInitCopy
Id: "backend-migrate-to-new", if !copy {
Query: fmt.Sprintf( copy, err = m.confirm(&terraform.InputOpts{
"Do you want to copy the legacy remote state from %q?", Id: "backend-migrate-to-new",
s.Remote.Type), Query: fmt.Sprintf(
Description: strings.TrimSpace(inputBackendMigrateLegacy), "Do you want to copy the legacy remote state from %q?",
}) s.Remote.Type),
if err != nil { Description: strings.TrimSpace(inputBackendMigrateLegacy),
return nil, fmt.Errorf( })
"Error asking for state copy action: %s", err) if err != nil {
return nil, fmt.Errorf(
"Error asking for state copy action: %s", err)
}
} }
// If the user wants a copy, copy! // 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 { func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
currentEnv := m.Env() currentEnv := m.Env()
// Ask the user if they want to migrate their existing remote state migrate := m.forceInitCopy
migrate, err := m.confirm(&terraform.InputOpts{ if !migrate {
Id: "backend-migrate-multistate-to-single", var err error
Query: fmt.Sprintf( // Ask the user if they want to migrate their existing remote state
"Destination state %q doesn't support environments (named states).\n"+ migrate, err = m.confirm(&terraform.InputOpts{
"Do you want to copy only your current environment?", Id: "backend-migrate-multistate-to-single",
opts.TwoType), Query: fmt.Sprintf(
Description: fmt.Sprintf( "Destination state %q doesn't support environments (named states).\n"+
strings.TrimSpace(inputBackendMigrateMultiToSingle), "Do you want to copy only your current environment?",
opts.OneType, opts.TwoType, currentEnv), opts.TwoType),
}) Description: fmt.Sprintf(
if err != nil { strings.TrimSpace(inputBackendMigrateMultiToSingle),
return fmt.Errorf( opts.OneType, opts.TwoType, currentEnv),
"Error asking for state migration action: %s", err) })
if err != nil {
return fmt.Errorf(
"Error asking for state migration action: %s", err)
}
} }
if !migrate { if !migrate {
return fmt.Errorf("Migration aborted by user.") 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) { func (m *Meta) backendMigrateEmptyConfirm(one, two state.State, opts *backendMigrateOpts) (bool, error) {
if m.forceInitCopy {
return true, nil
}
inputOpts := &terraform.InputOpts{ inputOpts := &terraform.InputOpts{
Id: "backend-migrate-copy-to-empty", Id: "backend-migrate-copy-to-empty",
Query: fmt.Sprintf( Query: fmt.Sprintf(
@ -357,6 +366,10 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
return false, fmt.Errorf("Error saving temporary state: %s", err) return false, fmt.Errorf("Error saving temporary state: %s", err)
} }
if m.forceInitCopy {
return true, nil
}
// Ask for confirmation // Ask for confirmation
inputOpts := &terraform.InputOpts{ inputOpts := &terraform.InputOpts{
Id: "backend-migrate-to-backend", Id: "backend-migrate-to-backend",

View File

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