mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-28 14:11:10 +01:00
Add lease subcommand
This commit is contained in:
parent
6b75e6e2bf
commit
9a80d9a8f8
40
command/lease.go
Normal file
40
command/lease.go
Normal 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
|
||||||
|
}
|
||||||
@ -9,24 +9,22 @@ import (
|
|||||||
"github.com/posener/complete"
|
"github.com/posener/complete"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure we are implementing the right interfaces.
|
var _ cli.Command = (*LeaseRenewCommand)(nil)
|
||||||
var _ cli.Command = (*RenewCommand)(nil)
|
var _ cli.CommandAutocomplete = (*LeaseRenewCommand)(nil)
|
||||||
var _ cli.CommandAutocomplete = (*RenewCommand)(nil)
|
|
||||||
|
|
||||||
// RenewCommand is a Command that mounts a new mount.
|
type LeaseRenewCommand struct {
|
||||||
type RenewCommand struct {
|
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
|
|
||||||
flagIncrement time.Duration
|
flagIncrement time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenewCommand) Synopsis() string {
|
func (c *LeaseRenewCommand) Synopsis() string {
|
||||||
return "Renews the lease of a secret"
|
return "Renews the lease of a secret"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenewCommand) Help() string {
|
func (c *LeaseRenewCommand) Help() string {
|
||||||
helpText := `
|
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
|
Renews the lease on a secret, extending the time that it can be used before
|
||||||
it is revoked by Vault.
|
it is revoked by Vault.
|
||||||
@ -38,7 +36,7 @@ Usage: vault renew [options] ID
|
|||||||
|
|
||||||
Renew a secret:
|
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
|
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.
|
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)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenewCommand) Flags() *FlagSets {
|
func (c *LeaseRenewCommand) Flags() *FlagSets {
|
||||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||||
f := set.NewFlagSet("Command Options")
|
f := set.NewFlagSet("Command Options")
|
||||||
|
|
||||||
@ -67,15 +65,15 @@ func (c *RenewCommand) Flags() *FlagSets {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenewCommand) AutocompleteArgs() complete.Predictor {
|
func (c *LeaseRenewCommand) AutocompleteArgs() complete.Predictor {
|
||||||
return complete.PredictAnything
|
return complete.PredictAnything
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenewCommand) AutocompleteFlags() complete.Flags {
|
func (c *LeaseRenewCommand) AutocompleteFlags() complete.Flags {
|
||||||
return c.Flags().Completions()
|
return c.Flags().Completions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenewCommand) Run(args []string) int {
|
func (c *LeaseRenewCommand) Run(args []string) int {
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
|
|
||||||
if err := f.Parse(args); err != nil {
|
if err := f.Parse(args); err != nil {
|
||||||
@ -8,20 +8,20 @@ import (
|
|||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRenewCommand(tb testing.TB) (*cli.MockUi, *RenewCommand) {
|
func testLeaseRenewCommand(tb testing.TB) (*cli.MockUi, *LeaseRenewCommand) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
ui := cli.NewMockUi()
|
ui := cli.NewMockUi()
|
||||||
return ui, &RenewCommand{
|
return ui, &LeaseRenewCommand{
|
||||||
BaseCommand: &BaseCommand{
|
BaseCommand: &BaseCommand{
|
||||||
UI: ui,
|
UI: ui,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testRenewCommandMountAndLease mounts a leased secret backend and returns
|
// testLeaseRenewCommandMountAndLease mounts a leased secret backend and returns
|
||||||
// the leaseID of an item.
|
// 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{
|
if err := client.Sys().Mount("testing", &api.MountInput{
|
||||||
Type: "generic-leased",
|
Type: "generic-leased",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -47,7 +47,7 @@ func testRenewCommandMountAndLease(tb testing.TB, client *api.Client) string {
|
|||||||
return secret.LeaseID
|
return secret.LeaseID
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenewCommand_Run(t *testing.T) {
|
func TestLeaseRenewCommand_Run(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@ -100,9 +100,9 @@ func TestRenewCommand_Run(t *testing.T) {
|
|||||||
client, closer := testVaultServer(t)
|
client, closer := testVaultServer(t)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
leaseID := testRenewCommandMountAndLease(t, client)
|
leaseID := testLeaseRenewCommandMountAndLease(t, client)
|
||||||
|
|
||||||
ui, cmd := testRenewCommand(t)
|
ui, cmd := testLeaseRenewCommand(t)
|
||||||
cmd.client = client
|
cmd.client = client
|
||||||
|
|
||||||
if tc.args != nil {
|
if tc.args != nil {
|
||||||
@ -127,9 +127,9 @@ func TestRenewCommand_Run(t *testing.T) {
|
|||||||
client, closer := testVaultServer(t)
|
client, closer := testVaultServer(t)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
leaseID := testRenewCommandMountAndLease(t, client)
|
leaseID := testLeaseRenewCommandMountAndLease(t, client)
|
||||||
|
|
||||||
_, cmd := testRenewCommand(t)
|
_, cmd := testLeaseRenewCommand(t)
|
||||||
cmd.client = client
|
cmd.client = client
|
||||||
|
|
||||||
code := cmd.Run([]string{leaseID})
|
code := cmd.Run([]string{leaseID})
|
||||||
@ -144,7 +144,7 @@ func TestRenewCommand_Run(t *testing.T) {
|
|||||||
client, closer := testVaultServerBad(t)
|
client, closer := testVaultServerBad(t)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
ui, cmd := testRenewCommand(t)
|
ui, cmd := testLeaseRenewCommand(t)
|
||||||
cmd.client = client
|
cmd.client = client
|
||||||
|
|
||||||
code := cmd.Run([]string{
|
code := cmd.Run([]string{
|
||||||
@ -164,7 +164,7 @@ func TestRenewCommand_Run(t *testing.T) {
|
|||||||
t.Run("no_tabs", func(t *testing.T) {
|
t.Run("no_tabs", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, cmd := testRenewCommand(t)
|
_, cmd := testLeaseRenewCommand(t)
|
||||||
assertNoTabs(t, cmd)
|
assertNoTabs(t, cmd)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -8,50 +8,48 @@ import (
|
|||||||
"github.com/posener/complete"
|
"github.com/posener/complete"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure we are implementing the right interfaces.
|
var _ cli.Command = (*LeaseRevokeCommand)(nil)
|
||||||
var _ cli.Command = (*ReadCommand)(nil)
|
var _ cli.CommandAutocomplete = (*LeaseRevokeCommand)(nil)
|
||||||
var _ cli.CommandAutocomplete = (*ReadCommand)(nil)
|
|
||||||
|
|
||||||
// RevokeCommand is a Command that mounts a new mount.
|
type LeaseRevokeCommand struct {
|
||||||
type RevokeCommand struct {
|
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
|
|
||||||
flagForce bool
|
flagForce bool
|
||||||
flagPrefix bool
|
flagPrefix bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RevokeCommand) Synopsis() string {
|
func (c *LeaseRevokeCommand) Synopsis() string {
|
||||||
return "Revokes leases and secrets"
|
return "Revokes leases and secrets"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RevokeCommand) Help() string {
|
func (c *LeaseRevokeCommand) Help() string {
|
||||||
helpText := `
|
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
|
Revokes secrets by their lease ID. This command can revoke a single secret
|
||||||
or multiple secrets based on a path-matched prefix.
|
or multiple secrets based on a path-matched prefix.
|
||||||
|
|
||||||
Revoke a single lease:
|
Revoke a single lease:
|
||||||
|
|
||||||
$ vault revoke database/creds/readonly/2f6a614c...
|
$ vault lease revoke database/creds/readonly/2f6a614c...
|
||||||
|
|
||||||
Revoke all leases for a role:
|
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
|
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()
|
` + c.Flags().Help()
|
||||||
|
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RevokeCommand) Flags() *FlagSets {
|
func (c *LeaseRevokeCommand) Flags() *FlagSets {
|
||||||
set := c.flagSet(FlagSetHTTP)
|
set := c.flagSet(FlagSetHTTP)
|
||||||
f := set.NewFlagSet("Command Options")
|
f := set.NewFlagSet("Command Options")
|
||||||
|
|
||||||
@ -60,10 +58,10 @@ func (c *RevokeCommand) Flags() *FlagSets {
|
|||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
Target: &c.flagForce,
|
Target: &c.flagForce,
|
||||||
Default: false,
|
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 " +
|
"fails. This is meant for recovery situations where the secret " +
|
||||||
"in the backend was manually removed. If this flag is specified, " +
|
"in the target secret engine was manually removed. If this flag is " +
|
||||||
"-prefix is also required.",
|
"specified, -prefix is also required.",
|
||||||
})
|
})
|
||||||
|
|
||||||
f.BoolVar(&BoolVar{
|
f.BoolVar(&BoolVar{
|
||||||
@ -77,15 +75,15 @@ func (c *RevokeCommand) Flags() *FlagSets {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RevokeCommand) AutocompleteArgs() complete.Predictor {
|
func (c *LeaseRevokeCommand) AutocompleteArgs() complete.Predictor {
|
||||||
return c.PredictVaultFiles()
|
return c.PredictVaultFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RevokeCommand) AutocompleteFlags() complete.Flags {
|
func (c *LeaseRevokeCommand) AutocompleteFlags() complete.Flags {
|
||||||
return c.Flags().Completions()
|
return c.Flags().Completions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RevokeCommand) Run(args []string) int {
|
func (c *LeaseRevokeCommand) Run(args []string) int {
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
|
|
||||||
if err := f.Parse(args); err != nil {
|
if err := f.Parse(args); err != nil {
|
||||||
@ -94,13 +92,11 @@ func (c *RevokeCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
args = f.Args()
|
args = f.Args()
|
||||||
leaseID, remaining, err := extractID(args)
|
switch {
|
||||||
if err != nil {
|
case len(args) < 1:
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args)))
|
||||||
return 1
|
return 1
|
||||||
}
|
case len(args) > 1:
|
||||||
|
|
||||||
if len(remaining) > 0 {
|
|
||||||
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args)))
|
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args)))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ -116,10 +112,12 @@ func (c *RevokeCommand) Run(args []string) int {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leaseID := strings.TrimSpace(args[0])
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case c.flagForce && c.flagPrefix:
|
case c.flagForce && c.flagPrefix:
|
||||||
c.UI.Warn(wrapAtLength("Warning! Force-removing leases can cause Vault " +
|
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 {
|
if err := client.Sys().RevokeForce(leaseID); err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error force revoking leases with prefix %s: %s", leaseID, err))
|
c.UI.Error(fmt.Sprintf("Error force revoking leases with prefix %s: %s", leaseID, err))
|
||||||
return 2
|
return 2
|
||||||
@ -8,18 +8,18 @@ import (
|
|||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRevokeCommand(tb testing.TB) (*cli.MockUi, *RevokeCommand) {
|
func testLeaseRevokeCommand(tb testing.TB) (*cli.MockUi, *LeaseRevokeCommand) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
ui := cli.NewMockUi()
|
ui := cli.NewMockUi()
|
||||||
return ui, &RevokeCommand{
|
return ui, &LeaseRevokeCommand{
|
||||||
BaseCommand: &BaseCommand{
|
BaseCommand: &BaseCommand{
|
||||||
UI: ui,
|
UI: ui,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRevokeCommand_Run(t *testing.T) {
|
func TestLeaseRevokeCommand_Run(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@ -85,7 +85,7 @@ func TestRevokeCommand_Run(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui, cmd := testRevokeCommand(t)
|
ui, cmd := testLeaseRevokeCommand(t)
|
||||||
cmd.client = client
|
cmd.client = client
|
||||||
|
|
||||||
tc.args = append(tc.args, secret.LeaseID)
|
tc.args = append(tc.args, secret.LeaseID)
|
||||||
@ -108,7 +108,7 @@ func TestRevokeCommand_Run(t *testing.T) {
|
|||||||
client, closer := testVaultServerBad(t)
|
client, closer := testVaultServerBad(t)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
ui, cmd := testRevokeCommand(t)
|
ui, cmd := testLeaseRevokeCommand(t)
|
||||||
cmd.client = client
|
cmd.client = client
|
||||||
|
|
||||||
code := cmd.Run([]string{
|
code := cmd.Run([]string{
|
||||||
@ -128,7 +128,7 @@ func TestRevokeCommand_Run(t *testing.T) {
|
|||||||
t.Run("no_tabs", func(t *testing.T) {
|
t.Run("no_tabs", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, cmd := testRevokeCommand(t)
|
_, cmd := testLeaseRevokeCommand(t)
|
||||||
assertNoTabs(t, cmd)
|
assertNoTabs(t, cmd)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user