Add lease subcommand

This commit is contained in:
Seth Vargo 2017-09-07 21:59:31 -04:00
parent 02341c3b6a
commit 76de999b34
No known key found for this signature in database
GPG Key ID: C921994F9C27E0FF
5 changed files with 93 additions and 57 deletions

40
command/lease.go Normal file
View File

@ -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 <subcommand> [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
}

View File

@ -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 {

View File

@ -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)
})
}

View File

@ -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

View File

@ -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)
})
}