diff --git a/api/sys_plugins.go b/api/sys_plugins.go index 118134380..389e66eb1 100644 --- a/api/sys_plugins.go +++ b/api/sys_plugins.go @@ -32,10 +32,11 @@ type ListPluginsResponse struct { } type PluginDetails struct { - Type string `json:"string"` - Name string `json:"name"` - Version string `json:"version,omitempty"` - Builtin bool `json:"builtin"` + Type string `json:"string"` + Name string `json:"name"` + Version string `json:"version,omitempty"` + Builtin bool `json:"builtin"` + DeprecationStatus string `json:"deprecation_status,omitempty" mapstructure:"deprecation_status"` } // ListPlugins wraps ListPluginsWithContext using context.Background. @@ -158,11 +159,12 @@ type GetPluginInput struct { // 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"` + Args []string `json:"args"` + Builtin bool `json:"builtin"` + Command string `json:"command"` + Name string `json:"name"` + SHA256 string `json:"sha256"` + DeprecationStatus string `json:"deprecation_status,omitempty"` } // GetPlugin wraps GetPluginWithContext using context.Background. diff --git a/changelog/17077.txt b/changelog/17077.txt new file mode 100644 index 000000000..0f5d1f8cc --- /dev/null +++ b/changelog/17077.txt @@ -0,0 +1,12 @@ +```release-note:change +plugins: `GET /sys/plugins/catalog/:type/:name` endpoint contains deprecation status for builtin plugins. +``` +```release-note:change +plugins: `GET /sys/plugins/catalog/` endpoint contains deprecation status in `detailed` list. +``` +```release-note:change +plugins: `plugin list` now accepts a `-detailed` flag, which display deprecation status and version info. +``` +```release-note:change +plugins: `plugin info` displays deprecation status for builtin plugins. +``` diff --git a/command/plugin_info.go b/command/plugin_info.go index a5676e730..bb7a4a505 100644 --- a/command/plugin_info.go +++ b/command/plugin_info.go @@ -107,11 +107,12 @@ func (c *PluginInfoCommand) Run(args []string) int { } data := map[string]interface{}{ - "args": resp.Args, - "builtin": resp.Builtin, - "command": resp.Command, - "name": resp.Name, - "sha256": resp.SHA256, + "args": resp.Args, + "builtin": resp.Builtin, + "command": resp.Command, + "name": resp.Name, + "sha256": resp.SHA256, + "deprecation_status": resp.DeprecationStatus, } if c.flagField != "" { diff --git a/command/plugin_list.go b/command/plugin_list.go index 40cf5a8fd..d9651127b 100644 --- a/command/plugin_list.go +++ b/command/plugin_list.go @@ -18,6 +18,8 @@ var ( type PluginListCommand struct { *BaseCommand + + flagDetailed bool } func (c *PluginListCommand) Synopsis() string { @@ -40,13 +42,30 @@ Usage: vault plugin list [options] [TYPE] $ vault plugin list database + List all available plugins with detailed output: + + $ vault plugin list -detailed + ` + c.Flags().Help() return strings.TrimSpace(helpText) } func (c *PluginListCommand) Flags() *FlagSets { - return c.flagSet(FlagSetHTTP | FlagSetOutputFormat) + set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) + + f := set.NewFlagSet("Command Options") + + f.BoolVar(&BoolVar{ + Name: "detailed", + Target: &c.flagDetailed, + Default: false, + Usage: "Print detailed plugin information such as plugin type, " + + "version, and deprecation status for each plugin. This option " + + "is only applicable to table-formatted output.", + }) + + return set } func (c *PluginListCommand) AutocompleteArgs() complete.Predictor { @@ -105,19 +124,11 @@ func (c *PluginListCommand) Run(args []string) int { switch Format(c.UI) { case "table": - var flattenedNames []string - namesAdded := make(map[string]bool) - for _, names := range resp.PluginsByType { - for _, name := range names { - if ok := namesAdded[name]; !ok { - flattenedNames = append(flattenedNames, name) - namesAdded[name] = true - } - } - sort.Strings(flattenedNames) + if c.flagDetailed { + c.UI.Output(tableOutput(c.detailedResponse(resp), nil)) + return 0 } - list := append([]string{"Plugins"}, flattenedNames...) - c.UI.Output(tableOutput(list, nil)) + c.UI.Output(tableOutput(c.simpleResponse(resp), nil)) return 0 default: res := make(map[string]interface{}) @@ -127,3 +138,28 @@ func (c *PluginListCommand) Run(args []string) int { return OutputData(c.UI, res) } } + +func (c *PluginListCommand) simpleResponse(plugins *api.ListPluginsResponse) []string { + var flattenedNames []string + namesAdded := make(map[string]bool) + for _, names := range plugins.PluginsByType { + for _, name := range names { + if ok := namesAdded[name]; !ok { + flattenedNames = append(flattenedNames, name) + namesAdded[name] = true + } + } + sort.Strings(flattenedNames) + } + list := append([]string{"Plugins"}, flattenedNames...) + return list +} + +func (c *PluginListCommand) detailedResponse(plugins *api.ListPluginsResponse) []string { + out := []string{"Name | Type | Version | Deprecation Status"} + for _, plugin := range plugins.Details { + out = append(out, fmt.Sprintf("%s | %s | %s | %s", plugin.Name, plugin.Type, plugin.Version, plugin.DeprecationStatus)) + } + + return out +} diff --git a/sdk/helper/pluginutil/runner.go b/sdk/helper/pluginutil/runner.go index 2fb484978..631c4f3a2 100644 --- a/sdk/helper/pluginutil/runner.go +++ b/sdk/helper/pluginutil/runner.go @@ -88,11 +88,12 @@ func (r *PluginRunner) RunMetadataMode(ctx context.Context, wrapper RunnerUtil, // VersionedPlugin holds any versioning information stored about a plugin in the // plugin catalog. type VersionedPlugin struct { - Type string `json:"type"` // string instead of consts.PluginType so that we get the string form in API responses. - Name string `json:"name"` - Version string `json:"version"` - SHA256 string `json:"sha256,omitempty"` - Builtin bool `json:"builtin"` + Type string `json:"type"` // string instead of consts.PluginType so that we get the string form in API responses. + Name string `json:"name"` + Version string `json:"version"` + SHA256 string `json:"sha256,omitempty"` + Builtin bool `json:"builtin"` + DeprecationStatus string `json:"deprecation_status,omitempty"` // Pre-parsed semver struct of the Version field SemanticVersion *version.Version `json:"-"` diff --git a/vault/logical_system.go b/vault/logical_system.go index b9a37363d..4373083b4 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -572,6 +572,11 @@ func (b *SystemBackend) handlePluginCatalogRead(ctx context.Context, _ *logical. "version": plugin.Version, } + if plugin.Builtin { + status, _ := b.Core.builtinRegistry.DeprecationStatus(plugin.Name, plugin.Type) + data["deprecation_status"] = status.String() + } + return &logical.Response{ Data: data, }, nil diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 17156b41e..b8dce2690 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -2989,14 +2989,18 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { t.Fatalf("err: %v", err) } + // Get deprecation status directly from the registry so we can compare it to the API response + deprecationStatus, _ := c.builtinRegistry.DeprecationStatus("mysql-database-plugin", consts.PluginTypeDatabase) + actualRespData := resp.Data expectedRespData := map[string]interface{}{ - "name": "mysql-database-plugin", - "command": "", - "args": []string(nil), - "sha256": "", - "builtin": true, - "version": c.pluginCatalog.getBuiltinVersion(consts.PluginTypeDatabase, "mysql-database-plugin"), + "name": "mysql-database-plugin", + "command": "", + "args": []string(nil), + "sha256": "", + "builtin": true, + "version": c.pluginCatalog.getBuiltinVersion(consts.PluginTypeDatabase, "mysql-database-plugin"), + "deprecation_status": deprecationStatus.String(), } if !reflect.DeepEqual(actualRespData, expectedRespData) { t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", actualRespData, expectedRespData) diff --git a/vault/plugin_catalog.go b/vault/plugin_catalog.go index 2da30137f..47c897652 100644 --- a/vault/plugin_catalog.go +++ b/vault/plugin_catalog.go @@ -853,15 +853,17 @@ func (c *PluginCatalog) listInternal(ctx context.Context, pluginType consts.Plug version := c.getBuiltinVersion(pluginType, plugin) semanticVersion, err := semver.NewVersion(version) + deprecationStatus, _ := c.builtinRegistry.DeprecationStatus(plugin, pluginType) if err != nil { return nil, err } result = append(result, pluginutil.VersionedPlugin{ - Name: plugin, - Type: pluginType.String(), - Version: version, - Builtin: true, - SemanticVersion: semanticVersion, + Name: plugin, + Type: pluginType.String(), + Version: version, + Builtin: true, + SemanticVersion: semanticVersion, + DeprecationStatus: deprecationStatus.String(), }) } diff --git a/website/content/docs/commands/plugin/info.mdx b/website/content/docs/commands/plugin/info.mdx index 685c95aef..485465783 100644 --- a/website/content/docs/commands/plugin/info.mdx +++ b/website/content/docs/commands/plugin/info.mdx @@ -9,6 +9,12 @@ description: The "plugin info" command displays information about a plugin in th The `plugin info` displays information about a plugin in the catalog. The plugin's type of "auth", "database", or "secret" must be included. +## deprecation_status field + +As of 1.12, all builtin plugins will have an associated Deprecation +Status. This status will be reflected in the `deprecation_status` key/value +pair, seen below. + ## Examples Display information about a plugin @@ -16,13 +22,26 @@ Display information about a plugin ```shell-session $ vault plugin info auth my-custom-plugin -Key Value ---- ----- -args [] -builtin false -command my-custom-plugin -name my-custom-plugin -sha256 d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 +Key Value +--- ----- +args [] +builtin false +command my-custom-plugin +deprecation_status n/a +name my-custom-plugin +sha256 d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 +``` + +```shell-session +$ vault plugin info database postgresql-database-plugin +Key Value +--- ----- +args [] +builtin true +command n/a +deprecation_status supported +name postgresql-database-plugin +sha256 n/a ``` ## Usage diff --git a/website/content/docs/commands/plugin/list.mdx b/website/content/docs/commands/plugin/list.mdx index 7ef9103bb..df9e41960 100644 --- a/website/content/docs/commands/plugin/list.mdx +++ b/website/content/docs/commands/plugin/list.mdx @@ -9,6 +9,12 @@ description: The "plugin list" command lists all available plugins in the plugin The `plugin list` command lists all available plugins in the plugin catalog. It can be used alone or with a type such as "auth", "database", or "secret". +## Deprecation Status Column + +As of 1.12, all builtin plugins will have an associated Deprecation +Status. This status will be reflected in the `Deprecation Status` column, seen +below. All non-builtin plugins will show a `Deprecation Status` of "n/a". + ## Examples List all available plugins in the catalog. @@ -28,6 +34,16 @@ cassandra-database-plugin # ... ``` +List detailed plugin information: + +```shell-session +$ vault plugin list -detailed +Name Type Version Deprecation Status +---- ---- ------- ------------------ +alicloud auth v0.12.0+builtin supported +app-id auth v1.12.0+builtin.vault pending removal +# ... +``` ## Usage The following flags are available in addition to the [standard set of @@ -38,3 +54,8 @@ flags](/docs/commands) included on all commands. - `-format` `(string: "table")` - Print the output in the given format. Valid formats are "table", "json", or "yaml". This can also be specified via the `VAULT_FORMAT` environment variable. + +### Command Options + +- `-detailed` `(bool: false)` - Print detailed information such as version and +deprecation status about each plugin.