Backend State Migration: change variable names from one/two to source/destination (#29699)
This commit is contained in:
parent
44764ffdee
commit
bea7e3ebce
|
@ -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)
|
||||||
|
|
|
@ -21,14 +21,14 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// backendMigrateState handles migrating (copying) state from one backend
|
// backendMigrateState handles migrating (copying) state from one backend
|
||||||
|
@ -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.
|
||||||
`
|
`
|
||||||
|
|
Loading…
Reference in New Issue