Merge pull request #15388 from hashicorp/jbardin/state-mv-backend

terraform state commands ignore the -state flag
This commit is contained in:
James Bardin 2017-06-23 15:53:15 -04:00 committed by GitHub
commit 61b96f0860
5 changed files with 134 additions and 48 deletions

View File

@ -18,46 +18,63 @@ type StateMeta struct{}
// backups to be timestamped rather than just the original state path plus a // backups to be timestamped rather than just the original state path plus a
// backup path. // backup path.
func (c *StateMeta) State(m *Meta) (state.State, error) { func (c *StateMeta) State(m *Meta) (state.State, error) {
// Load the backend var realState state.State
b, err := m.Backend(nil) backupPath := m.backupPath
if err != nil { stateOutPath := m.statePath
return nil, err
// use the specified state
if m.statePath != "" {
realState = &state.LocalState{
Path: m.statePath,
}
} else {
// Load the backend
b, err := m.Backend(nil)
if err != nil {
return nil, err
}
env := m.Workspace()
// Get the state
s, err := b.State(env)
if err != nil {
return nil, err
}
// Get a local backend
localRaw, err := m.Backend(&BackendOpts{ForceLocal: true})
if err != nil {
// This should never fail
panic(err)
}
localB := localRaw.(*backendlocal.Local)
_, stateOutPath, _ = localB.StatePaths(env)
if err != nil {
return nil, err
}
realState = s
} }
env := m.Workspace() // We always backup state commands, so set the back if none was specified
// Get the state // (the default is "-", but some tests bypass the flag parsing).
s, err := b.State(env) if backupPath == "-" || backupPath == "" {
if err != nil { // Determine the backup path. stateOutPath is set to the resulting
return nil, err // file where state is written (cached in the case of remote state)
backupPath = fmt.Sprintf(
"%s.%d%s",
stateOutPath,
time.Now().UTC().Unix(),
DefaultBackupExtension)
} }
// Get a local backend
localRaw, err := m.Backend(&BackendOpts{ForceLocal: true})
if err != nil {
// This should never fail
panic(err)
}
localB := localRaw.(*backendlocal.Local)
_, stateOutPath, _ := localB.StatePaths(env)
if err != nil {
return nil, err
}
// Determine the backup path. stateOutPath is set to the resulting
// file where state is written (cached in the case of remote state)
backupPath := fmt.Sprintf(
"%s.%d%s",
stateOutPath,
time.Now().UTC().Unix(),
DefaultBackupExtension)
// Wrap it for backups // Wrap it for backups
s = &state.BackupState{ realState = &state.BackupState{
Real: s, Real: realState,
Path: backupPath, Path: backupPath,
} }
return s, nil return realState, nil
} }
// filterInstance filters a single instance out of filter results. // filterInstance filters a single instance out of filter results.

View File

@ -203,8 +203,7 @@ Options:
-backup=PATH Path where Terraform should write the backup for the original -backup=PATH Path where Terraform should write the backup for the original
state. This can't be disabled. If not set, Terraform state. This can't be disabled. If not set, Terraform
will write it to the same path as the statefile with will write it to the same path as the statefile with
a backup extension. This backup will be made in addition a backup extension.
to the timestamped backup.
-backup-out=PATH Path where Terraform should write the backup for the destination -backup-out=PATH Path where Terraform should write the backup for the destination
state. This can't be disabled. If not set, Terraform state. This can't be disabled. If not set, Terraform

View File

@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/hashicorp/terraform/helper/copy"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
) )
@ -72,6 +73,86 @@ func TestStateMv(t *testing.T) {
testStateOutput(t, backups[0], testStateMvOutputOriginal) testStateOutput(t, backups[0], testStateMvOutputOriginal)
} }
// don't modify backend state is we supply a -state flag
func TestStateMv_explicitWithBackend(t *testing.T) {
td := tempDir(t)
copy.CopyDir(testFixturePath("init-backend"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
backupPath := filepath.Join(td, "backup")
state := &terraform.State{
Modules: []*terraform.ModuleState{
&terraform.ModuleState{
Path: []string{"root"},
Resources: map[string]*terraform.ResourceState{
"test_instance.foo": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
"test_instance.baz": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
},
},
},
}
statePath := testStateFile(t, state)
// init our backend
ui := new(cli.MockUi)
ic := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{}
if code := ic.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
// only modify statePath
p := testProvider()
ui = new(cli.MockUi)
c := &StateMvCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}
args = []string{
"-backup", backupPath,
"-state", statePath,
"test_instance.foo",
"test_instance.bar",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
// Test it is correct
testStateOutput(t, statePath, testStateMvOutput)
}
func TestStateMv_backupExplicit(t *testing.T) { func TestStateMv_backupExplicit(t *testing.T) {
td := tempDir(t) td := tempDir(t)
defer os.RemoveAll(td) defer os.RemoveAll(td)
@ -132,12 +213,7 @@ func TestStateMv_backupExplicit(t *testing.T) {
// Test it is correct // Test it is correct
testStateOutput(t, statePath, testStateMvOutput) testStateOutput(t, statePath, testStateMvOutput)
// Test we have backups // Test backup
backups := testStateBackups(t, filepath.Dir(statePath))
if len(backups) != 1 {
t.Fatalf("bad: %#v", backups)
}
testStateOutput(t, backups[0], testStateMvOutputOriginal)
testStateOutput(t, backupPath, testStateMvOutputOriginal) testStateOutput(t, backupPath, testStateMvOutputOriginal)
} }

View File

@ -78,8 +78,7 @@ Options:
-backup=PATH Path where Terraform should write the backup -backup=PATH Path where Terraform should write the backup
state. This can't be disabled. If not set, Terraform state. This can't be disabled. If not set, Terraform
will write it to the same path as the statefile with will write it to the same path as the statefile with
a backup extension. This backup will be made in addition a backup extension.
to the timestamped backup.
-state=statefile Path to a Terraform state file to use to look -state=statefile Path to a Terraform state file to use to look
up Terraform-managed resources. By default it will up Terraform-managed resources. By default it will

View File

@ -130,12 +130,7 @@ func TestStateRm_backupExplicit(t *testing.T) {
// Test it is correct // Test it is correct
testStateOutput(t, statePath, testStateRmOutput) testStateOutput(t, statePath, testStateRmOutput)
// Test we have backups // Test backup
backups := testStateBackups(t, filepath.Dir(statePath))
if len(backups) != 1 {
t.Fatalf("bad: %#v", backups)
}
testStateOutput(t, backups[0], testStateRmOutputOriginal)
testStateOutput(t, backupPath, testStateRmOutputOriginal) testStateOutput(t, backupPath, testStateRmOutputOriginal)
} }