Merge pull request #15652 from hashicorp/jbardin/state-command
state commands with remote state backends
This commit is contained in:
commit
eadda50f02
|
@ -11,30 +11,32 @@ import (
|
|||
)
|
||||
|
||||
// StateMeta is the meta struct that should be embedded in state subcommands.
|
||||
type StateMeta struct{}
|
||||
type StateMeta struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
// State returns the state for this meta. This gets the appropriate state from
|
||||
// the backend, but changes the way that backups are done. This configures
|
||||
// backups to be timestamped rather than just the original state path plus a
|
||||
// backup path.
|
||||
func (c *StateMeta) State(m *Meta) (state.State, error) {
|
||||
func (c *StateMeta) State() (state.State, error) {
|
||||
var realState state.State
|
||||
backupPath := m.backupPath
|
||||
stateOutPath := m.statePath
|
||||
backupPath := c.backupPath
|
||||
stateOutPath := c.statePath
|
||||
|
||||
// use the specified state
|
||||
if m.statePath != "" {
|
||||
if c.statePath != "" {
|
||||
realState = &state.LocalState{
|
||||
Path: m.statePath,
|
||||
Path: c.statePath,
|
||||
}
|
||||
} else {
|
||||
// Load the backend
|
||||
b, err := m.Backend(nil)
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env := m.Workspace()
|
||||
env := c.Workspace()
|
||||
// Get the state
|
||||
s, err := b.State(env)
|
||||
if err != nil {
|
||||
|
@ -42,7 +44,7 @@ func (c *StateMeta) State(m *Meta) (state.State, error) {
|
|||
}
|
||||
|
||||
// Get a local backend
|
||||
localRaw, err := m.Backend(&BackendOpts{ForceLocal: true})
|
||||
localRaw, err := c.Backend(&BackendOpts{ForceLocal: true})
|
||||
if err != nil {
|
||||
// This should never fail
|
||||
panic(err)
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
// StateMvCommand is a Command implementation that shows a single resource.
|
||||
type StateMvCommand struct {
|
||||
Meta
|
||||
StateMeta
|
||||
}
|
||||
|
||||
|
@ -21,12 +20,13 @@ func (c *StateMvCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// We create two metas to track the two states
|
||||
var meta1, meta2 Meta
|
||||
var backupPathOut, statePathOut string
|
||||
|
||||
cmdFlags := c.Meta.flagSet("state mv")
|
||||
cmdFlags.StringVar(&meta1.backupPath, "backup", "-", "backup")
|
||||
cmdFlags.StringVar(&meta1.statePath, "state", DefaultStateFilename, "path")
|
||||
cmdFlags.StringVar(&meta2.backupPath, "backup-out", "-", "backup")
|
||||
cmdFlags.StringVar(&meta2.statePath, "state-out", "", "path")
|
||||
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
|
||||
cmdFlags.StringVar(&c.statePath, "state", "", "path")
|
||||
cmdFlags.StringVar(&backupPathOut, "backup-out", "-", "backup")
|
||||
cmdFlags.StringVar(&statePathOut, "state-out", "", "path")
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
@ -36,16 +36,11 @@ func (c *StateMvCommand) Run(args []string) int {
|
|||
return cli.RunResultHelp
|
||||
}
|
||||
|
||||
// Copy the `-state` flag for output if we weren't given a custom one
|
||||
if meta2.statePath == "" {
|
||||
meta2.statePath = meta1.statePath
|
||||
}
|
||||
|
||||
// Read the from state
|
||||
stateFrom, err := c.StateMeta.State(&meta1)
|
||||
stateFrom, err := c.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
||||
return cli.RunResultHelp
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := stateFrom.RefreshState(); err != nil {
|
||||
|
@ -62,11 +57,14 @@ func (c *StateMvCommand) Run(args []string) int {
|
|||
// Read the destination state
|
||||
stateTo := stateFrom
|
||||
stateToReal := stateFromReal
|
||||
if meta2.statePath != meta1.statePath {
|
||||
stateTo, err = c.StateMeta.State(&meta2)
|
||||
|
||||
if statePathOut != "" {
|
||||
c.statePath = statePathOut
|
||||
c.backupPath = backupPathOut
|
||||
stateTo, err = c.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
||||
return cli.RunResultHelp
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := stateTo.RefreshState(); err != nil {
|
||||
|
@ -185,28 +183,30 @@ func (c *StateMvCommand) addableResult(results []*terraform.StateFilterResult) i
|
|||
|
||||
func (c *StateMvCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: terraform state mv [options] ADDRESS ADDRESS
|
||||
Usage: terraform state mv [options] SOURCE DESTINATION
|
||||
|
||||
Move an item in the state to another location or to a completely different
|
||||
state file.
|
||||
This command will move an item matched by the address given to the
|
||||
destination address. This command can also move to a destination address
|
||||
in a completely different state file.
|
||||
|
||||
This command is useful for module refactors (moving items into a module),
|
||||
configuration refactors (moving items to a completely different or new
|
||||
state file), or generally renaming of resources.
|
||||
This can be used for simple resource renaming, moving items to and from
|
||||
a module, moving entire modules, and more. And because this command can also
|
||||
move data to a completely new state, it can also be used for refactoring
|
||||
one configuration into multiple separately managed Terraform configurations.
|
||||
|
||||
This command creates a timestamped backup of the state on every invocation.
|
||||
This can't be disabled. Due to the destructive nature of this command,
|
||||
the backup is ensured by Terraform for safety reasons.
|
||||
This command will output a backup copy of the state prior to saving any
|
||||
changes. The backup cannot be disabled. Due to the destructive nature
|
||||
of this command, backups are required.
|
||||
|
||||
If you're moving from one state file to a different state file, a backup
|
||||
will be created for each state file.
|
||||
If you're moving an item to a different state file, a backup will be created
|
||||
for each state file.
|
||||
|
||||
Options:
|
||||
|
||||
-backup=PATH Path where Terraform should write the backup for the original
|
||||
state. This can't be disabled. If not set, Terraform
|
||||
will write it to the same path as the statefile with
|
||||
a backup extension.
|
||||
a ".backup" extension.
|
||||
|
||||
-backup-out=PATH Path where Terraform should write the backup for the destination
|
||||
state. This can't be disabled. If not set, Terraform
|
||||
|
@ -215,13 +215,12 @@ Options:
|
|||
to be specified if -state-out is set to a different path
|
||||
than -state.
|
||||
|
||||
-state=PATH Path to a Terraform state file to use to look
|
||||
up Terraform-managed resources. By default it will
|
||||
use the state "terraform.tfstate" if it exists.
|
||||
-state=PATH Path to the source state file. Defaults to the configured
|
||||
backend, or "terraform.tfstate"
|
||||
|
||||
-state-out=PATH Path to the destination state file to move the item
|
||||
to. This defaults to the same statefile. This will
|
||||
overwrite the destination state file.
|
||||
-state-out=PATH Path to the destination state file to write to. If this
|
||||
isn't specified, the source state file will be used. This
|
||||
can be a new or existing path.
|
||||
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
|
|
|
@ -47,9 +47,11 @@ func TestStateMv(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -133,9 +135,11 @@ func TestStateMv_explicitWithBackend(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui = new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -194,9 +198,11 @@ func TestStateMv_backupExplicit(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -244,9 +250,11 @@ func TestStateMv_stateOutNew(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -316,9 +324,11 @@ func TestStateMv_stateOutExisting(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -357,9 +367,11 @@ func TestStateMv_noState(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -418,9 +430,11 @@ func TestStateMv_stateOutNew_count(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -596,9 +610,11 @@ func TestStateMv_stateOutNew_largeCount(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -677,9 +693,11 @@ func TestStateMv_stateOutNew_nestedModule(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -705,6 +723,160 @@ func TestStateMv_stateOutNew_nestedModule(t *testing.T) {
|
|||
testStateOutput(t, backups[0], testStateMvNestedModule_stateOutOriginal)
|
||||
}
|
||||
|
||||
func TestStateMv_withinBackend(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("backend-unchanged"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// the local backend state file is "foo"
|
||||
statePath := "local-state.tfstate"
|
||||
backupPath := "local-state.backup"
|
||||
|
||||
f, err := os.Create(statePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := terraform.WriteState(state, f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-backup", backupPath,
|
||||
"test_instance.foo",
|
||||
"test_instance.bar",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
testStateOutput(t, statePath, testStateMvOutput)
|
||||
testStateOutput(t, backupPath, testStateMvOutputOriginal)
|
||||
}
|
||||
|
||||
func TestStateMv_fromBackendToLocal(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("backend-unchanged"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// the local backend state file is "foo"
|
||||
statePath := "local-state.tfstate"
|
||||
|
||||
// real "local" state file
|
||||
statePathOut := "real-local.tfstate"
|
||||
|
||||
f, err := os.Create(statePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := terraform.WriteState(state, f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateMvCommand{
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-state-out", statePathOut,
|
||||
"test_instance.foo",
|
||||
"test_instance.bar",
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
testStateOutput(t, statePathOut, testStateMvCount_stateOutSrc)
|
||||
|
||||
// the backend state should be left with only baz
|
||||
testStateOutput(t, statePath, testStateMvOriginal_backend)
|
||||
}
|
||||
|
||||
const testStateMvOutputOriginal = `
|
||||
test_instance.baz:
|
||||
ID = foo
|
||||
|
@ -943,3 +1115,10 @@ const testStateMvExisting_stateDstOriginal = `
|
|||
test_instance.qux:
|
||||
ID = bar
|
||||
`
|
||||
|
||||
const testStateMvOriginal_backend = `
|
||||
test_instance.baz:
|
||||
ID = foo
|
||||
bar = value
|
||||
foo = value
|
||||
`
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
// StateRmCommand is a Command implementation that shows a single resource.
|
||||
type StateRmCommand struct {
|
||||
Meta
|
||||
StateMeta
|
||||
}
|
||||
|
||||
|
@ -20,8 +19,8 @@ func (c *StateRmCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
cmdFlags := c.Meta.flagSet("state show")
|
||||
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "-", "backup")
|
||||
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
|
||||
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
|
||||
cmdFlags.StringVar(&c.statePath, "state", "", "path")
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
return cli.RunResultHelp
|
||||
}
|
||||
|
@ -32,10 +31,10 @@ func (c *StateRmCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
state, err := c.StateMeta.State(&c.Meta)
|
||||
state, err := c.State()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
||||
return cli.RunResultHelp
|
||||
return 1
|
||||
}
|
||||
if err := state.RefreshState(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/copy"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
@ -47,9 +48,11 @@ func TestStateRm(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateRmCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -109,9 +112,11 @@ func TestStateRmNoArgs(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateRmCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -169,9 +174,11 @@ func TestStateRm_backupExplicit(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateRmCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -198,9 +205,11 @@ func TestStateRm_noState(t *testing.T) {
|
|||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateRmCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -210,6 +219,110 @@ func TestStateRm_noState(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStateRm_needsInit(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("backend-change"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateRmCommand{
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{"foo"}
|
||||
if code := c.Run(args); code == 0 {
|
||||
t.Fatal("expected error\noutput:", ui.OutputWriter)
|
||||
}
|
||||
|
||||
if !strings.Contains(ui.ErrorWriter.String(), "Initialization") {
|
||||
t.Fatal("expected initialization error, got:\n", ui.ErrorWriter)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateRm_backendState(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("backend-unchanged"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
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.bar": &terraform.ResourceState{
|
||||
Type: "test_instance",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "foo",
|
||||
Attributes: map[string]string{
|
||||
"foo": "value",
|
||||
"bar": "value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// the local backend state file is "foo"
|
||||
statePath := "local-state.tfstate"
|
||||
backupPath := "local-state.backup"
|
||||
|
||||
f, err := os.Create(statePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := terraform.WriteState(state, f); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
c := &StateRmCommand{
|
||||
StateMeta{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-backup", backupPath,
|
||||
"test_instance.foo",
|
||||
}
|
||||
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, testStateRmOutput)
|
||||
|
||||
// Test backup
|
||||
testStateOutput(t, backupPath, testStateRmOutputOriginal)
|
||||
}
|
||||
|
||||
const testStateRmOutputOriginal = `
|
||||
test_instance.bar:
|
||||
ID = foo
|
||||
|
|
|
@ -28,7 +28,7 @@ func TestStateDefaultBackupExtension(t *testing.T) {
|
|||
tmp, cwd := testCwd(t)
|
||||
defer testFixCwd(t, tmp, cwd)
|
||||
|
||||
s, err := (&StateMeta{}).State(&Meta{})
|
||||
s, err := (&StateMeta{}).State()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -276,13 +276,17 @@ func init() {
|
|||
|
||||
"state rm": func() (cli.Command, error) {
|
||||
return &command.StateRmCommand{
|
||||
Meta: meta,
|
||||
StateMeta: command.StateMeta{
|
||||
Meta: meta,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
||||
"state mv": func() (cli.Command, error) {
|
||||
return &command.StateMvCommand{
|
||||
Meta: meta,
|
||||
StateMeta: command.StateMeta{
|
||||
Meta: meta,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
||||
|
|
|
@ -40,19 +40,22 @@ in [resource addressing format](/docs/commands/state/addressing.html).
|
|||
|
||||
The command-line flags are all optional. The list of available flags are:
|
||||
|
||||
* `-backup=path` - Path to a backup file Defaults to the state path plus
|
||||
a timestamp with the ".backup" extension.
|
||||
* `-backup=path` - Path where Terraform should write the backup for the
|
||||
original state. This can't be disabled. If not set, Terraform will write it
|
||||
to the same path as the statefile with a ".backup" extension.
|
||||
|
||||
* `-backup-out=path` - Path to the backup file for the output state.
|
||||
This is only necessary if `-state-out` is specified.
|
||||
* `-backup-out=path` - Path where Terraform should write the backup for the
|
||||
destination state. This can't be disabled. If not set, Terraform will write
|
||||
it to the same path as the destination state file with a backup extension.
|
||||
This only needs to be specified if -state-out is set to a different path than
|
||||
-state.
|
||||
|
||||
* `-state=path` - Path to the state file. Defaults to "terraform.tfstate".
|
||||
Ignored when [remote state](/docs/state/remote.html) is used.
|
||||
* `-state=path` - Path to the source state file to read from. Defaults to the
|
||||
configured backend, or "terraform.tfstate".
|
||||
|
||||
* `-state-out=path` - Path to the state file to write to. If this isn't specified
|
||||
the state specified by `-state` will be used. This can be
|
||||
a new or existing path. Ignored when
|
||||
[remote state](/docs/state/remote.html) is used.
|
||||
* `-state-out=path` - Path to the destination state file to write to. If this
|
||||
isn't specified the source state file will be used. This can be a new or
|
||||
existing path.
|
||||
|
||||
## Example: Rename a Resource
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ and more.
|
|||
|
||||
Usage: `terraform state rm [options] ADDRESS...`
|
||||
|
||||
The command will remove all the items matched by the addresses given.
|
||||
Remove one or more items from the Terraform state.
|
||||
|
||||
Items removed from the Terraform state are _not physically destroyed_.
|
||||
Items removed from the Terraform state are only no longer managed by
|
||||
|
@ -43,10 +43,13 @@ in [resource addressing format](/docs/commands/state/addressing.html).
|
|||
|
||||
The command-line flags are all optional. The list of available flags are:
|
||||
|
||||
* `-backup=path` - Path to a backup file Defaults to the state path plus
|
||||
a timestamp with the ".backup" extension.
|
||||
* `-backup=path` - Path where Terraform should write the backup state. This
|
||||
can't be disabled. If not set, Terraform will write it to the same path as
|
||||
the statefile with a backup extension.
|
||||
|
||||
* `-state=path` - Path to the state file. Defaults to "terraform.tfstate".
|
||||
* `-state=path` - Path to a Terraform state file to use to look up
|
||||
Terraform-managed resources. By default it will use the configured backend,
|
||||
or the default "terraform.tfstate" if it exists.
|
||||
|
||||
## Example: Remove a Resource
|
||||
|
||||
|
|
Loading…
Reference in New Issue