Add command to look up a lease by ID (#11129)

* snapshot

* basic test

* update command and add documentation

* update help text

* typo

* add changelog for lease lookup command

* run go mod vendor

* remove tabs from help output
This commit is contained in:
Clint 2021-03-18 11:11:09 -05:00 committed by GitHub
parent eb36b68710
commit 5353279e75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 304 additions and 0 deletions

View File

@ -27,6 +27,27 @@ func (c *Sys) Renew(id string, increment int) (*Secret, error) {
return ParseSecret(resp.Body) return ParseSecret(resp.Body)
} }
func (c *Sys) Lookup(id string) (*Secret, error) {
r := c.c.NewRequest("PUT", "/v1/sys/leases/lookup")
body := map[string]interface{}{
"lease_id": id,
}
if err := r.SetJSONBody(body); err != nil {
return nil, err
}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ParseSecret(resp.Body)
}
func (c *Sys) Revoke(id string) error { func (c *Sys) Revoke(id string) error {
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke") r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke")
body := map[string]interface{}{ body := map[string]interface{}{

3
changelog/11129.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
cli/api: Add lease lookup command
```

View File

@ -286,6 +286,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
BaseCommand: getBaseCommand(), BaseCommand: getBaseCommand(),
}, nil }, nil
}, },
"lease lookup": func() (cli.Command, error) {
return &LeaseLookupCommand{
BaseCommand: getBaseCommand(),
}, nil
},
"lease revoke": func() (cli.Command, error) { "lease revoke": func() (cli.Command, error) {
return &LeaseRevokeCommand{ return &LeaseRevokeCommand{
BaseCommand: getBaseCommand(), BaseCommand: getBaseCommand(),

89
command/lease_lookup.go Normal file
View File

@ -0,0 +1,89 @@
package command
import (
"fmt"
"strings"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
var _ cli.Command = (*LeaseLookupCommand)(nil)
var _ cli.CommandAutocomplete = (*LeaseLookupCommand)(nil)
type LeaseLookupCommand struct {
*BaseCommand
}
func (c *LeaseLookupCommand) Synopsis() string {
return "Lookup the lease of a secret"
}
func (c *LeaseLookupCommand) Help() string {
helpText := `
Usage: vault lease lookup ID
Lookup the lease information of a secret.
Every secret in Vault has a lease associated with it. Users can look up
information on the lease by referencing the lease ID.
Lookup lease of a secret:
$ vault lease lookup database/creds/readonly/2f6a614c...
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *LeaseLookupCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
return set
}
func (c *LeaseLookupCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictAnything
}
func (c *LeaseLookupCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}
func (c *LeaseLookupCommand) Run(args []string) int {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
leaseID := ""
args = f.Args()
switch len(args) {
case 0:
c.UI.Error("Missing ID!")
return 1
case 1:
leaseID = strings.TrimSpace(args[0])
default:
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
}
secret, err := client.Sys().Lookup(leaseID)
if err != nil {
c.UI.Error(fmt.Sprintf("error looking up lease id %s: %s", leaseID, err))
return 2
}
return OutputSecret(c.UI, secret)
}

View File

@ -0,0 +1,100 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/cli"
)
func testLeaseLookupCommand(tb testing.TB) (*cli.MockUi, *LeaseLookupCommand) {
tb.Helper()
ui := cli.NewMockUi()
return ui, &LeaseLookupCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
}
}
// testLeaseLookupCommandMountAndLease mounts a leased secret backend and returns
// the leaseID of an item.
func testLeaseLookupCommandMountAndLease(tb testing.TB, client *api.Client) string {
if err := client.Sys().Mount("testing", &api.MountInput{
Type: "generic-leased",
}); err != nil {
tb.Fatal(err)
}
if _, err := client.Logical().Write("testing/foo", map[string]interface{}{
"key": "value",
"lease": "5m",
}); err != nil {
tb.Fatal(err)
}
// Read the secret back to get the leaseID
secret, err := client.Logical().Read("testing/foo")
if err != nil {
tb.Fatal(err)
}
if secret == nil || secret.LeaseID == "" {
tb.Fatalf("missing secret or lease: %#v", secret)
}
return secret.LeaseID
}
// TestLeaseLookupCommand_Run tests basic lookup
func TestLeaseLookupCommand_Run(t *testing.T) {
t.Parallel()
t.Run("empty", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
_ = testLeaseLookupCommandMountAndLease(t, client)
ui, cmd := testLeaseLookupCommand(t)
cmd.client = client
code := cmd.Run(nil)
if exp := 1; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
expectedMsg := "Missing ID!"
if !strings.Contains(combined, expectedMsg) {
t.Errorf("expected %q to contain %q", combined, expectedMsg)
}
})
t.Run("integration", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
leaseID := testLeaseLookupCommandMountAndLease(t, client)
_, cmd := testLeaseLookupCommand(t)
cmd.client = client
code := cmd.Run([]string{leaseID})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
})
t.Run("no_tabs", func(t *testing.T) {
t.Parallel()
_, cmd := testLeaseLookupCommand(t)
assertNoTabs(t, cmd)
})
}

