command: terraform state rm to require at least one argument

Due to how the state filter machinery works, passing no arguments is valid
and matches _all_ resources.

It is very unlikely that someone wants to remove everything from state, so
this ends up being a very dangerous default for the "terraform state rm"
command, and surprising for someone who perhaps runs it looking for the
usage information.

So we'll be pragmatic here and reject the no-arguments case for this
command, accepting that it makes the unlikely case of intentionally
deleting all resources harder in order to make it less likely that it
will happen _unintentionally_.

If someone does really want to remove all resources from the state, they
can provide an explicit empty string argument, but this isn't documented
because it's a weird case that doesn't seem worth mentioning.

This fixes #15283.
This commit is contained in:
Martin Atkins 2017-07-05 14:58:08 -07:00
parent d5ebad33a4
commit fee1197cf9
2 changed files with 62 additions and 0 deletions

View File

@ -24,6 +24,11 @@ func (c *StateRmCommand) Run(args []string) int {
}
args = cmdFlags.Args()
if len(args) < 1 {
c.Ui.Error("At least one resource address is required.")
return 1
}
state, err := c.StateMeta.State(&c.Meta)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))

View File

@ -3,6 +3,7 @@ package command
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/hashicorp/terraform/terraform"
@ -71,6 +72,62 @@ func TestStateRm(t *testing.T) {
testStateOutput(t, backups[0], testStateRmOutputOriginal)
}
func TestStateRmNoArgs(t *testing.T) {
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",
},
},
},
},
},
},
}
statePath := testStateFile(t, state)
p := testProvider()
ui := new(cli.MockUi)
c := &StateRmCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}
args := []string{
"-state", statePath,
}
if code := c.Run(args); code != 1 {
t.Errorf("wrong exit status %d; want %d", code, 1)
}
if msg := ui.ErrorWriter.String(); !strings.Contains(msg, "At least one resource address") {
t.Errorf("not the error we were looking for:\n%s", msg)
}
}
func TestStateRm_backupExplicit(t *testing.T) {
td := tempDir(t)
defer os.RemoveAll(td)