From 54e536cfe02a26c5f3885e56489bcc096d3f9638 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 21 Mar 2017 15:05:51 -0400 Subject: [PATCH] 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. --- command/init.go | 7 ++ command/init_test.go | 5 +- command/meta.go | 20 +++--- command/meta_backend.go | 112 ++++++++++++++++++-------------- command/meta_backend_migrate.go | 41 ++++++++---- command/meta_backend_test.go | 15 ++--- 6 files changed, 117 insertions(+), 83 deletions(-) diff --git a/command/init.go b/command/init.go index 534734445..d2a9e835c 100644 --- a/command/init.go +++ b/command/init.go @@ -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) } diff --git a/command/init_test.go b/command/init_test.go index a3fa1b1dd..dee54495d 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -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()) } diff --git a/command/meta.go b/command/meta.go index 0780c544f..0dd4c7884 100644 --- a/command/meta.go +++ b/command/meta.go @@ -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 diff --git a/command/meta_backend.go b/command/meta_backend.go index 494360f00..5019c0242 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -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! diff --git a/command/meta_backend_migrate.go b/command/meta_backend_migrate.go index 643e6e570..b9133a052 100644 --- a/command/meta_backend_migrate.go +++ b/command/meta_backend_migrate.go @@ -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", diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index 5cc8289d4..8c1fe2d66 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -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})