Add API functions and completions for plugins (#4194)
This commit is contained in:
parent
43e9bcd948
commit
34beea85fb
117
api/sys_plugins.go
Normal file
117
api/sys_plugins.go
Normal 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
|
||||
}
|
@ -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.",
|
||||
})
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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.",
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user