Skip to content

Conversation

@abendrothj
Copy link

Abstract

This proposal enhances Velero's plugin management CLI to distinguish between built-in and external plugins, and allows removal of external plugins by plugin name.
The design addresses user confusion about which plugins are mandatory and provides intuitive plugin removal workflows.

Background

Currently, velero plugin get shows all installed plugins without indicating which are built into the Velero server binary versus installed via init containers.
Users cannot easily identify mandatory plugins that cannot be removed by modifying the Velero deployment.
Additionally, velero plugin remove only accepts init container names or images, requiring users to know the exact container details rather than the intuitive plugin names they see in velero plugin get.

Goals

  • Display built-in status in velero plugin get output to help users identify mandatory plugins.
  • Enable velero plugin remove <plugin-name> syntax for intuitive plugin removal.

Non Goals

  • Changing the existing velero plugin remove <image> or velero plugin remove <container-name> functionality.
  • Adding plugin version management or dependency resolution.
  • Modifying the plugin registration or discovery mechanisms.
  • Adding plugin dependency resolution or conflict detection.

High-Level Design

The design extends the ServerStatusRequest API to include plugin metadata (BuiltIn flag and Command field) that the server populates based on plugin registration context.
The CLI is enhanced to display this metadata and use it for intelligent plugin removal decisions.
Plugin name resolution uses heuristics to map user-provided plugin names to init containers in the Velero deployment.

Detailed Design

API Changes

Extend PluginInfo struct in pkg/apis/velero/v1/server_status_request_types.go:

type PluginInfo struct {
    Name string `json:"name"`
    Kind string `json:"kind"`
    // Command is the command/binary that registered the plugin on the server.
    // For built-in Velero plugins this will typically be the Velero server binary.
    // This field is primarily for informational/diagnostic purposes.
    // +optional
    Command string `json:"command,omitempty"`
    
    // BuiltIn indicates whether the plugin is provided by the Velero server
    // binary (i.e. it's a system/plugin that cannot be removed by changing
    // init containers). When true, the CLI should not allow removal.
    // +optional
    BuiltIn bool `json:"builtIn,omitempty"`
}

Server-Side Changes

Modify GetInstalledPluginInfo in internal/velero/serverstatusrequest.go:

  • Populate Command field with the plugin's registration command.
  • Set BuiltIn = true when plugin.Command == os.Args[0] (registered by Velero server binary).

CLI Output Changes

Update pkg/cmd/util/output/plugin_printer.go:

  • Add "BuiltIn" column to plugin table output.
  • Display boolean values for built-in status.

Plugin Removal Enhancement

Enhance pkg/cmd/cli/plugin/remove.go:

  • Accept plugin names (containing '/') as arguments.
  • Query server status to check if plugin is built-in.
  • Refuse removal of built-in plugins with clear error message.
  • Use heuristics to map plugin names to init containers:
    • Exact init container name match.
    • Exact init container image match.
    • Sanitized plugin name matching init container name.
    • Substring matching of plugin name in init container image.
  • Handle ambiguous matches by asking user to specify exact container/image.

Plugin Name Resolution Heuristics

When user provides plugin name (e.g., velero.io/aws):

  1. Check if plugin is built-in via server status query.
  2. If built-in, refuse removal with error.
  3. If not built-in, search Velero deployment init containers:
    • Exact name match: aws-plugin.
    • Exact image match: velero/velero-plugin-for-aws.
    • Sanitized name match: convert velero.io/aws to aws and match container names.
    • Substring match: find containers with aws in image or name.
  4. If multiple matches found, return error asking for disambiguation.
  5. If no matches found, return error with available init containers.

Alternatives Considered

  • Server-side plugin-to-image mapping: Could expose plugin-to-init-container mapping from server, but adds complexity and requires server-side changes to plugin registry.
  • Plugin manifest files: Could require users to maintain plugin manifests, but adds operational overhead.
  • Strict naming conventions: Could enforce strict init container naming, but breaks existing deployments.

Security Considerations

No security implications.
The changes only affect CLI display and plugin removal workflows.
No new attack surfaces are introduced.

Compatibility

  • Backward compatible: Existing velero plugin remove <image> and velero plugin remove <container-name> syntax continues to work.
  • API compatible: New fields in PluginInfo are optional and won't break existing clients.
  • Deployment compatible: No changes to Velero server deployment or plugin installation process.

Implementation

The implementation is complete as a proof of concept in the current PR.
The changes were implemented in the following sequence:

  1. Add API fields and server-side population.
  2. Update CLI output formatting.
  3. Enhance plugin removal command.
  4. Add comprehensive unit tests.
    The author (Jake Abendroth) has implemented this design.

Open Issues

  • Should we consider exposing plugin version information in future iterations?
  • Are the proposed heuristics sufficient for all common plugin naming patterns?
  • Should we add a --force flag to override built-in plugin protection for advanced users?

Does your change fix a particular issue?

Fixes #3210

Please indicate you've done the following:

Lyndon-Li and others added 6 commits October 19, 2025 02:20
Signed-off-by: Lyndon-Li <[email protected]>
Signed-off-by: Jake Abendroth <[email protected]>
Signed-off-by: Lyndon-Li <[email protected]>
Signed-off-by: Jake Abendroth <[email protected]>
- Introduced 'Command' and 'BuiltIn' fields in PluginInfo for better plugin management.
- Updated GetInstalledPluginInfo to mark built-in plugins.
- Enhanced remove command to prevent removal of built-in plugins.
- Modified plugin printer to display built-in status.

Signed-off-by: Jake Abendroth <[email protected]>
- Introduced tests for GetInstalledPluginInfo to validate built-in plugin detection.
- Added tests for plugin removal logic, ensuring built-in plugins cannot be removed.
- Implemented heuristic matching for plugin names in various scenarios.

Signed-off-by: Jake Abendroth <[email protected]>
- Add changelog for issue vmware-tanzu#3210 plugin management enhancements
- Update overview-plugins.md to document new plugin name removal syntax
- Add section explaining built-in vs external plugins
- Document new BuiltIn column in 'velero plugin get' output

Signed-off-by: Jake Abendroth <[email protected]>
Comment on lines 28 to 31
NAME KIND BuiltIn
velero.io/aws ObjectStore true
velero.io/pod BackupItemAction true
velero.io/gcp ObjectStore false
Copy link
Collaborator

@kaovilai kaovilai Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NAME KIND BuiltIn
velero.io/aws ObjectStore true
velero.io/pod BackupItemAction true
velero.io/gcp ObjectStore false
NAME KIND Origin
velero.io/aws ObjectStore Internal
velero.io/pod BackupItemAction Internal
velero.io/gcp ObjectStore External

I like internal/external better imo.. but regardless both aws and gcp example here should match, they are both not built-in. note above is snippet is just for discussion.

Copy link
Author

@abendrothj abendrothj Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! We are mocking which plugins are external/internal in the test, but you're absolutely right - both AWS and GCP plugins are external in reality, not built-in.

I've updated the tests to use more realistic examples:

  • Built-in plugins: velero.io/pod, velero.io/pv
  • External plugins: velero.io/aws, velero.io/gcp

The mock data was just to test the BuiltIn detection logic (Command == os.Args[0]), but it should reflect reality for clarity. Thanks for pointing that out!

…l plugins

- Changed the command paths for the AWS plugin to reflect its status as an external plugin.
- Updated test cases to ensure correct detection of built-in and external plugins.
- Revised documentation to clarify the distinction between built-in and external plugins, including examples.

Signed-off-by: Jake Abendroth <[email protected]>
…removal commands

- Adjusted the output of the 'velero plugin get' command to correctly indicate the built-in status of the AWS and pod plugins.
- Updated examples for the plugin removal command to demonstrate the inability to remove built-in plugins.

Signed-off-by: Jake Abendroth <[email protected]>
@reasonerjt
Copy link
Contributor

@abendrothj Thanks for the PR.

Could you write a separate PR for the design? After the design is reviewed and approved by the maintainers, we can merge the implementation.
Note that we are relatively close to the Feature Freeze milestone of v1.18 so this enhancement may be targeting a later release such as v1.19.

@abendrothj
Copy link
Author

@abendrothj Thanks for the PR.

Could you write a separate PR for the design? After the design is reviewed and approved by the maintainers, we can merge the implementation. Note that we are relatively close to the Feature Freeze milestone of v1.18 so this enhancement may be targeting a later release such as v1.19.

#9364

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display which plugins are mandatory and can't be removed

4 participants