vault/command/plugin_reload.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
}