Skip to content

Commit

Permalink
Add version to plugin deregister; add details to plugin list
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher Swenson committed Aug 26, 2022
1 parent ab498b7 commit f699be4
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 19 deletions.
21 changes: 20 additions & 1 deletion api/sys_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,22 @@ type ListPluginsResponse struct {
// PluginsByType is the list of plugins by type.
PluginsByType map[consts.PluginType][]string `json:"types"`

Details []PluginDetails `json:"details,omitempty"`

// Names is the list of names of the plugins.
//
// Deprecated: Newer server responses should be returning PluginsByType (json:
// "types") instead.
Names []string `json:"names"`
}

type PluginDetails struct {
Type string `json:"string"`
Name string `json:"name"`
Version string `json:"version,omitempty"`
Builtin bool `json:"builtin"`
}

// ListPlugins wraps ListPluginsWithContext using context.Background.
func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
return c.ListPluginsWithContext(context.Background(), i)
Expand Down Expand Up @@ -98,6 +107,7 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (

result := &ListPluginsResponse{
PluginsByType: make(map[consts.PluginType][]string),
Details: []PluginDetails{},
}
if i.Type == consts.PluginTypeUnknown {
for _, pluginType := range consts.PluginTypes {
Expand Down Expand Up @@ -129,6 +139,12 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) (
result.PluginsByType[i.Type] = respKeys
}

if detailed, ok := secret.Data["detailed"]; ok {
if err := mapstructure.Decode(detailed, &result.Details); err != nil {
return nil, err
}
}

return result, nil
}

Expand Down Expand Up @@ -230,6 +246,9 @@ type DeregisterPluginInput struct {

// Type of the plugin. Required.
Type consts.PluginType `json:"type"`

// Version of the plugin. Optional.
Version string `json:"version,omitempty"`
}

// DeregisterPlugin wraps DeregisterPluginWithContext using context.Background.
Expand All @@ -245,7 +264,7 @@ func (c *Sys) DeregisterPluginWithContext(ctx context.Context, i *DeregisterPlug

path := catalogPathByType(i.Type, i.Name)
req := c.c.NewRequest(http.MethodDelete, path)

req.Params.Set("version", i.Version)
resp, err := c.c.rawRequestWithContext(ctx, req)
if err == nil {
defer resp.Body.Close()
Expand Down
32 changes: 25 additions & 7 deletions command/plugin_deregister.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -33,7 +34,7 @@ Usage: vault plugin deregister [options] TYPE NAME
Deregister the plugin named my-custom-plugin:
$ vault plugin deregister auth my-custom-plugin
$ vault plugin deregister auth my-custom-plugin [version]
` + c.Flags().Help()

Expand All @@ -60,14 +61,14 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
return 1
}

var pluginNameRaw, pluginTypeRaw string
var pluginNameRaw, pluginTypeRaw, pluginVersionRaw 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)))
c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, 2, or 3, got %d)", len(args)))
return 1
case len(args) > 2:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args)))
case len(args) > 3:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, 2, or 3, got %d)", len(args)))
return 1

// These cases should come after invalid cases have been checked
Expand All @@ -77,6 +78,10 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
case len(args) == 2:
pluginTypeRaw = args[0]
pluginNameRaw = args[1]
case len(args) == 3:
pluginTypeRaw = args[0]
pluginNameRaw = args[1]
pluginVersionRaw = args[2]
}

client, err := c.Client()
Expand All @@ -91,10 +96,23 @@ func (c *PluginDeregisterCommand) Run(args []string) int {
return 2
}
pluginName := strings.TrimSpace(pluginNameRaw)
pluginVersion := strings.TrimSpace(pluginVersionRaw)
if pluginVersion != "" {
semanticVersion, err := semver.NewSemver(pluginVersion)
if err != nil {
c.UI.Error(fmt.Sprintf("version %q is not a valid semantic version: %v", pluginVersionRaw, err))
return 2
}

// Canonicalize the version string.
// Add the 'v' back in, since semantic version strips it out, and we want to be consistent with internal plugins.
pluginVersion = "v" + semanticVersion.String()
}

if err := client.Sys().DeregisterPlugin(&api.DeregisterPluginInput{
Name: pluginName,
Type: pluginType,
Name: pluginName,
Type: pluginType,
Version: pluginVersion,
}); err != nil {
c.UI.Error(fmt.Sprintf("Error deregistering plugin named %s: %s", pluginName, err))
return 2
Expand Down
64 changes: 55 additions & 9 deletions command/plugin_deregister_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
},
{
"too_many_args",
[]string{"foo", "bar", "fizz"},
[]string{"foo", "bar", "fizz", "fuzz"},
"Too many arguments",
1,
},
Expand Down Expand Up @@ -142,14 +142,15 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
defer closer()

pluginName := "my-plugin"
testPluginCreateAndRegisterVersioned(t, client, pluginDir, pluginName, consts.PluginTypeCredential)
_, _, version := testPluginCreateAndRegisterVersioned(t, client, pluginDir, pluginName, consts.PluginTypeCredential)

ui, cmd := testPluginDeregisterCommand(t)
cmd.client = client

code := cmd.Run([]string{
consts.PluginTypeCredential.String(),
pluginName,
version,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
Expand All @@ -162,22 +163,67 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
}

resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{
Type: consts.PluginTypeCredential,
Type: consts.PluginTypeUnknown,
})
if err != nil {
t.Fatal(err)
}

found := false
for _, plugins := range resp.PluginsByType {
for _, p := range plugins {
if p == pluginName {
found = true
}
for _, p := range resp.Details {
if p.Name == pluginName {
found = true
}
}
if found {
t.Errorf("expected %q to not be in %q", pluginName, resp.PluginsByType)
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)
}
})

Expand Down
4 changes: 2 additions & 2 deletions sdk/logical/system_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ type SystemView interface {

// LookupPlugin looks into the plugin catalog for a plugin with the given
// name. Returns a PluginRunner or an error if a plugin can not be found.
LookupPlugin(context.Context, string, consts.PluginType) (*pluginutil.PluginRunner, error)
LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*pluginutil.PluginRunner, error)

// LookupPluginVersion looks into the plugin catalog for a plugin with the given
// name and version. Returns a PluginRunner or an error if a plugin can not be found.
LookupPluginVersion(context.Context, string, consts.PluginType, string) (*pluginutil.PluginRunner, error)
LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*pluginutil.PluginRunner, error)

// NewPluginClient returns a client for managing the lifecycle of plugin
// processes
Expand Down

0 comments on commit f699be4

Please sign in to comment.