Update mount table and CLI with plugin version for auth (#16856)

This commit is contained in:
Christopher Swenson
2022-08-31 11:23:05 -07:00
committed by GitHub
parent 5e44064931
commit 9d97decb26
32 changed files with 1479 additions and 328 deletions

View File

@@ -37,6 +37,7 @@ type AuthEnableCommand struct {
flagExternalEntropyAccess bool
flagTokenType string
flagVersion int
flagPluginVersion string
}
func (c *AuthEnableCommand) Synopsis() string {
@@ -199,6 +200,13 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Usage: "Select the version of the auth method to run. Not supported by all auth methods.",
})
f.StringVar(&StringVar{
Name: "plugin-version",
Target: &c.flagPluginVersion,
Default: "",
Usage: "Select the version of the plugin to enable.",
})
return set
}
@@ -262,6 +270,7 @@ func (c *AuthEnableCommand) Run(args []string) int {
authOpts := &api.EnableAuthOptions{
Type: authType,
Version: c.flagPluginVersion,
Description: c.flagDescription,
Local: c.flagLocal,
SealWrap: c.flagSealWrap,

View File

@@ -118,10 +118,10 @@ func (c *AuthListCommand) simpleMounts(auths map[string]*api.AuthMount) []string
}
sort.Strings(paths)
out := []string{"Path | Type | Accessor | Description"}
out := []string{"Path | Type | Accessor | Description | Version"}
for _, path := range paths {
mount := auths[path]
out = append(out, fmt.Sprintf("%s | %s | %s | %s", path, mount.Type, mount.Accessor, mount.Description))
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s", path, mount.Type, mount.Accessor, mount.Description, mount.Version))
}
return out
@@ -145,7 +145,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
}
}
out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID"}
out := []string{"Path | Plugin | Accessor | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | External Entropy Access | Options | Description | UUID | Version"}
for _, path := range paths {
mount := auths[path]
@@ -162,7 +162,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
pluginName = mount.Config.PluginName
}
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s | %s | %s",
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s | %s | %s | %s",
path,
pluginName,
mount.Accessor,
@@ -175,6 +175,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
mount.Options,
mount.Description,
mount.UUID,
mount.Version,
))
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
semver "github.com/hashicorp/go-version"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/mitchellh/cli"
@@ -17,6 +18,8 @@ var (
type PluginDeregisterCommand struct {
*BaseCommand
flagVersion string
}
func (c *PluginDeregisterCommand) Synopsis() string {
@@ -28,20 +31,36 @@ func (c *PluginDeregisterCommand) Help() string {
Usage: vault plugin deregister [options] TYPE NAME
Deregister an existing plugin in the catalog. If the plugin does not exist,
no action is taken (the command is idempotent). The argument of type
no action is taken (the command is idempotent). The TYPE argument
takes "auth", "database", or "secret".
Deregister the plugin named my-custom-plugin:
Deregister the unversioned auth plugin named my-custom-plugin:
$ vault plugin deregister auth my-custom-plugin
Deregister the auth plugin named my-custom-plugin, version 1.0.0:
$ vault plugin deregister -version=v1.0.0 auth my-custom-plugin
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *PluginDeregisterCommand) Flags() *FlagSets {
return c.flagSet(FlagSetHTTP)
set := c.flagSet(FlagSetHTTP)
f := set.NewFlagSet("Command Options")
f.StringVar(&StringVar{
Name: "version",
Target: &c.flagVersion,
Completion: complete.PredictAnything,
Usage: "Version of the plugin to deregister. If unset, " +
"only an unversioned plugin may be deregistered.",
})
return set
}
func (c *PluginDeregisterCommand) AutocompleteArgs() complete.Predictor {
@@ -62,21 +81,19 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
var pluginNameRaw, pluginTypeRaw string
args = f.Args()
switch {
case len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1 or 2, got %d)", len(args)))
switch len(args) {
case 0:
c.UI.Error("Not enough arguments (expected 1, or 2, got 0)")
return 1
case len(args) > 2:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args)))
return 1
// These cases should come after invalid cases have been checked
case len(args) == 1:
case 1:
pluginTypeRaw = "unknown"
pluginNameRaw = args[0]
case len(args) == 2:
case 2:
pluginTypeRaw = args[0]
pluginNameRaw = args[1]
default:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, or 2, got %d)", len(args)))
return 1
}
client, err := c.Client()
@@ -91,10 +108,18 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
return 2
}
pluginName := strings.TrimSpace(pluginNameRaw)
if c.flagVersion != "" {
_, err := semver.NewSemver(c.flagVersion)
if err != nil {
c.UI.Error(fmt.Sprintf("version %q is not a valid semantic version: %v", c.flagVersion, err))
return 2
}
}
if err := client.Sys().DeregisterPlugin(&api.DeregisterPluginInput{
Name: pluginName,
Type: pluginType,
Name: pluginName,
Type: pluginType,
Version: c.flagVersion,
}); err != nil {
c.UI.Error(fmt.Sprintf("Error deregistering plugin named %s: %s", pluginName, err))
return 2

View File

@@ -6,6 +6,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@@ -76,7 +77,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
t.Run("integration", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
@@ -131,6 +132,101 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
}
})
t.Run("integration with version", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
defer closer()
pluginName := "my-plugin"
_, _, version := testPluginCreateAndRegisterVersioned(t, client, pluginDir, pluginName, consts.PluginTypeCredential)
ui, cmd := testPluginDeregisterCommand(t)
cmd.client = client
code := cmd.Run([]string{
"-version=" + version,
consts.PluginTypeCredential.String(),
pluginName,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Deregistered plugin (if it was registered): "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{
Type: consts.PluginTypeUnknown,
})
if err != nil {
t.Fatal(err)
}
found := false
for _, p := range resp.Details {
if p.Name == pluginName {
found = true
}
}
if found {
t.Errorf("expected %q to not be in %#v", pluginName, resp.Details)
}
})
t.Run("integration with missing version", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
defer closer()
pluginName := "my-plugin"
testPluginCreateAndRegisterVersioned(t, client, pluginDir, pluginName, consts.PluginTypeCredential)
ui, cmd := testPluginDeregisterCommand(t)
cmd.client = client
code := cmd.Run([]string{
consts.PluginTypeCredential.String(),
pluginName,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Deregistered plugin (if it was registered): "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{
Type: consts.PluginTypeUnknown,
})
if err != nil {
t.Fatal(err)
}
found := false
for _, p := range resp.Details {
if p.Name == pluginName {
found = true
}
}
if !found {
t.Errorf("expected %q to be in %#v", pluginName, resp.Details)
}
})
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()

