command: Fix TestInit_inputFalse

This test was using old-style state files as its input, differing only by
lineage. Since lineages are now managed within the state manager itself,
the test can't use that to distinguish the two files and so we put a
different output in each one instead.

This also introduces some TRACE logging to the migration codepaths.
There's some hard-to-follow control flow here and so this extra logging
helps to understand the reason for a particular outcome, and since this
codepath is visited only in "terraform init" anyway it doesn't hurt to
be a bit more verbose here.
This commit is contained in:
Martin Atkins 2018-11-09 14:26:01 -08:00
parent c0b7f58143
commit f6d468ffd5
2 changed files with 40 additions and 7 deletions

View File

@ -11,15 +11,18 @@ import (
"strings"
"testing"
"github.com/hashicorp/terraform/configs"
"github.com/mitchellh/cli"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/backend/local"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/helper/copy"
"github.com/hashicorp/terraform/plugin/discovery"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/states/statemgr"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
)
func TestInit_empty(t *testing.T) {
@ -553,13 +556,24 @@ func TestInit_inputFalse(t *testing.T) {
}
// write different states for foo and bar
s := terraform.NewState()
s.Lineage = "foo"
if err := (&state.LocalState{Path: "foo"}).WriteState(s); err != nil {
fooState := states.BuildState(func(s *states.SyncState) {
s.SetOutputValue(
addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
cty.StringVal("foo"),
false, // not sensitive
)
})
if err := statemgr.NewFilesystem("foo").WriteState(fooState); err != nil {
t.Fatal(err)
}
s.Lineage = "bar"
if err := (&state.LocalState{Path: "bar"}).WriteState(s); err != nil {
barState := states.BuildState(func(s *states.SyncState) {
s.SetOutputValue(
addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
cty.StringVal("bar"),
false, // not sensitive
)
})
if err := statemgr.NewFilesystem("bar").WriteState(barState); err != nil {
t.Fatal(err)
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
@ -41,6 +42,7 @@ type backendMigrateOpts struct {
//
// This will attempt to lock both states for the migration.
func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
log.Printf("[TRACE] backendMigrateState: need to migrate from %q to %q backend config", opts.OneType, opts.TwoType)
// 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.
var oneSingle, twoSingle bool
@ -126,6 +128,8 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error {
// Multi-state to multi-state.
func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
log.Print("[TRACE] backendMigrateState: migrating all named workspaces")
// Ask the user if they want to migrate their existing remote state
migrate, err := m.confirm(&terraform.InputOpts{
Id: "backend-migrate-multistate-to-multistate",
@ -175,6 +179,8 @@ func (m *Meta) backendMigrateState_S_S(opts *backendMigrateOpts) error {
// Multi-state to single state.
func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
log.Printf("[TRACE] backendMigrateState: target backend type %q does not support named workspaces", opts.TwoType)
currentEnv := m.Workspace()
migrate := opts.force
@ -212,6 +218,8 @@ func (m *Meta) backendMigrateState_S_s(opts *backendMigrateOpts) error {
// Single state to single state, assumed default state name.
func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
log.Printf("[TRACE] backendMigrateState: migrating %q workspace to %q workspace", opts.oneEnv, opts.twoEnv)
stateOne, err := opts.One.StateMgr(opts.oneEnv)
if err != nil {
return fmt.Errorf(strings.TrimSpace(
@ -224,6 +232,7 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
// Do not migrate workspaces without state.
if stateOne.State().Empty() {
log.Print("[TRACE] backendMigrateState: source workspace has empty state, so nothing to migrate")
return nil
}
@ -232,6 +241,7 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
// 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.
stateTwo, err = func() (statemgr.Full, error) {
log.Print("[TRACE] backendMigrateState: target doesn't support a default workspace, so we must prompt for a new name")
name, err := m.UIInput().Input(&terraform.InputOpts{
Id: "new-state-name",
Query: fmt.Sprintf(
@ -284,9 +294,11 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
sm2, _ := stateTwo.(statemgr.PersistentMeta)
if one != nil && two != nil {
if sm1 == nil || sm2 == nil {
log.Print("[TRACE] backendMigrateState: both source and destination workspaces have no state, so no migration is needed")
return nil
}
if sm1.StateSnapshotMeta().Lineage == sm2.StateSnapshotMeta().Lineage {
log.Printf("[TRACE] backendMigrateState: both source and destination workspaces have equal state with lineage %q, so no migration is needed", sm1.StateSnapshotMeta().Lineage)
return nil
}
}
@ -309,10 +321,12 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
// We now own a lock, so double check that we have the version
// corresponding to the lock.
log.Print("[TRACE] backendMigrateState: refreshing source workspace state")
if err := stateOne.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err)
}
log.Print("[TRACE] backendMigrateState: refreshing target workspace state")
if err := stateTwo.RefreshState(); err != nil {
return fmt.Errorf(strings.TrimSpace(
errMigrateSingleLoadDefault), opts.OneType, err)
@ -326,20 +340,24 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
switch {
// No migration necessary
case one.Empty() && two.Empty():
log.Print("[TRACE] backendMigrateState: both source and destination workspaces have empty state, so no migration is required")
return nil
// No migration necessary if we're inheriting state.
case one.Empty() && !two.Empty():
log.Print("[TRACE] backendMigrateState: source workspace has empty state, so no migration is required")
return nil
// We have existing state moving into no state. Ask the user if
// they'd like to do this.
case !one.Empty() && two.Empty():
log.Print("[TRACE] backendMigrateState: target workspace has empty state, so might copy source workspace state")
confirmFunc = m.backendMigrateEmptyConfirm
// Both states are non-empty, meaning we need to determine which
// state should be used and update accordingly.
case !one.Empty() && !two.Empty():
log.Print("[TRACE] backendMigrateState: both source and destination workspaces have states, so might overwrite destination with source")
confirmFunc = m.backendMigrateNonEmptyConfirm
}
@ -350,6 +368,7 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
if !opts.force {
// Abort if we can't ask for input.
if !m.input {
log.Print("[TRACE] backendMigrateState: can't prompt for input, so aborting migration")
return errors.New("error asking for state migration action: input disabled")
}