diff --git a/api/sys_plugins.go b/api/sys_plugins.go index 52be04f667..eac3df89f0 100644 --- a/api/sys_plugins.go +++ b/api/sys_plugins.go @@ -129,8 +129,9 @@ type GetPluginResponse struct { SHA256 string `json:"sha256"` } +// GetPlugin retrieves information about the plugin. func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) { - path := fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", i.Type, i.Name) + path := catalogPathByType(i.Type, i.Name) req := c.c.NewRequest(http.MethodGet, path) ctx, cancelFunc := context.WithCancel(context.Background()) @@ -142,13 +143,13 @@ func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) { defer resp.Body.Close() var result struct { - Data GetPluginResponse + Data *GetPluginResponse } err = resp.DecodeJSON(&result) if err != nil { return nil, err } - return &result.Data, err + return result.Data, err } // RegisterPluginInput is used as input to the RegisterPlugin function. @@ -171,8 +172,9 @@ type RegisterPluginInput struct { // RegisterPlugin registers the plugin with the given information. func (c *Sys) RegisterPlugin(i *RegisterPluginInput) error { - path := fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", i.Type, i.Name) + path := catalogPathByType(i.Type, i.Name) req := c.c.NewRequest(http.MethodPut, path) + if err := req.SetJSONBody(i); err != nil { return err } @@ -198,7 +200,7 @@ type DeregisterPluginInput struct { // 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/%s", i.Type, i.Name) + path := catalogPathByType(i.Type, i.Name) req := c.c.NewRequest(http.MethodDelete, path) ctx, cancelFunc := context.WithCancel(context.Background()) @@ -209,3 +211,15 @@ func (c *Sys) DeregisterPlugin(i *DeregisterPluginInput) error { } return err } + +// catalogPathByType is a helper to construct the proper API path by plugin type +func catalogPathByType(pluginType consts.PluginType, name string) string { + path := fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", pluginType, name) + + // Backwards compat, if type is not provided then use old path + if pluginType == consts.PluginTypeUnknown { + path = fmt.Sprintf("/v1/sys/plugins/catalog/%s", name) + } + + return path +} diff --git a/command/plugin_deregister.go b/command/plugin_deregister.go index 2e3b8c3b21..897c1292b1 100644 --- a/command/plugin_deregister.go +++ b/command/plugin_deregister.go @@ -58,14 +58,23 @@ func (c *PluginDeregisterCommand) Run(args []string) int { return 1 } + var pluginNameRaw, pluginTypeRaw string args = f.Args() switch { - case len(args) < 2: - c.UI.Error(fmt.Sprintf("Not enough arguments (expected 2, got %d)", len(args))) + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1 or 2, got %d)", len(args))) return 1 case len(args) > 2: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 2, got %d)", len(args))) + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args))) return 1 + + // These cases should come after invalid cases have been checked + case len(args) == 1: + pluginTypeRaw = "unknown" + pluginNameRaw = args[0] + case len(args) == 2: + pluginTypeRaw = args[0] + pluginNameRaw = args[1] } client, err := c.Client() @@ -74,12 +83,12 @@ func (c *PluginDeregisterCommand) Run(args []string) int { return 2 } - pluginType, err := consts.ParsePluginType(strings.TrimSpace(args[0])) + pluginType, err := consts.ParsePluginType(strings.TrimSpace(pluginTypeRaw)) if err != nil { c.UI.Error(err.Error()) return 2 } - pluginName := strings.TrimSpace(args[1]) + pluginName := strings.TrimSpace(pluginNameRaw) if err := client.Sys().DeregisterPlugin(&api.DeregisterPluginInput{ Name: pluginName, diff --git a/command/plugin_info.go b/command/plugin_info.go index 98c121482b..63d6d71f11 100644 --- a/command/plugin_info.go +++ b/command/plugin_info.go @@ -58,14 +58,23 @@ func (c *PluginInfoCommand) Run(args []string) int { return 1 } + var pluginNameRaw, pluginTypeRaw string args = f.Args() switch { - case len(args) < 2: - c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args))) + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1 or 2, got %d)", len(args))) return 1 case len(args) > 2: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args))) return 1 + + // These cases should come after invalid cases have been checked + case len(args) == 1: + pluginTypeRaw = "unknown" + pluginNameRaw = args[0] + case len(args) == 2: + pluginTypeRaw = args[0] + pluginNameRaw = args[1] } client, err := c.Client() @@ -74,12 +83,12 @@ func (c *PluginInfoCommand) Run(args []string) int { return 2 } - pluginType, err := consts.ParsePluginType(strings.TrimSpace(args[0])) + pluginType, err := consts.ParsePluginType(strings.TrimSpace(pluginTypeRaw)) if err != nil { c.UI.Error(err.Error()) return 2 } - pluginName := strings.TrimSpace(args[1]) + pluginName := strings.TrimSpace(pluginNameRaw) resp, err := client.Sys().GetPlugin(&api.GetPluginInput{ Name: pluginName, @@ -90,6 +99,11 @@ func (c *PluginInfoCommand) Run(args []string) int { return 2 } + if resp == nil { + c.UI.Error(fmt.Sprintf("No value found for plugin %q", pluginName)) + return 2 + } + data := map[string]interface{}{ "args": resp.Args, "builtin": resp.Builtin, diff --git a/command/plugin_register.go b/command/plugin_register.go index e9a74a3424..718bb6b3c5 100644 --- a/command/plugin_register.go +++ b/command/plugin_register.go @@ -96,17 +96,26 @@ func (c *PluginRegisterCommand) Run(args []string) int { return 1 } + var pluginNameRaw, pluginTypeRaw string args = f.Args() switch { - case len(args) < 2: - c.UI.Error(fmt.Sprintf("Not enough arguments (expected 2, got %d)", len(args))) + case len(args) < 1: + c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1 or 2, got %d)", len(args))) return 1 case len(args) > 2: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 2, got %d)", len(args))) + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 2, got %d)", len(args))) return 1 case c.flagSHA256 == "": c.UI.Error("SHA256 is required for all plugins, please provide -sha256") return 1 + + // These cases should come after invalid cases have been checked + case len(args) == 1: + pluginTypeRaw = "unknown" + pluginNameRaw = args[0] + case len(args) == 2: + pluginTypeRaw = args[0] + pluginNameRaw = args[1] } client, err := c.Client() @@ -115,12 +124,12 @@ func (c *PluginRegisterCommand) Run(args []string) int { return 2 } - pluginType, err := consts.ParsePluginType(strings.TrimSpace(args[0])) + pluginType, err := consts.ParsePluginType(strings.TrimSpace(pluginTypeRaw)) if err != nil { c.UI.Error(err.Error()) return 2 } - pluginName := strings.TrimSpace(args[1]) + pluginName := strings.TrimSpace(pluginNameRaw) command := c.flagCommand if command == "" { diff --git a/helper/consts/plugin_types.go b/helper/consts/plugin_types.go index 71915ffa2a..e0a00e4860 100644 --- a/helper/consts/plugin_types.go +++ b/helper/consts/plugin_types.go @@ -54,6 +54,6 @@ func ParsePluginType(pluginType string) (PluginType, error) { case "secret": return PluginTypeSecrets, nil default: - return PluginTypeUnknown, fmt.Errorf("%s is not a supported plugin type", pluginType) + return PluginTypeUnknown, fmt.Errorf("%q is not a supported plugin type", pluginType) } } diff --git a/vault/logical_system.go b/vault/logical_system.go index 2ea39bfd80..1c043f6318 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -349,7 +349,17 @@ func (b *SystemBackend) handlePluginCatalogRead(ctx context.Context, req *logica return logical.ErrorResponse("missing plugin name"), nil } - pluginType, err := consts.ParsePluginType(d.Get("type").(string)) + pluginTypeStr := d.Get("type").(string) + if pluginTypeStr == "" { + // If the plugin type is not provided (i.e. the old + // sys/plugins/catalog/:name endpoint is being requested) short-circuit here + // and return a warning + resp := &logical.Response{} + resp.AddWarning(fmt.Sprintf("Deprecated API endpoint, cannot read plugin information from catalog for %q", pluginName)) + return resp, nil + } + + pluginType, err := consts.ParsePluginType(pluginTypeStr) if err != nil { return nil, err } @@ -388,7 +398,20 @@ func (b *SystemBackend) handlePluginCatalogDelete(ctx context.Context, req *logi if pluginName == "" { return logical.ErrorResponse("missing plugin name"), nil } - pluginType, err := consts.ParsePluginType(d.Get("type").(string)) + + var resp *logical.Response + pluginTypeStr := d.Get("type").(string) + if pluginTypeStr == "" { + // If the plugin type is not provided (i.e. the old + // sys/plugins/catalog/:name endpoint is being requested), set type to + // unknown and let pluginCatalog.Delete proceed. It should handle + // deregistering out of the old storage path (root of core/plugin-catalog) + resp = new(logical.Response) + resp.AddWarning(fmt.Sprintf("Deprecated API endpoint, cannot deregister plugin from catalog for %q", pluginName)) + pluginTypeStr = "unknown" + } + + pluginType, err := consts.ParsePluginType(pluginTypeStr) if err != nil { return nil, err } @@ -396,7 +419,7 @@ func (b *SystemBackend) handlePluginCatalogDelete(ctx context.Context, req *logi return nil, err } - return nil, nil + return resp, nil } func (b *SystemBackend) handlePluginReloadUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {