Merge pull request #15388 from hashicorp/jbardin/state-mv-backend
terraform state commands ignore the -state flag
This commit is contained in:
commit
61b96f0860
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue