Add API functions and completions for plugins (#4194)

This commit is contained in:
Seth Vargo 2018-03-27 01:40:33 +08:00 committed by Jeff Mitchell
parent 65d8eb0914
commit b665909b09
5 changed files with 201 additions and 2 deletions

117
api/sys_plugins.go Normal file
View File

@ -0,0 +1,117 @@
package api
import (
"fmt"
"net/http"
)
// ListPluginsInput is used as input to the ListPlugins function.
type ListPluginsInput struct{}
// ListPluginsResponse is the response from the ListPlugins call.
type ListPluginsResponse struct {
// Names is the list of names of the plugins.
Names []string
}
// ListPlugins lists all plugins in the catalog and returns their names as a
// list of strings.
func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
path := "/v1/sys/plugins/catalog"
req := c.c.NewRequest("LIST", path)
resp, err := c.c.RawRequest(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result struct {
Data struct {
Keys []string `json:"keys"`
} `json:"data"`
}
if err := resp.DecodeJSON(&result); err != nil {
return nil, err
}
return &ListPluginsResponse{Names: result.Data.Keys}, nil
}
// GetPluginInput is used as input to the GetPlugin function.
type GetPluginInput struct {
Name string `json:"-"`
}
// GetPluginResponse is the response from the GetPlugin call.
type GetPluginResponse struct {
Args []string `json:"args"`
Builtin bool `json:"builtin"`
Command string `json:"command"`
Name string `json:"name"`
SHA256 string `json:"sha256"`
}
func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name)
req := c.c.NewRequest(http.MethodGet, path)
resp, err := c.c.RawRequest(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result GetPluginResponse
err = resp.DecodeJSON(&result)
if err != nil {
return nil, err
}
return &result, err
}
// RegisterPluginInput is used as input to the RegisterPlugin function.
type RegisterPluginInput struct {
// Name is the name of the plugin. Required.
Name string `json:"-"`
// Args is the list of args to spawn the process with.
Args []string `json:"args,omitempty"`
// Command is the command to run.
Command string `json:"command,omitempty"`
// SHA256 is the shasum of the plugin.
SHA256 string `json:"sha256,omitempty"`
}
// RegisterPlugin registers the plugin with the given information.
func (c *Sys) RegisterPlugin(i *RegisterPluginInput) error {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name)
req := c.c.NewRequest(http.MethodPut, path)
if err := req.SetJSONBody(i); err != nil {
return err
}
resp, err := c.c.RawRequest(req)
if err == nil {
defer resp.Body.Close()
}
return err
}
// DeregisterPluginInput is used as input to the DeregisterPlugin function.
type DeregisterPluginInput struct {
// Name is the name of the plugin. Required.
Name string `json:"-"`
}
// DeregisterPlugin removes the plugin with the given name from the plugin
// catalog.
func (c *Sys) DeregisterPlugin(i *DeregisterPluginInput) error {
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name)
req := c.c.NewRequest(http.MethodDelete, path)
resp, err := c.c.RawRequest(req)
if err == nil {
defer resp.Body.Close()
}
return err
}

View File

@ -123,7 +123,7 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
f.StringVar(&StringVar{
Name: "plugin-name",
Target: &c.flagPluginName,
Completion: complete.PredictAnything,
Completion: c.PredictVaultPlugins(),
Usage: "Name of the auth method plugin. This plugin name must already " +
"exist in the Vault server's plugin catalog.",
})

View File

@ -139,6 +139,11 @@ func (b *BaseCommand) PredictVaultAuths() complete.Predictor {
return NewPredict().VaultAuths()
}
// PredictVaultPlugins returns a predictor for installed plugins.
func (b *BaseCommand) PredictVaultPlugins() complete.Predictor {
return NewPredict().VaultPlugins()
}
// PredictVaultPolicies returns a predictor for "folders". See PredictVaultFiles
// for more information and restrictions.
func (b *BaseCommand) PredictVaultPolicies() complete.Predictor {
@ -177,6 +182,13 @@ func (p *Predict) VaultAuths() complete.Predictor {
return p.filterFunc(p.auths)
}
// VaultPlugins returns a predictor for Vault's plugin catalog. This is a public
// API for consumers, but you probably want BaseCommand.PredictVaultPlugins
// instead.
func (p *Predict) VaultPlugins() complete.Predictor {
return p.filterFunc(p.plugins)
}
// VaultPolicies returns a predictor for Vault "folders". This is a public API for
// consumers, but you probably want BaseCommand.PredictVaultPolicies instead.
func (p *Predict) VaultPolicies() complete.Predictor {
@ -310,6 +322,22 @@ func (p *Predict) auths() []string {
return list
}
// plugins returns a sorted list of the plugins in the catalog.
func (p *Predict) plugins() []string {
client := p.Client()
if client == nil {
return nil
}
result, err := client.Sys().ListPlugins(nil)
if err != nil {
return nil
}
plugins := result.Names
sort.Strings(plugins)
return plugins
}
// policies returns a sorted list of the policies stored in this Vault
// server.
func (p *Predict) policies() []string {

View File

@ -299,6 +299,60 @@ func TestPredict_Mounts(t *testing.T) {
})
}
func TestPredict_Plugins(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
badClient, badCloser := testVaultServerBad(t)
defer badCloser()
cases := []struct {
name string
client *api.Client
exp []string
}{
{
"not_connected_client",
badClient,
nil,
},
{
"good_path",
client,
[]string{
"cassandra-database-plugin",
"hana-database-plugin",
"mongodb-database-plugin",
"mssql-database-plugin",
"mysql-aurora-database-plugin",
"mysql-database-plugin",
"mysql-legacy-database-plugin",
"mysql-rds-database-plugin",
"postgresql-database-plugin",
},
},
}
t.Run("group", func(t *testing.T) {
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := NewPredict()
p.client = tc.client
act := p.plugins()
if !reflect.DeepEqual(act, tc.exp) {
t.Errorf("expected %q to be %q", act, tc.exp)
}
})
}
})
}
func TestPredict_Policies(t *testing.T) {
t.Parallel()

View File

@ -140,7 +140,7 @@ func (c *SecretsEnableCommand) Flags() *FlagSets {
f.StringVar(&StringVar{
Name: "plugin-name",
Target: &c.flagPluginName,
Completion: complete.PredictAnything,
Completion: c.PredictVaultPlugins(),
Usage: "Name of the secrets engine plugin. This plugin name must already " +
"exist in Vault's plugin catalog.",
})