View File

@@ -5,6 +5,7 @@ import (
"testing"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@@ -73,7 +74,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
t.Run("default", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)
@@ -104,7 +105,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
t.Run("field", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)

View File

@@ -21,6 +21,7 @@ type PluginRegisterCommand struct {
flagArgs []string
flagCommand string
flagSHA256 string
flagVersion string
}
func (c *PluginRegisterCommand) Synopsis() string {
@@ -37,12 +38,13 @@ Usage: vault plugin register [options] TYPE NAME
Register the plugin named my-custom-plugin:
$ vault plugin register -sha256=d3f0a8b... auth my-custom-plugin
$ vault plugin register -sha256=d3f0a8b... -version=v1.0.0 auth my-custom-plugin
Register a plugin with custom arguments:
$ vault plugin register \
-sha256=d3f0a8b... \
-version=v1.0.0 \
-args=--with-glibc,--with-cgo \
auth my-custom-plugin
@@ -79,6 +81,13 @@ func (c *PluginRegisterCommand) Flags() *FlagSets {
Usage: "SHA256 of the plugin binary. This is required for all plugins.",
})
f.StringVar(&StringVar{
Name: "version",
Target: &c.flagVersion,
Completion: complete.PredictAnything,
Usage: "Version of the plugin. Optional.",
})
return set
}
@@ -144,6 +153,7 @@ func (c *PluginRegisterCommand) Run(args []string) int {
Args: c.flagArgs,
Command: command,
SHA256: c.flagSHA256,
Version: c.flagVersion,
}); err != nil {
c.UI.Error(fmt.Sprintf("Error registering plugin %s: %s", pluginName, err))
return 2

View File

@@ -6,6 +6,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@@ -77,7 +78,7 @@ func TestPluginRegisterCommand_Run(t *testing.T) {
t.Run("integration", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)

View File

@@ -6,6 +6,7 @@ import (
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
@@ -82,7 +83,7 @@ func TestPluginReloadCommand_Run(t *testing.T) {
t.Run("integration", func(t *testing.T) {
t.Parallel()
pluginDir, cleanup := testPluginDir(t)
pluginDir, cleanup := vault.MakeTestPluginDir(t)
defer cleanup(t)
client, _, closer := testVaultServerPluginDir(t, pluginDir)

View File

@@ -6,36 +6,12 @@ import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/consts"
)
// testPluginDir creates a temporary directory suitable for holding plugins.
// This helper also resolves symlinks to make tests happy on OS X.
func testPluginDir(tb testing.TB) (string, func(tb testing.TB)) {
tb.Helper()
dir, err := ioutil.TempDir("", "")
if err != nil {
tb.Fatal(err)
}
// OSX tempdir are /var, but actually symlinked to /private/var
dir, err = filepath.EvalSymlinks(dir)
if err != nil {
tb.Fatal(err)
}
return dir, func(tb testing.TB) {
if err := os.RemoveAll(dir); err != nil {
tb.Fatal(err)
}
}
}
// testPluginCreate creates a sample plugin in a tempdir and returns the shasum
// and filepath to the plugin.
func testPluginCreate(tb testing.TB, dir, name string) (string, string) {
@@ -78,3 +54,22 @@ func testPluginCreateAndRegister(tb testing.TB, client *api.Client, dir, name st
return pth, sha256Sum
}
// testPluginCreateAndRegisterVersioned creates a versioned plugin and registers it in the catalog.
func testPluginCreateAndRegisterVersioned(tb testing.TB, client *api.Client, dir, name string, pluginType consts.PluginType) (string, string, string) {
tb.Helper()
pth, sha256Sum := testPluginCreate(tb, dir, name)
if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{
Name: name,
Type: pluginType,
Command: name,
SHA256: sha256Sum,
Version: "v1.0.0",
}); err != nil {
tb.Fatal(err)
}
return pth, sha256Sum, "v1.0.0"
}