Update delete command

This commit is contained in:
Seth Vargo 2017-09-05 00:00:06 -04:00
parent fd5ba4c5ed
commit 38823efa70
No known key found for this signature in database
GPG Key ID: C921994F9C27E0FF
2 changed files with 187 additions and 83 deletions

View File

@ -4,64 +4,93 @@ import (
"fmt"
"strings"
"github.com/hashicorp/vault/meta"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*DeleteCommand)(nil)
var _ cli.CommandAutocomplete = (*DeleteCommand)(nil)
// DeleteCommand is a Command that puts data into the Vault.
type DeleteCommand struct {
meta.Meta
}
func (c *DeleteCommand) Run(args []string) int {
flags := c.Meta.FlagSet("delete", meta.FlagSetDefault)
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
args = flags.Args()
if len(args) != 1 {
c.Ui.Error("delete expects one argument")
flags.Usage()
return 1
}
path := args[0]
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client: %s", err))
return 2
}
if _, err := client.Logical().Delete(path); err != nil {
c.Ui.Error(fmt.Sprintf(
"Error deleting '%s': %s", path, err))
return 1
}
c.Ui.Output(fmt.Sprintf("Success! Deleted '%s' if it existed.", path))
return 0
*BaseCommand
}
func (c *DeleteCommand) Synopsis() string {
return "Delete operation on secrets in Vault"
return "Deletes secrets and configuration"
}
func (c *DeleteCommand) Help() string {
helpText := `
Usage: vault delete [options] path
Usage: vault delete [options] PATH
Delete data (secrets or configuration) from Vault.
Deletes secrets and configuration from Vault at the given path. The behavior
of "delete" is delegated to the backend corresponding to the given path.
Delete sends a delete operation request to the given path. The
behavior of the delete is determined by the backend at the given
path. For example, deleting "aws/policy/ops" will delete the "ops"
policy for the AWS backend. Use "vault help" for more details on
whether delete is supported for a path and what the behavior is.
Remove data in the status secret backend:
$ vault delete secret/my-secret
Uninstall an encryption key in the transit backend:
$ vault delete transit/keys/my-key
Delete an IAM role:
$ vault delete aws/roles/ops
For a full list of examples and paths, please see the documentation that
corresponds to the secret backend in use.
` + c.Flags().Help()
General Options:
` + meta.GeneralOptionsUsage()
return strings.TrimSpace(helpText)
}
func (c *DeleteCommand) Flags() *FlagSets {
return c.flagSet(FlagSetHTTP)
}
func (c *DeleteCommand) AutocompleteArgs() complete.Predictor {
return c.PredictVaultFiles()
}
func (c *DeleteCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}
func (c *DeleteCommand) Run(args []string) int {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
args = f.Args()
path, kvs, err := extractPath(args)
if err != nil {
c.UI.Error(err.Error())
return 1
}
if len(kvs) > 0 {
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args)))
return 1
}
client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
return 2
}
if _, err := client.Logical().Delete(path); err != nil {
c.UI.Error(fmt.Sprintf("Error deleting %s: %s", path, err))
return 2
}
c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", path))
return 0
}

View File

@ -1,56 +1,131 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/meta"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
func TestDelete(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
func testDeleteCommand(tb testing.TB) (*cli.MockUi, *DeleteCommand) {
tb.Helper()
ui := new(cli.MockUi)
c := &DeleteCommand{
Meta: meta.Meta{
ClientToken: token,
Ui: ui,
ui := cli.NewMockUi()
return ui, &DeleteCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
}
}
func TestDeleteCommand_Run(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
out string
code int
}{
{
"empty",
nil,
"Missing PATH!",
1,
},
{
"slash",
[]string{"/"},
"Missing PATH!",
1,
},
}
args := []string{
"-address", addr,
"secret/foo",
}
t.Run("validations", func(t *testing.T) {
t.Parallel()
// Run once so the client is setup, ignore errors
c.Run(args)
for _, tc := range cases {
tc := tc
// Get the client so we can write data
client, err := c.Client()
if err != nil {
t.Fatalf("err: %s", err)
}
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
data := map[string]interface{}{"value": "bar"}
if _, err := client.Logical().Write("secret/foo", data); err != nil {
t.Fatalf("err: %s", err)
}
ui, cmd := testDeleteCommand(t)
// Run the delete
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
code := cmd.Run(tc.args)
if code != tc.code {
t.Errorf("expected %d to be %d", code, tc.code)
}
resp, err := client.Logical().Read("secret/foo")
if err != nil {
t.Fatalf("err: %s", err)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, tc.out) {
t.Errorf("expected %q to contain %q", combined, tc.out)
}
})
}
})
t.Run("integration", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
if _, err := client.Logical().Write("secret/delete/foo", map[string]interface{}{
"foo": "bar",
}); err != nil {
t.Fatal(err)
}
ui, cmd := testDeleteCommand(t)
cmd.client = client
code := cmd.Run([]string{
"secret/delete/foo",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Data deleted (if it existed) at: secret/delete/foo"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
secret, _ := client.Logical().Read("secret/delete/foo")
if secret != nil {
t.Errorf("expected deletion: %#v", secret)
}
})
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServerBad(t)
defer closer()
ui, cmd := testDeleteCommand(t)
cmd.client = client
code := cmd.Run([]string{
"secret/delete/foo",
})
if exp := 2; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error deleting secret/delete/foo: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})
t.Run("no_tabs", func(t *testing.T) {
t.Parallel()
_, cmd := testDeleteCommand(t)
assertNoTabs(t, cmd)
})
}