mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-06 06:37:02 +02:00
182 lines
4.7 KiB
Go
182 lines
4.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package command
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/cli"
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/posener/complete"
|
|
)
|
|
|
|
var (
|
|
_ cli.Command = (*PluginReloadCommand)(nil)
|
|
_ cli.CommandAutocomplete = (*PluginReloadCommand)(nil)
|
|
)
|
|
|
|
type PluginReloadCommand struct {
|
|
*BaseCommand
|
|
plugin string
|
|
mounts []string
|
|
scope string
|
|
pluginType string
|
|
}
|
|
|
|
func (c *PluginReloadCommand) Synopsis() string {
|
|
return "Reload mounted plugin backend"
|
|
}
|
|
|
|
func (c *PluginReloadCommand) Help() string {
|
|
helpText := `
|
|
Usage: vault plugin reload [options]
|
|
|
|
Reloads mounted plugins. Either the plugin name or the desired plugin
|
|
mount(s) must be provided, but not both. In case the plugin name is provided,
|
|
all of its corresponding mounted paths that use the plugin backend will be reloaded.
|
|
|
|
If run with a Vault namespace other than the root namespace, only plugins
|
|
running in the same namespace will be reloaded.
|
|
|
|
Reload the secret plugin named "my-custom-plugin" on the current node:
|
|
|
|
$ vault plugin reload -type=secret -plugin=my-custom-plugin
|
|
|
|
Reload the secret plugin named "my-custom-plugin" across all nodes and replicated clusters:
|
|
|
|
$ vault plugin reload -type=secret -plugin=my-custom-plugin -scope=global
|
|
|
|
` + c.Flags().Help()
|
|
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (c *PluginReloadCommand) Flags() *FlagSets {
|
|
set := c.flagSet(FlagSetHTTP)
|
|
|
|
f := set.NewFlagSet("Command Options")
|
|
|
|
f.StringVar(&StringVar{
|
|
Name: "plugin",
|
|
Target: &c.plugin,
|
|
Completion: complete.PredictAnything,
|
|
Usage: "The name of the plugin to reload, as registered in the plugin catalog.",
|
|
})
|
|
|
|
f.StringSliceVar(&StringSliceVar{
|
|
Name: "mounts",
|
|
Target: &c.mounts,
|
|
Completion: complete.PredictAnything,
|
|
Usage: "Array or comma-separated string mount paths of the plugin backends to reload.",
|
|
})
|
|
|
|
f.StringVar(&StringVar{
|
|
Name: "scope",
|
|
Target: &c.scope,
|
|
Completion: complete.PredictAnything,
|
|
Usage: "The scope of the reload, omitted for local, 'global', for replicated reloads.",
|
|
})
|
|
|
|
f.StringVar(&StringVar{
|
|
Name: "type",
|
|
Target: &c.pluginType,
|
|
Completion: complete.PredictAnything,
|
|
Usage: "The type of plugin to reload, one of auth, secret, or database. Mutually " +
|
|
"exclusive with -mounts. If not provided, all plugins with a matching name will be reloaded.",
|
|
})
|
|
|
|
return set
|
|
}
|
|
|
|
func (c *PluginReloadCommand) AutocompleteArgs() complete.Predictor {
|
|
return nil
|
|
}
|
|
|
|
func (c *PluginReloadCommand) AutocompleteFlags() complete.Flags {
|
|
return c.Flags().Completions()
|
|
}
|
|
|
|
func (c *PluginReloadCommand) Run(args []string) int {
|
|
f := c.Flags()
|
|
|
|
if err := f.Parse(args); err != nil {
|
|
c.UI.Error(err.Error())
|
|
return 1
|
|
}
|
|
|
|
positionalArgs := len(f.Args())
|
|
switch {
|
|
case positionalArgs != 0:
|
|
c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", positionalArgs))
|
|
return 1
|
|
case c.plugin == "" && len(c.mounts) == 0:
|
|
c.UI.Error("No plugins specified, must specify exactly one of -plugin or -mounts")
|
|
return 1
|
|
case c.plugin != "" && len(c.mounts) > 0:
|
|
c.UI.Error("Must specify exactly one of -plugin or -mounts")
|
|
return 1
|
|
case c.scope != "" && c.scope != "global":
|
|
c.UI.Error(fmt.Sprintf("Invalid reload scope: %s", c.scope))
|
|
return 1
|
|
case len(c.mounts) > 0 && c.pluginType != "":
|
|
c.UI.Error("Cannot specify -type with -mounts")
|
|
return 1
|
|
}
|
|
|
|
client, err := c.Client()
|
|
if err != nil {
|
|
c.UI.Error(err.Error())
|
|
return 2
|
|
}
|
|
|
|
var reloadID string
|
|
if client.Namespace() == "" {
|
|
pluginType := api.PluginTypeUnknown
|
|
pluginTypeStr := strings.TrimSpace(c.pluginType)
|
|
if pluginTypeStr != "" {
|
|
var err error
|
|
pluginType, err = api.ParsePluginType(pluginTypeStr)
|
|
if err != nil {
|
|
c.UI.Error(fmt.Sprintf("Error parsing -type as a plugin type, must be unset or one of auth, secret, or database: %s", err))
|
|
return 1
|
|
}
|
|
}
|
|
|
|
reloadID, err = client.Sys().RootReloadPlugin(context.Background(), &api.RootReloadPluginInput{
|
|
Plugin: c.plugin,
|
|
Type: pluginType,
|
|
Scope: c.scope,
|
|
})
|
|
} else {
|
|
reloadID, err = client.Sys().ReloadPlugin(&api.ReloadPluginInput{
|
|
Plugin: c.plugin,
|
|
Mounts: c.mounts,
|
|
Scope: c.scope,
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
c.UI.Error(fmt.Sprintf("Error reloading plugin/mounts: %s", err))
|
|
return 2
|
|
}
|
|
|
|
if len(c.mounts) > 0 {
|
|
if reloadID != "" {
|
|
c.UI.Output(fmt.Sprintf("Success! Reloading mounts: %s, reload_id: %s", c.mounts, reloadID))
|
|
} else {
|
|
c.UI.Output(fmt.Sprintf("Success! Reloaded mounts: %s", c.mounts))
|
|
}
|
|
} else {
|
|
if reloadID != "" {
|
|
c.UI.Output(fmt.Sprintf("Success! Reloading plugin: %s, reload_id: %s", c.plugin, reloadID))
|
|
} else {
|
|
c.UI.Output(fmt.Sprintf("Success! Reloaded plugin: %s", c.plugin))
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|