Documentation
¶
Overview ¶
Package plugins implements the d8-cli plugin system.
A d8 plugin is a standalone binary published to an OCI Registry. It is not part of d8-cli and can be developed independently.
Why plugins exist ¶
- Isolate dependencies.
- Enable independent, parallel development.
- Let different teams own different plugins (delivery, system, ...).
- Keep d8 itself compact, with only the dependencies it actually needs.
What d8-cli can do with plugins ¶
- Download them.
- Validate their dependencies (requirements the plugin declares in its contract).
- Run them as if they were native subcommands.
Where a plugin lives ¶
A plugin lives in the cluster's OCI registry, reached exclusively through the in-cluster registry-packages-proxy. The image carries the plugin binary in its layers and a contract, published as a manifest annotation, that describes the plugin:
- name;
- version;
- description;
- environment variables;
- flags;
- requirements.
How a plugin invocation works ¶
- The user invokes a command through d8.
- The parent CLI checks whether the plugin is installed.
- If it is not, the image is pulled through the registry-packages-proxy.
- The binary is unpacked.
- Requirements are validated.
- A symlink is pointed at the current major version.
- The plugin is exec'd with the forwarded arguments.
On-disk layout ¶
Installed plugins live under the install root: /opt/deckhouse/lib/deckhouse-cli by default (override with --plugins-dir / DECKHOUSE_CLI_PATH; ~/.deckhouse-cli when the default is not writable). Concrete paths:
<root>/plugins/<name>/v<major>/<name> plugin binary (one per major version) <root>/plugins/<name>/current symlink to the active major's binary <root>/plugins/<name>/install.lock install lock (one per plugin) <root>/cache/contracts/<name>.json cached contract
Versions are kept per major; the `current` symlink selects the active one, so switching is an atomic repoint - the same idea selfupdate uses for d8 itself. Package internal/plugins/layout holds the authoritative path builders.
What the plugin system is made of ¶
- Discover - learn what plugins exist and what their contracts declare.
- Install - download and place the plugin in the right location with proper validation.
- Exec - run the plugin as part of d8 without losing argument context.
Index ¶
- Variables
- func ProvidesEnv(name string) bool
- func SplitPlatform(version *semver.Version) (*semver.Version, string)
- func ValidatePluginName(name string) error
- type InstallOption
- type Manager
- func (m *Manager) AvailablePlugins(ctx context.Context) ([]RemotePluginInfo, error)
- func (m *Manager) EnsureInstallRoot() error
- func (m *Manager) InitPluginServices(ctx context.Context) error
- func (m *Manager) InstallPlugin(ctx context.Context, pluginName string, opts ...InstallOption) error
- func (m *Manager) InstalledPluginContract(pluginName string) (*internal.Plugin, error)
- func (m *Manager) InstalledPluginNames() ([]string, error)
- func (m *Manager) InstalledVersionOrNil(pluginName string) *semver.Version
- func (m *Manager) LatestVersion(ctx context.Context, pluginName string) (*semver.Version, error)
- func (m *Manager) List() []PluginInfo
- func (m *Manager) PluginContract(ctx context.Context, pluginName, tag string) (*internal.Plugin, error)
- func (m *Manager) PublishedVersions(ctx context.Context, pluginName string) ([]PluginVersion, error)
- func (m *Manager) Remove(pluginName string) error
- func (m *Manager) RemoveAll() error
- func (m *Manager) RunInstalled(ctx context.Context, pluginName string, args []string) error
- func (m *Manager) SetBuiltinCommands(names []string)
- func (m *Manager) SetDirectory(dir string)
- func (m *Manager) UpdateAll(ctx context.Context, opts ...InstallOption) error
- type PluginInfo
- type PluginVersion
- type RemotePluginInfo
- type Transport
Constants ¶
This section is empty.
Variables ¶
var ErrCatalogUnsupported = errors.New("listing published plugins is not supported by this transport")
ErrCatalogUnsupported means the transport in use cannot enumerate the published plugins. It is a property of the transport, not a runtime failure: the proxy allowlist admits deckhouse-cli and deckhouse-cli/plugins/<name> and refuses the bare deckhouse-cli/plugins path, so over TransportRPP the request is never made. Addressing a plugin by exact name is unaffected - install, update and versions work on every transport.
Functions ¶
func ProvidesEnv ¶ added in v0.32.0
ProvidesEnv reports whether d8 actually injects a value for a contract-requested env var (vs leaving it to pass through from the inherited environment). It is the single source of truth for both injection and help. MODULE_CONFIG_INFO is not yet provided (it needs a defined module mapping in the contract).
func SplitPlatform ¶ added in v0.33.15
SplitPlatform separates a version's platform suffix from the version itself. Plugin images are published one tag per platform, so a single release reaches the registry as v0.0.34-linux-amd64, v0.0.34-darwin-arm64 and so on - the os-arch pair riding at the end of the semver prerelease slot.
A release that is itself a pre-release keeps that identity: v0.0.1-test-linux-amd64 splits into v0.0.1-test and linux/amd64, so its per-platform tags collapse onto the pre-release rather than onto the stable version of the same number. A version whose prerelease carries no platform tail comes back untouched with an empty platform.
func ValidatePluginName ¶ added in v0.32.0
ValidatePluginName guards every user-supplied plugin name before it reaches MkdirAll / RemoveAll / registry paths. The rule lives in layout so that mirror applies the same one to catalog and contract names.
Types ¶
type InstallOption ¶ added in v0.32.0
type InstallOption func(*installOptions)
func InstallWithForce ¶ added in v0.32.0
func InstallWithForce() InstallOption
func InstallWithMajorVersion ¶ added in v0.32.0
func InstallWithMajorVersion(majorVersion int) InstallOption
func InstallWithVersion ¶ added in v0.32.0
func InstallWithVersion(version string) InstallOption
type Manager ¶ added in v0.32.0
type Manager struct {
// contains filtered or unexported fields
}
Manager is the plugin machinery shared by every `d8 dist plugins ...` subcommand and the per-plugin wrapper command (see internal/plugins/cmd): it installs, updates, removes, lists and runs plugins from the configured source.
func NewManager ¶ added in v0.32.0
func (*Manager) AvailablePlugins ¶ added in v0.33.15
func (m *Manager) AvailablePlugins(ctx context.Context) ([]RemotePluginInfo, error)
AvailablePlugins enumerates the plugins published in the registry, by name.
The catalog is the tag list of the plugins repository, where each plugin has an image tagged with its name; a source that can reach it declares pluginCatalog. Only a failure to enumerate at all is returned as an error - a plugin whose versions cannot be resolved still appears, carrying a Note that says so.
func (*Manager) EnsureInstallRoot ¶ added in v0.32.0
EnsureInstallRoot creates <pluginDirectory>/plugins; on permission denied falls back to ~/.deckhouse-cli, updates m.pluginDirectory, and retries.
func (*Manager) InitPluginServices ¶ added in v0.32.0
InitPluginServices wires m.service to the in-cluster registry-packages-proxy, reaching the proxy by the user's kubeconfig identity. ctx bounds endpoint discovery, so a Ctrl-C during command startup is honored. The proxy is the only plugin source (ADR: deckhouse-cli reaches the registry exclusively through it).
func (*Manager) InstallPlugin ¶ added in v0.32.0
func (m *Manager) InstallPlugin(ctx context.Context, pluginName string, opts ...InstallOption) error
InstallPlugin installs or switches to a plugin version. Steps: select the version, validate requirements, lay out plugins/<name>/v<major>, swap the binary in, point `current` at it, cache the contract. Tune behaviour with the InstallWith* options (version, major, force). With no options it installs the newest cluster-compatible version within the installed major. A plugin's mandatory dependencies are always installed/upgraded first.
func (*Manager) InstalledPluginContract ¶ added in v0.32.0
InstalledPluginContract reads the cached contract from <plugin-dir>/cache/contracts/<plugin>.json and converts it to a domain object.
func (*Manager) InstalledPluginNames ¶ added in v0.32.0
InstalledPluginNames returns the plugins that are actually installed under the plugins root - a directory with a `current` symlink. A leftover directory from a failed install has no symlink and is excluded, so it cannot become an install target for a plugin the user never had.
func (*Manager) InstalledVersionOrNil ¶ added in v0.32.0
InstalledVersionOrNil returns the active installed version of the plugin, or nil when the plugin is not installed or its version cannot be probed - best-effort: a version listing then simply carries no "current" marker.
func (*Manager) LatestVersion ¶ added in v0.32.0
LatestVersion lists tags from the registry for a plugin and returns the highest STABLE semver version - the same notion of "latest" that install selection uses (select.go), so `plugins list`/`contract` never advertise a pre-release that a default install would not pick.
func (*Manager) List ¶ added in v0.32.0
func (m *Manager) List() []PluginInfo
List returns the plugins installed on disk. For the plugins published in the registry and available to install, see AvailablePlugins.
func (*Manager) PluginContract ¶ added in v0.32.0
func (m *Manager) PluginContract(ctx context.Context, pluginName, tag string) (*internal.Plugin, error)
PluginContract fetches a plugin contract, memoizing it per name@tag for the duration of the command. Requirements-aware selection probes several versions' contracts, and the chosen one is fetched again by the install pipeline; the cache keeps that to a single pull per version.
func (*Manager) PublishedVersions ¶ added in v0.32.0
func (m *Manager) PublishedVersions(ctx context.Context, pluginName string) ([]PluginVersion, error)
PublishedVersions lists the plugin's published releases, newest first (unparseable tags are dropped). A plugin is published one tag per platform, so the per-platform tags of a release are collapsed into a single entry carrying the platforms it was built for.
func (*Manager) Remove ¶ added in v0.32.0
Remove deletes an installed plugin from disk: its install directory and the cached contract. It is idempotent (removing a plugin that is not installed is a no-op) and holds the plugin's install lock so it cannot race a concurrent install of the same plugin.
func (*Manager) RemoveAll ¶ added in v0.32.0
RemoveAll deletes every plugin found under the plugins root, each under its own install lock.
func (*Manager) RunInstalled ¶ added in v0.32.0
RunInstalled ensures the plugin is installed, enforces its contract requirements, then execs its binary with args. stdin/stdout/stderr are inherited; the contract's requested env vars are injected.
func (*Manager) SetBuiltinCommands ¶ added in v0.32.0
SetBuiltinCommands records the d8 built-in command names that satisfy a plugin dependency of the same name. It bridges capabilities that ship as a built-in command but are not yet published as standalone plugins (e.g. delivery-kit): such a dependency counts as satisfied by the command's mere presence, with no on-disk install and no registry lookup.
func (*Manager) SetDirectory ¶ added in v0.32.0
SetDirectory retargets the manager at another install root. The command layer calls it after flag parsing, so --plugins-dir overrides the directory captured at construction time.
func (*Manager) UpdateAll ¶ added in v0.32.0
func (m *Manager) UpdateAll(ctx context.Context, opts ...InstallOption) error
UpdateAll updates every installed plugin to its newest cluster-compatible version within the current major. A per-plugin failure does not stop the others; the failures are reported together in the returned error.
opts are forwarded to every plugin's install, so only options that make sense applied uniformly belong here - the command layer rejects the rest (an exact --version or a --use-major pin cannot mean anything across a whole set).
type PluginInfo ¶ added in v0.32.0
PluginInfo is one installed plugin's name, version and description for display.
type PluginVersion ¶ added in v0.33.15
PluginVersion is one published release: the version with any platform suffix removed, plus the platforms that release was published for. Platforms is empty for a plugin published as a single platform-independent tag.
type RemotePluginInfo ¶ added in v0.33.15
RemotePluginInfo is one plugin published in the registry: its name, the newest stable version on offer, and whether it is already installed locally. Note says why Version is empty - a plugin that could only be named is still listed, never silently dropped.
type Transport ¶ added in v0.33.15
type Transport string
Transport names how the plugin subsystem reaches the registry. The two differ in more than plumbing - see pluginCatalog for the capability that only one of them has - so the transport in use is named in errors rather than left implicit.
const ( // TransportRPP goes through the in-cluster registry-packages-proxy, authenticated // by the caller's kubeconfig identity and needing no registry credentials. It is // the default and the only supported transport (ADR #386). TransportRPP Transport = "registry-packages-proxy" // TransportRegistry talks to a registry repository directly, with credentials, // selected by the legacy --source bypass (see source_legacy.go). TransportRegistry Transport = "registry" )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package pluginscmd implements the `d8 dist plugins` command tree and the per-plugin wrapper command on top of the internal/plugins machinery.
|
Package pluginscmd implements the `d8 dist plugins` command tree and the per-plugin wrapper command on top of the internal/plugins machinery. |
|
errdetect
Package errdetect maps registry-packages-proxy failures from `d8 dist plugins` to HelpfulErrors with plugin-specific guidance.
|
Package errdetect maps registry-packages-proxy failures from `d8 dist plugins` to HelpfulErrors with plugin-specific guidance. |
|
Package flags defines the shared CLI flag set used by the `d8 dist plugins` management subcommands and consumed when building the registry-packages-proxy client and enforcing cluster-side requirements.
|
Package flags defines the shared CLI flag set used by the `d8 dist plugins` management subcommands and consumed when building the registry-packages-proxy client and enforcing cluster-side requirements. |
|
Package layout centralizes the on-disk filesystem layout used by the d8 plugins subsystem: directory names, suffixes, and helpers that build concrete paths from the install root.
|
Package layout centralizes the on-disk filesystem layout used by the d8 plugins subsystem: directory names, suffixes, and helpers that build concrete paths from the install root. |
|
Package requirements answers one question: does THIS cluster satisfy a plugin's cluster-side requirements (Kubernetes, Deckhouse and module versions)?
|
Package requirements answers one question: does THIS cluster satisfy a plugin's cluster-side requirements (Kubernetes, Deckhouse and module versions)? |