Backend State Migration: change variable names from one/two to source/destination (#29699)

This commit is contained in:
Omar Ismail 2021-10-05 16:36:50 -04:00 committed by GitHub
parent 44764ffdee
commit bea7e3ebce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 107 deletions

View File

@ -753,10 +753,10 @@ func (m *Meta) backend_c_r_S(c *configs.Backend, cHash int, sMgr *clistate.Local
// Perform the migration // Perform the migration
err := m.backendMigrateState(&backendMigrateOpts{ err := m.backendMigrateState(&backendMigrateOpts{
OneType: s.Backend.Type, SourceType: s.Backend.Type,
TwoType: "local", DestinationType: "local",
One: b, Source: b,
Two: localB, Destination: localB,
}) })
if err != nil { if err != nil {
diags = diags.Append(err) diags = diags.Append(err)
@ -829,10 +829,10 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
if len(localStates) > 0 { if len(localStates) > 0 {
// Perform the migration // Perform the migration
err = m.backendMigrateState(&backendMigrateOpts{ err = m.backendMigrateState(&backendMigrateOpts{
OneType: "local", SourceType: "local",
TwoType: c.Type, DestinationType: c.Type,
One: localB, Source: localB,
Two: b, Destination: b,
}) })
if err != nil { if err != nil {
diags = diags.Append(err) diags = diags.Append(err)
@ -944,10 +944,10 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
// Perform the migration // Perform the migration
err := m.backendMigrateState(&backendMigrateOpts{ err := m.backendMigrateState(&backendMigrateOpts{
OneType: s.Backend.Type, SourceType: s.Backend.Type,
TwoType: c.Type, DestinationType: c.Type,
One: oldB, Source: oldB,
Two: b, Destination: b,
}) })
if err != nil { if err != nil {
diags = diags.Append(err) diags = diags.Append(err)

View File

@ -21,13 +21,13 @@ import (
) )
type backendMigrateOpts struct { type backendMigrateOpts struct {
OneType, TwoType string SourceType, DestinationType string
One, Two backend.Backend Source, Destination backend.Backend
// Fields below are set internally when migrate is called // Fields below are set internally when migrate is called
oneEnv string // source env sourceWorkspace string
twoEnv string // dest env destinationWorkspace string
force bool // if true, won't ask for confirmation force bool // if true, won't ask for confirmation
} }
@ -43,45 +43,45 @@ type backendMigrateOpts struct {
// //
// This will attempt to lock both states for the migration. // This will attempt to lock both states for the migration.
func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error { func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
log.Printf("[TRACE] backendMigrateState: need to migrate from %q to %q backend config", opts.OneType, opts.TwoType) log.Printf("[TRACE] backendMigrateState: need to migrate from %q to %q backend config", opts.SourceType, opts.DestinationType)
// We need to check what the named state status is. If we're converting // We need to check what the named state status is. If we're converting
// from multi-state to single-state for example, we need to handle that. // from multi-state to single-state for example, we need to handle that.
var oneSingle, twoSingle bool var sourceSingleState, destinationSingleState bool
oneStates, err := opts.One.Workspaces() sourceWorkspaces, err := opts.Source.Workspaces()
if err == backend.ErrWorkspacesNotSupported { if err == backend.ErrWorkspacesNotSupported {
oneSingle = true sourceSingleState = true
err = nil err = nil
} }
if err != nil { if err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateLoadStates), opts.OneType, err) errMigrateLoadStates), opts.SourceType, err)
} }
twoWorkspaces, err := opts.Two.Workspaces() destinationWorkspaces, err := opts.Destination.Workspaces()
if err == backend.ErrWorkspacesNotSupported { if err == backend.ErrWorkspacesNotSupported {
twoSingle = true destinationSingleState = true
err = nil err = nil
} }
if err != nil { if err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateLoadStates), opts.TwoType, err) errMigrateLoadStates), opts.DestinationType, err)
} }
// Set up defaults // Set up defaults
opts.oneEnv = backend.DefaultStateName opts.sourceWorkspace = backend.DefaultStateName
opts.twoEnv = backend.DefaultStateName opts.destinationWorkspace = backend.DefaultStateName
opts.force = m.forceInitCopy opts.force = m.forceInitCopy
// Disregard remote Terraform version for the state source backend. If it's a // Disregard remote Terraform version for the state source backend. If it's a
// Terraform Cloud remote backend, we don't care about the remote version, // Terraform Cloud remote backend, we don't care about the remote version,
// as we are migrating away and will not break a remote workspace. // as we are migrating away and will not break a remote workspace.
m.ignoreRemoteBackendVersionConflict(opts.One) m.ignoreRemoteBackendVersionConflict(opts.Source)
for _, twoWorkspace := range twoWorkspaces { for _, workspace := range destinationWorkspaces {
// Check the remote Terraform version for the state destination backend. If // Check the remote Terraform version for the state destination backend. If
// it's a Terraform Cloud remote backend, we want to ensure that we don't // it's a Terraform Cloud remote backend, we want to ensure that we don't
// break the workspace by uploading an incompatible state file. // break the workspace by uploading an incompatible state file.
diags := m.remoteBackendVersionCheck(opts.Two, twoWorkspace) diags := m.remoteBackendVersionCheck(opts.Destination, workspace)
if diags.HasErrors() { if diags.HasErrors() {
return diags.Err() return diags.Err()
} }
@ -92,20 +92,20 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
switch { switch {
// Single-state to single-state. This is the easiest case: we just // Single-state to single-state. This is the easiest case: we just
// copy the default state directly. // copy the default state directly.
case oneSingle && twoSingle: case sourceSingleState && destinationSingleState:
return m.backendMigrateState_s_s(opts) return m.backendMigrateState_s_s(opts)
// Single-state to multi-state. This is easy since we just copy // Single-state to multi-state. This is easy since we just copy
// the default state and ignore the rest in the destination. // the default state and ignore the rest in the destination.
case oneSingle && !twoSingle: case sourceSingleState && !destinationSingleState:
return m.backendMigrateState_s_s(opts) return m.backendMigrateState_s_s(opts)
// Multi-state to single-state. If the source has more than the default // Multi-state to single-state. If the source has more than the default
// state this is complicated since we have to ask the user what to do. // state this is complicated since we have to ask the user what to do.
case !oneSingle && twoSingle: case !sourceSingleState && destinationSingleState:
// If the source only has one state and it is the default, // If the source only has one state and it is the default,
// treat it as if it doesn't support multi-state. // treat it as if it doesn't support multi-state.
if len(oneStates) == 1 && oneStates[0] == backend.DefaultStateName { if len(sourceWorkspaces) == 1 && sourceWorkspaces[0] == backend.DefaultStateName {
return m.backendMigrateState_s_s(opts) return m.backendMigrateState_s_s(opts)
} }
@ -113,10 +113,10 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
// Multi-state to multi-state. We merge the states together (migrating // Multi-state to multi-state. We merge the states together (migrating
// each from the source to the destination one by one). // each from the source to the destination one by one).
case !oneSingle && !twoSingle: case !sourceSingleState && !destinationSingleState:
// If the source only has one state and it is the default, // If the source only has one state and it is the default,
// treat it as if it doesn't support multi-state. // treat it as if it doesn't support multi-state.
if len(oneStates) == 1 && oneStates[0] == backend.DefaultStateName { if len(sourceWorkspaces) == 1 && sourceWorkspaces[0] == backend.DefaultStateName {
return m.backendMigrateState_s_s(opts) return m.backendMigrateState_s_s(opts)
} }
@ -154,10 +154,10 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
Id: "backend-migrate-multistate-to-multistate", Id: "backend-migrate-multistate-to-multistate",
Query: fmt.Sprintf( Query: fmt.Sprintf(
"Do you want to migrate all workspaces to %q?", "Do you want to migrate all workspaces to %q?",
opts.TwoType), opts.DestinationType),
Description: fmt.Sprintf( Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateMultiToMulti), strings.TrimSpace(inputBackendMigrateMultiToMulti),
opts.OneType, opts.TwoType), opts.SourceType, opts.DestinationType),
}) })
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
@ -169,20 +169,20 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
} }
// Read all the states // Read all the states
oneStates, err := opts.One.Workspaces() sourceWorkspaces, err := opts.Source.Workspaces()
if err != nil { if err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateLoadStates), opts.OneType, err) errMigrateLoadStates), opts.SourceType, err)
} }
// Sort the states so they're always copied alphabetically // Sort the states so they're always copied alphabetically
sort.Strings(oneStates) sort.Strings(sourceWorkspaces)
// Go through each and migrate // Go through each and migrate
for _, name := range oneStates { for _, name := range sourceWorkspaces {
// Copy the same names // Copy the same names
opts.oneEnv = name opts.sourceWorkspace = name
opts.twoEnv = name opts.destinationWorkspace = name
// Force it, we confirmed above // Force it, we confirmed above
opts.force = true opts.force = true
@ -190,7 +190,7 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
// Perform the migration // Perform the migration
if err := m.backendMigrateState_s_s(opts); err != nil { if err := m.backendMigrateState_s_s(opts); err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateMulti), name, opts.OneType, opts.TwoType, err) errMigrateMulti), name, opts.SourceType, opts.DestinationType, err)
} }
} }
@ -199,7 +199,7 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
// Multi-state to single state. // Multi-state to single state.
func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error { func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
log.Printf("[TRACE] backendMigrateState: target backend type %q does not support named workspaces", opts.TwoType) log.Printf("[TRACE] backendMigrateState: destination backend type %q does not support named workspaces", opts.DestinationType)
currentEnv, err := m.Workspace() currentEnv, err := m.Workspace()
if err != nil { if err != nil {
@ -215,10 +215,10 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
Query: fmt.Sprintf( Query: fmt.Sprintf(
"Destination state %q doesn't support workspaces.\n"+ "Destination state %q doesn't support workspaces.\n"+
"Do you want to copy only your current workspace?", "Do you want to copy only your current workspace?",
opts.TwoType), opts.DestinationType),
Description: fmt.Sprintf( Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateMultiToSingle), strings.TrimSpace(inputBackendMigrateMultiToSingle),
opts.OneType, opts.TwoType, currentEnv), opts.SourceType, opts.DestinationType, currentEnv),
}) })
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
@ -231,7 +231,7 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
} }
// Copy the default state // Copy the default state
opts.oneEnv = currentEnv opts.sourceWorkspace = currentEnv
// now switch back to the default env so we can acccess the new backend // now switch back to the default env so we can acccess the new backend
m.SetWorkspace(backend.DefaultStateName) m.SetWorkspace(backend.DefaultStateName)
@ -241,46 +241,46 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
// Single state to single state, assumed default state name. // Single state to single state, assumed default state name.
func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error { func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
log.Printf("[TRACE] backendMigrateState: migrating %q workspace to %q workspace", opts.oneEnv, opts.twoEnv) log.Printf("[TRACE] backendMigrateState: migrating %q workspace to %q workspace", opts.sourceWorkspace, opts.destinationWorkspace)
stateOne, err := opts.One.StateMgr(opts.oneEnv) sourceState, err := opts.Source.StateMgr(opts.sourceWorkspace)
if err != nil { if err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err) errMigrateSingleLoadDefault), opts.SourceType, err)
} }
if err := stateOne.RefreshState(); err != nil { if err := sourceState.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err) errMigrateSingleLoadDefault), opts.SourceType, err)
} }
// Do not migrate workspaces without state. // Do not migrate workspaces without state.
if stateOne.State().Empty() { if sourceState.State().Empty() {
log.Print("[TRACE] backendMigrateState: source workspace has empty state, so nothing to migrate") log.Print("[TRACE] backendMigrateState: source workspace has empty state, so nothing to migrate")
return nil return nil
} }
stateTwo, err := opts.Two.StateMgr(opts.twoEnv) destinationState, err := opts.Destination.StateMgr(opts.destinationWorkspace)
if err == backend.ErrDefaultWorkspaceNotSupported { if err == backend.ErrDefaultWorkspaceNotSupported {
// If the backend doesn't support using the default state, we ask the user // If the backend doesn't support using the default state, we ask the user
// for a new name and migrate the default state to the given named state. // for a new name and migrate the default state to the given named state.
stateTwo, err = func() (statemgr.Full, error) { destinationState, err = func() (statemgr.Full, error) {
log.Print("[TRACE] backendMigrateState: target doesn't support a default workspace, so we must prompt for a new name") log.Print("[TRACE] backendMigrateState: destination doesn't support a default workspace, so we must prompt for a new name")
name, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{ name, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
Id: "new-state-name", Id: "new-state-name",
Query: fmt.Sprintf( Query: fmt.Sprintf(
"[reset][bold][yellow]The %q backend configuration only allows "+ "[reset][bold][yellow]The %q backend configuration only allows "+
"named workspaces![reset]", "named workspaces![reset]",
opts.TwoType), opts.DestinationType),
Description: strings.TrimSpace(inputBackendNewWorkspaceName), Description: strings.TrimSpace(inputBackendNewWorkspaceName),
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("Error asking for new state name: %s", err) return nil, fmt.Errorf("Error asking for new state name: %s", err)
} }
// Update the name of the target state. // Update the name of the destination state.
opts.twoEnv = name opts.destinationWorkspace = name
stateTwo, err := opts.Two.StateMgr(opts.twoEnv) destinationState, err := opts.Destination.StateMgr(opts.destinationWorkspace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -291,34 +291,34 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
// If the currently selected workspace is the default workspace, then set // If the currently selected workspace is the default workspace, then set
// the named workspace as the new selected workspace. // the named workspace as the new selected workspace.
if workspace == backend.DefaultStateName { if workspace == backend.DefaultStateName {
if err := m.SetWorkspace(opts.twoEnv); err != nil { if err := m.SetWorkspace(opts.destinationWorkspace); err != nil {
return nil, fmt.Errorf("Failed to set new workspace: %s", err) return nil, fmt.Errorf("Failed to set new workspace: %s", err)
} }
} }
return stateTwo, nil return destinationState, nil
}() }()
} }
if err != nil { if err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.TwoType, err) errMigrateSingleLoadDefault), opts.DestinationType, err)
} }
if err := stateTwo.RefreshState(); err != nil { if err := destinationState.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.TwoType, err) errMigrateSingleLoadDefault), opts.DestinationType, err)
} }
// Check if we need migration at all. // Check if we need migration at all.
// This is before taking a lock, because they may also correspond to the same lock. // This is before taking a lock, because they may also correspond to the same lock.
one := stateOne.State() source := sourceState.State()
two := stateTwo.State() destination := destinationState.State()
// no reason to migrate if the state is already there // no reason to migrate if the state is already there
if one.Equal(two) { if source.Equal(destination) {
// Equal isn't identical; it doesn't check lineage. // Equal isn't identical; it doesn't check lineage.
sm1, _ := stateOne.(statemgr.PersistentMeta) sm1, _ := sourceState.(statemgr.PersistentMeta)
sm2, _ := stateTwo.(statemgr.PersistentMeta) sm2, _ := destinationState.(statemgr.PersistentMeta)
if one != nil && two != nil { if source != nil && destination != nil {
if sm1 == nil || sm2 == nil { if sm1 == nil || sm2 == nil {
log.Print("[TRACE] backendMigrateState: both source and destination workspaces have no state, so no migration is needed") log.Print("[TRACE] backendMigrateState: both source and destination workspaces have no state, so no migration is needed")
return nil return nil
@ -336,56 +336,56 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
view := views.NewStateLocker(arguments.ViewHuman, m.View) view := views.NewStateLocker(arguments.ViewHuman, m.View)
locker := clistate.NewLocker(m.stateLockTimeout, view) locker := clistate.NewLocker(m.stateLockTimeout, view)
lockerOne := locker.WithContext(lockCtx) lockerSource := locker.WithContext(lockCtx)
if diags := lockerOne.Lock(stateOne, "migration source state"); diags.HasErrors() { if diags := lockerSource.Lock(sourceState, "migration source state"); diags.HasErrors() {
return diags.Err() return diags.Err()
} }
defer lockerOne.Unlock() defer lockerSource.Unlock()
lockerTwo := locker.WithContext(lockCtx) lockerDestination := locker.WithContext(lockCtx)
if diags := lockerTwo.Lock(stateTwo, "migration destination state"); diags.HasErrors() { if diags := lockerDestination.Lock(destinationState, "migration destination state"); diags.HasErrors() {
return diags.Err() return diags.Err()
} }
defer lockerTwo.Unlock() defer lockerDestination.Unlock()
// We now own a lock, so double check that we have the version // We now own a lock, so double check that we have the version
// corresponding to the lock. // corresponding to the lock.
log.Print("[TRACE] backendMigrateState: refreshing source workspace state") log.Print("[TRACE] backendMigrateState: refreshing source workspace state")
if err := stateOne.RefreshState(); err != nil { if err := sourceState.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err) errMigrateSingleLoadDefault), opts.SourceType, err)
} }
log.Print("[TRACE] backendMigrateState: refreshing target workspace state") log.Print("[TRACE] backendMigrateState: refreshing destination workspace state")
if err := stateTwo.RefreshState(); err != nil { if err := destinationState.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace( return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err) errMigrateSingleLoadDefault), opts.SourceType, err)
} }
one = stateOne.State() source = sourceState.State()
two = stateTwo.State() destination = destinationState.State()
} }
var confirmFunc func(statemgr.Full, statemgr.Full, *backendMigrateOpts) (bool, error) var confirmFunc func(statemgr.Full, statemgr.Full, *backendMigrateOpts) (bool, error)
switch { switch {
// No migration necessary // No migration necessary
case one.Empty() && two.Empty(): case source.Empty() && destination.Empty():
log.Print("[TRACE] backendMigrateState: both source and destination workspaces have empty state, so no migration is required") log.Print("[TRACE] backendMigrateState: both source and destination workspaces have empty state, so no migration is required")
return nil return nil
// No migration necessary if we're inheriting state. // No migration necessary if we're inheriting state.
case one.Empty() && !two.Empty(): case source.Empty() && !destination.Empty():
log.Print("[TRACE] backendMigrateState: source workspace has empty state, so no migration is required") log.Print("[TRACE] backendMigrateState: source workspace has empty state, so no migration is required")
return nil return nil
// We have existing state moving into no state. Ask the user if // We have existing state moving into no state. Ask the user if
// they'd like to do this. // they'd like to do this.
case !one.Empty() && two.Empty(): case !source.Empty() && destination.Empty():
log.Print("[TRACE] backendMigrateState: target workspace has empty state, so might copy source workspace state") log.Print("[TRACE] backendMigrateState: destination workspace has empty state, so might copy source workspace state")
confirmFunc = m.backendMigrateEmptyConfirm confirmFunc = m.backendMigrateEmptyConfirm
// Both states are non-empty, meaning we need to determine which // Both states are non-empty, meaning we need to determine which
// state should be used and update accordingly. // state should be used and update accordingly.
case !one.Empty() && !two.Empty(): case !source.Empty() && !destination.Empty():
log.Print("[TRACE] backendMigrateState: both source and destination workspaces have states, so might overwrite destination with source") log.Print("[TRACE] backendMigrateState: both source and destination workspaces have states, so might overwrite destination with source")
confirmFunc = m.backendMigrateNonEmptyConfirm confirmFunc = m.backendMigrateNonEmptyConfirm
} }
@ -402,7 +402,7 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
} }
// Confirm with the user whether we want to copy state over // Confirm with the user whether we want to copy state over
confirm, err := confirmFunc(stateOne, stateTwo, opts) confirm, err := confirmFunc(sourceState, destinationState, opts)
if err != nil { if err != nil {
log.Print("[TRACE] backendMigrateState: error reading input, so aborting migration") log.Print("[TRACE] backendMigrateState: error reading input, so aborting migration")
return err return err
@ -417,36 +417,36 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
// includes preserving any lineage/serial information where possible, if // includes preserving any lineage/serial information where possible, if
// both managers support such metadata. // both managers support such metadata.
log.Print("[TRACE] backendMigrateState: migration confirmed, so migrating") log.Print("[TRACE] backendMigrateState: migration confirmed, so migrating")
if err := statemgr.Migrate(stateTwo, stateOne); err != nil { if err := statemgr.Migrate(destinationState, sourceState); err != nil {
return fmt.Errorf(strings.TrimSpace(errBackendStateCopy), return fmt.Errorf(strings.TrimSpace(errBackendStateCopy),
opts.OneType, opts.TwoType, err) opts.SourceType, opts.DestinationType, err)
} }
if err := stateTwo.PersistState(); err != nil { if err := destinationState.PersistState(); err != nil {
return fmt.Errorf(strings.TrimSpace(errBackendStateCopy), return fmt.Errorf(strings.TrimSpace(errBackendStateCopy),
opts.OneType, opts.TwoType, err) opts.SourceType, opts.DestinationType, err)
} }
// And we're done. // And we're done.
return nil return nil
} }
func (m *Meta) backendMigrateEmptyConfirm(one, two statemgr.Full, opts *backendMigrateOpts) (bool, error) { func (m *Meta) backendMigrateEmptyConfirm(source, destination statemgr.Full, opts *backendMigrateOpts) (bool, error) {
inputOpts := &terraform.InputOpts{ inputOpts := &terraform.InputOpts{
Id: "backend-migrate-copy-to-empty", Id: "backend-migrate-copy-to-empty",
Query: "Do you want to copy existing state to the new backend?", Query: "Do you want to copy existing state to the new backend?",
Description: fmt.Sprintf( Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateEmpty), strings.TrimSpace(inputBackendMigrateEmpty),
opts.OneType, opts.TwoType), opts.SourceType, opts.DestinationType),
} }
return m.confirm(inputOpts) return m.confirm(inputOpts)
} }
func (m *Meta) backendMigrateNonEmptyConfirm( func (m *Meta) backendMigrateNonEmptyConfirm(
stateOne, stateTwo statemgr.Full, opts *backendMigrateOpts) (bool, error) { sourceState, destinationState statemgr.Full, opts *backendMigrateOpts) (bool, error) {
// We need to grab both states so we can write them to a file // We need to grab both states so we can write them to a file
one := stateOne.State() source := sourceState.State()
two := stateTwo.State() destination := destinationState.State()
// Save both to a temporary // Save both to a temporary
td, err := ioutil.TempDir("", "terraform") td, err := ioutil.TempDir("", "terraform")
@ -462,12 +462,12 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
} }
// Write the states // Write the states
onePath := filepath.Join(td, fmt.Sprintf("1-%s.tfstate", opts.OneType)) sourcePath := filepath.Join(td, fmt.Sprintf("1-%s.tfstate", opts.SourceType))
twoPath := filepath.Join(td, fmt.Sprintf("2-%s.tfstate", opts.TwoType)) destinationPath := filepath.Join(td, fmt.Sprintf("2-%s.tfstate", opts.DestinationType))
if err := saveHelper(opts.OneType, onePath, one); err != nil { if err := saveHelper(opts.SourceType, sourcePath, source); err != nil {
return false, fmt.Errorf("Error saving temporary state: %s", err) return false, fmt.Errorf("Error saving temporary state: %s", err)
} }
if err := saveHelper(opts.TwoType, twoPath, two); err != nil { if err := saveHelper(opts.DestinationType, destinationPath, destination); err != nil {
return false, fmt.Errorf("Error saving temporary state: %s", err) return false, fmt.Errorf("Error saving temporary state: %s", err)
} }
@ -477,7 +477,7 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
Query: "Do you want to copy existing state to the new backend?", Query: "Do you want to copy existing state to the new backend?",
Description: fmt.Sprintf( Description: fmt.Sprintf(
strings.TrimSpace(inputBackendMigrateNonEmpty), strings.TrimSpace(inputBackendMigrateNonEmpty),
opts.OneType, opts.TwoType, onePath, twoPath), opts.SourceType, opts.DestinationType, sourcePath, destinationPath),
} }
// Confirm with the user that the copy should occur // Confirm with the user that the copy should occur
@ -553,7 +553,7 @@ const inputBackendMigrateMultiToSingle = `
The existing %[1]q backend supports workspaces and you currently are The existing %[1]q backend supports workspaces and you currently are
using more than one. The newly configured %[2]q backend doesn't support using more than one. The newly configured %[2]q backend doesn't support
workspaces. If you continue, Terraform will copy your current workspace %[3]q workspaces. If you continue, Terraform will copy your current workspace %[3]q
to the default workspace in the target backend. Your existing workspaces in the to the default workspace in the new backend. Your existing workspaces in the
source backend won't be modified. If you want to switch workspaces, back them source backend won't be modified. If you want to switch workspaces, back them
up, or cancel altogether, answer "no" and Terraform will abort. up, or cancel altogether, answer "no" and Terraform will abort.
` `