View File

@ -27,6 +27,27 @@ func (c *Sys) Renew(id string, increment int) (*Secret, error) {
return ParseSecret(resp.Body) return ParseSecret(resp.Body)
} }
func (c *Sys) Lookup(id string) (*Secret, error) {
r := c.c.NewRequest("PUT", "/v1/sys/leases/lookup")
body := map[string]interface{}{
"lease_id": id,
}
if err := r.SetJSONBody(body); err != nil {
return nil, err
}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ParseSecret(resp.Body)
}
func (c *Sys) Revoke(id string) error { func (c *Sys) Revoke(id string) error {
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke") r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke")
body := map[string]interface{}{ body := map[string]interface{}{

View File

@ -14,6 +14,20 @@ secrets. For leases attached to tokens, use the [`vault token`](/docs/commands/t
## Examples ## Examples
Lookup a lease:
```shell-session
$ vault lease lookup database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83
Key Value
--- -----
expire_time 2021-03-17T11:55:50.755313-05:00
id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83
issue_time 2021-03-17T11:45:50.755312-05:00
last_renewal <nil>
renewable true
ttl 9m52s
```
Renew a lease: Renew a lease:
```shell-session ```shell-session
@ -40,6 +54,7 @@ Usage: vault lease <subcommand> [options] [args]
# ... # ...
Subcommands: Subcommands:
lookup Lookup lease information by lease id
renew Renews the lease of a secret renew Renews the lease of a secret
revoke Revokes leases and secrets revoke Revokes leases and secrets
``` ```

View File

@ -14,6 +14,20 @@ secrets. For leases attached to tokens, use the [`vault token`](/docs/commands/t
## Examples ## Examples
Lookup a lease:
```shell-session
$ vault lease lookup database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83
Key Value
--- -----
expire_time 2021-03-17T11:55:50.755313-05:00
id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83
issue_time 2021-03-17T11:45:50.755312-05:00
last_renewal <nil>
renewable true
ttl 9m52s
```
Renew a lease: Renew a lease:
```shell-session ```shell-session
@ -40,6 +54,7 @@ Usage: vault lease <subcommand> [options] [args]
# ... # ...
Subcommands: Subcommands:
lookup Lookup lease information by lease id
renew Renews the lease of a secret renew Renews the lease of a secret
revoke Revokes leases and secrets revoke Revokes leases and secrets
``` ```

View File

@ -0,0 +1,35 @@
---
layout: docs
page_title: lease lookup - Command
sidebar_title: <code>lookup</code>
description: |-
The "lease lookup" command retrieves information about a lease.
---
# lease lookup
The `lease lookup` command retrieves information on the lease of a secret.
Every secret in Vault has a lease associated with it. Users can look up
information on the lease by referencing the lease ID.
## Examples
Lookup a lease:
```shell-session
$ vault lease lookup database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83
Key Value
--- -----
expire_time 2021-03-17T11:55:50.755313-05:00
id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83
issue_time 2021-03-17T11:45:50.755312-05:00
last_renewal <nil>
renewable true
ttl 9m52s
```
## Usage
There are no flags beyond the [standard set of flags](/docs/commands)
included on all commands.