From 9a80d9a8f848553341f9da0134ce152e77d6c855 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 7 Sep 2017 21:59:31 -0400 Subject: [PATCH] Add lease subcommand --- command/lease.go | 40 ++++++++++++++ command/{renew.go => lease_renew.go} | 24 ++++----- .../{renew_test.go => lease_renew_test.go} | 22 ++++---- command/{revoke.go => lease_revoke.go} | 52 +++++++++---------- .../{revoke_test.go => lease_revoke_test.go} | 12 ++--- 5 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 command/lease.go rename command/{renew.go => lease_renew.go} (79%) rename command/{renew_test.go => lease_renew_test.go} (81%) rename command/{revoke.go => lease_revoke.go} (66%) rename command/{revoke_test.go => lease_revoke_test.go} (88%) diff --git a/command/lease.go b/command/lease.go new file mode 100644 index 0000000000..76f6cc174c --- /dev/null +++ b/command/lease.go @@ -0,0 +1,40 @@ +package command + +import ( + "strings" + + "github.com/mitchellh/cli" +) + +var _ cli.Command = (*LeaseCommand)(nil) + +type LeaseCommand struct { + *BaseCommand +} + +func (c *LeaseCommand) Synopsis() string { + return "Interact with leases" +} + +func (c *LeaseCommand) Help() string { + helpText := ` +Usage: vault lease [options] [args] + + This command groups subcommands for interacting with leases. Users can revoke + or renew leases. + + Renew a lease: + + $ vault lease renew database/creds/readonly/2f6a614c... + + Revoke a lease: + + $ vault lease revoke database/creds/readonly/2f6a614c... +` + + return strings.TrimSpace(helpText) +} + +func (c *LeaseCommand) Run(args []string) int { + return cli.RunResultHelp +} diff --git a/command/renew.go b/command/lease_renew.go similarity index 79% rename from command/renew.go rename to command/lease_renew.go index c2130d122a..4dd2e1c573 100644 --- a/command/renew.go +++ b/command/lease_renew.go @@ -9,24 +9,22 @@ import ( "github.com/posener/complete" ) -// Ensure we are implementing the right interfaces. -var _ cli.Command = (*RenewCommand)(nil) -var _ cli.CommandAutocomplete = (*RenewCommand)(nil) +var _ cli.Command = (*LeaseRenewCommand)(nil) +var _ cli.CommandAutocomplete = (*LeaseRenewCommand)(nil) -// RenewCommand is a Command that mounts a new mount. -type RenewCommand struct { +type LeaseRenewCommand struct { *BaseCommand flagIncrement time.Duration } -func (c *RenewCommand) Synopsis() string { +func (c *LeaseRenewCommand) Synopsis() string { return "Renews the lease of a secret" } -func (c *RenewCommand) Help() string { +func (c *LeaseRenewCommand) Help() string { helpText := ` -Usage: vault renew [options] ID +Usage: vault lease renew [options] ID Renews the lease on a secret, extending the time that it can be used before it is revoked by Vault. @@ -38,7 +36,7 @@ Usage: vault renew [options] ID Renew a secret: - $ vault renew database/creds/readonly/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 + $ vault lease renew database/creds/readonly/2f6a614c... Lease renewal will fail if the secret is not renewable, the secret has already been revoked, or if the secret has already reached its maximum TTL. @@ -50,7 +48,7 @@ Usage: vault renew [options] ID return strings.TrimSpace(helpText) } -func (c *RenewCommand) Flags() *FlagSets { +func (c *LeaseRenewCommand) Flags() *FlagSets { set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) f := set.NewFlagSet("Command Options") @@ -67,15 +65,15 @@ func (c *RenewCommand) Flags() *FlagSets { return set } -func (c *RenewCommand) AutocompleteArgs() complete.Predictor { +func (c *LeaseRenewCommand) AutocompleteArgs() complete.Predictor { return complete.PredictAnything } -func (c *RenewCommand) AutocompleteFlags() complete.Flags { +func (c *LeaseRenewCommand) AutocompleteFlags() complete.Flags { return c.Flags().Completions() } -func (c *RenewCommand) Run(args []string) int { +func (c *LeaseRenewCommand) Run(args []string) int { f := c.Flags() if err := f.Parse(args); err != nil { diff --git a/command/renew_test.go b/command/lease_renew_test.go similarity index 81% rename from command/renew_test.go rename to command/lease_renew_test.go index 5861cc16f0..166e5156be 100644 --- a/command/renew_test.go +++ b/command/lease_renew_test.go @@ -8,20 +8,20 @@ import ( "github.com/mitchellh/cli" ) -func testRenewCommand(tb testing.TB) (*cli.MockUi, *RenewCommand) { +func testLeaseRenewCommand(tb testing.TB) (*cli.MockUi, *LeaseRenewCommand) { tb.Helper() ui := cli.NewMockUi() - return ui, &RenewCommand{ + return ui, &LeaseRenewCommand{ BaseCommand: &BaseCommand{ UI: ui, }, } } -// testRenewCommandMountAndLease mounts a leased secret backend and returns +// testLeaseRenewCommandMountAndLease mounts a leased secret backend and returns // the leaseID of an item. -func testRenewCommandMountAndLease(tb testing.TB, client *api.Client) string { +func testLeaseRenewCommandMountAndLease(tb testing.TB, client *api.Client) string { if err := client.Sys().Mount("testing", &api.MountInput{ Type: "generic-leased", }); err != nil { @@ -47,7 +47,7 @@ func testRenewCommandMountAndLease(tb testing.TB, client *api.Client) string { return secret.LeaseID } -func TestRenewCommand_Run(t *testing.T) { +func TestLeaseRenewCommand_Run(t *testing.T) { t.Parallel() cases := []struct { @@ -100,9 +100,9 @@ func TestRenewCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - leaseID := testRenewCommandMountAndLease(t, client) + leaseID := testLeaseRenewCommandMountAndLease(t, client) - ui, cmd := testRenewCommand(t) + ui, cmd := testLeaseRenewCommand(t) cmd.client = client if tc.args != nil { @@ -127,9 +127,9 @@ func TestRenewCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - leaseID := testRenewCommandMountAndLease(t, client) + leaseID := testLeaseRenewCommandMountAndLease(t, client) - _, cmd := testRenewCommand(t) + _, cmd := testLeaseRenewCommand(t) cmd.client = client code := cmd.Run([]string{leaseID}) @@ -144,7 +144,7 @@ func TestRenewCommand_Run(t *testing.T) { client, closer := testVaultServerBad(t) defer closer() - ui, cmd := testRenewCommand(t) + ui, cmd := testLeaseRenewCommand(t) cmd.client = client code := cmd.Run([]string{ @@ -164,7 +164,7 @@ func TestRenewCommand_Run(t *testing.T) { t.Run("no_tabs", func(t *testing.T) { t.Parallel() - _, cmd := testRenewCommand(t) + _, cmd := testLeaseRenewCommand(t) assertNoTabs(t, cmd) }) } diff --git a/command/revoke.go b/command/lease_revoke.go similarity index 66% rename from command/revoke.go rename to command/lease_revoke.go index 8b4ba6118c..45f5dc3a76 100644 --- a/command/revoke.go +++ b/command/lease_revoke.go @@ -8,50 +8,48 @@ import ( "github.com/posener/complete" ) -// Ensure we are implementing the right interfaces. -var _ cli.Command = (*ReadCommand)(nil) -var _ cli.CommandAutocomplete = (*ReadCommand)(nil) +var _ cli.Command = (*LeaseRevokeCommand)(nil) +var _ cli.CommandAutocomplete = (*LeaseRevokeCommand)(nil) -// RevokeCommand is a Command that mounts a new mount. -type RevokeCommand struct { +type LeaseRevokeCommand struct { *BaseCommand flagForce bool flagPrefix bool } -func (c *RevokeCommand) Synopsis() string { +func (c *LeaseRevokeCommand) Synopsis() string { return "Revokes leases and secrets" } -func (c *RevokeCommand) Help() string { +func (c *LeaseRevokeCommand) Help() string { helpText := ` -Usage: vault revoke [options] ID +Usage: vault lease revoke [options] ID Revokes secrets by their lease ID. This command can revoke a single secret or multiple secrets based on a path-matched prefix. Revoke a single lease: - $ vault revoke database/creds/readonly/2f6a614c... + $ vault lease revoke database/creds/readonly/2f6a614c... Revoke all leases for a role: - $ vault revoke -prefix aws/creds/deploy + $ vault lease revoke -prefix aws/creds/deploy - Force delete leases from Vault even if backend revocation fails: + Force delete leases from Vault even if secret engine revocation fails: - $ vault revoke -force -prefix consul/creds + $ vault lease revoke -force -prefix consul/creds For a full list of examples and paths, please see the documentation that - corresponds to the secret backend in use. + corresponds to the secret engine in use. ` + c.Flags().Help() return strings.TrimSpace(helpText) } -func (c *RevokeCommand) Flags() *FlagSets { +func (c *LeaseRevokeCommand) Flags() *FlagSets { set := c.flagSet(FlagSetHTTP) f := set.NewFlagSet("Command Options") @@ -60,10 +58,10 @@ func (c *RevokeCommand) Flags() *FlagSets { Aliases: []string{"f"}, Target: &c.flagForce, Default: false, - Usage: "Delete the lease from Vault even if the backend revocation " + + Usage: "Delete the lease from Vault even if the secret engine revocation " + "fails. This is meant for recovery situations where the secret " + - "in the backend was manually removed. If this flag is specified, " + - "-prefix is also required.", + "in the target secret engine was manually removed. If this flag is " + + "specified, -prefix is also required.", }) f.BoolVar(&BoolVar{ @@ -77,15 +75,15 @@ func (c *RevokeCommand) Flags() *FlagSets { return set } -func (c *RevokeCommand) AutocompleteArgs() complete.Predictor { +func (c *LeaseRevokeCommand) AutocompleteArgs() complete.Predictor { return c.PredictVaultFiles() } -func (c *RevokeCommand) AutocompleteFlags() complete.Flags { +func (c *LeaseRevokeCommand) AutocompleteFlags() complete.Flags { return c.Flags().Completions() } -func (c *RevokeCommand) Run(args []string) int { +func (c *LeaseRevokeCommand) Run(args []string) int { f := c.Flags() if err := f.Parse(args); err != nil { @@ -94,13 +92,11 @@ func (c *RevokeCommand) Run(args []string) int { } args = f.Args() - leaseID, remaining, err := extractID(args) - if err != nil { - c.UI.Error(err.Error()) + switch { + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) return 1 - } - - if len(remaining) > 0 { + case len(args) > 1: c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) return 1 } @@ -116,10 +112,12 @@ func (c *RevokeCommand) Run(args []string) int { return 2 } + leaseID := strings.TrimSpace(args[0]) + switch { case c.flagForce && c.flagPrefix: c.UI.Warn(wrapAtLength("Warning! Force-removing leases can cause Vault " + - "to become out of sync with credential backends!")) + "to become out of sync with secret engines!")) if err := client.Sys().RevokeForce(leaseID); err != nil { c.UI.Error(fmt.Sprintf("Error force revoking leases with prefix %s: %s", leaseID, err)) return 2 diff --git a/command/revoke_test.go b/command/lease_revoke_test.go similarity index 88% rename from command/revoke_test.go rename to command/lease_revoke_test.go index 1b2b94fd6e..97904a4d7f 100644 --- a/command/revoke_test.go +++ b/command/lease_revoke_test.go @@ -8,18 +8,18 @@ import ( "github.com/mitchellh/cli" ) -func testRevokeCommand(tb testing.TB) (*cli.MockUi, *RevokeCommand) { +func testLeaseRevokeCommand(tb testing.TB) (*cli.MockUi, *LeaseRevokeCommand) { tb.Helper() ui := cli.NewMockUi() - return ui, &RevokeCommand{ + return ui, &LeaseRevokeCommand{ BaseCommand: &BaseCommand{ UI: ui, }, } } -func TestRevokeCommand_Run(t *testing.T) { +func TestLeaseRevokeCommand_Run(t *testing.T) { t.Parallel() cases := []struct { @@ -85,7 +85,7 @@ func TestRevokeCommand_Run(t *testing.T) { t.Fatal(err) } - ui, cmd := testRevokeCommand(t) + ui, cmd := testLeaseRevokeCommand(t) cmd.client = client tc.args = append(tc.args, secret.LeaseID) @@ -108,7 +108,7 @@ func TestRevokeCommand_Run(t *testing.T) { client, closer := testVaultServerBad(t) defer closer() - ui, cmd := testRevokeCommand(t) + ui, cmd := testLeaseRevokeCommand(t) cmd.client = client code := cmd.Run([]string{ @@ -128,7 +128,7 @@ func TestRevokeCommand_Run(t *testing.T) { t.Run("no_tabs", func(t *testing.T) { t.Parallel() - _, cmd := testRevokeCommand(t) + _, cmd := testLeaseRevokeCommand(t) assertNoTabs(t, cmd) }) }