kep-number | title | authors | owning-sig | participating-sigs | reviewers | approvers | editor | creation-date | last-updated | status | see-also | replaces | superseded-by | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
24 |
Kubectl Plugins |
|
sig-cli |
|
|
|
juanvallejo |
2018-07-24 |
2018-08-09 |
provisional |
|
|
- Table of Contents
- Summary
- Motivation
- Proposal
- Graduation Criteria
- Implementation History
- Drawbacks
- Future Improvements/Considerations
This proposal introduces the main design for a plugin mechanism in kubectl
.
The mechanism is a git-style system, that looks for executables on a user's $PATH
whose name begins with kubectl-
.
This allows plugin binaries to override existing command paths and add custom commands and subcommands to kubectl
.
The main motivation behind a plugin system for kubectl
stems from being able to provide users with a way to extend
the functionality of kubectl
, beyond what is offered by its core commands.
By picturing the core commands provided by kubectl
as essential building blocks for interacting with a Kubernetes
cluster, we can begin to think of plugins as a means of using these building blocks to provide more complex functionality.
A new command, kubectl set-ns
, for example, could take advantage of the rudimentary functionality already provided by
the kubectl config
command, and build on top of it to provide users with a powerful, yet easy-to-use way of switching
to a new namespace.
For example, the user experience for switching namespaces could go from:
kubectl config set-context $(kubectl config current-context) --namespace=mynewnamespace
to:
kubectl set-ns mynewnamespace
where set-ns
would be a user-provided plugin which would call the initial kubectl config set-context ...
command
and set the namespace flag according to the value provided as the plugin's first parameter.
The set-ns
command above could have multiple variations, or be expanded to support subcommands with relative ease.
Since plugins would be distributed by their authors, independent from the core Kubernetes repository, plugins could
release updates and changes at their own pace.
The existing alpha plugin system in kubectl
presents a few limitations with its current design.
It forces plugin scripts and executables to exist in a pre-determined location, requires a per-plugin metadata file for
interpretation, and does not provide a clear way to override existing command paths or provide additional subcommands
without having to override a top-level command.
The proposed git-style re-design of the plugin system allows us to implement extensibility requests from users that the current system is unable to address. See kubernetes/kubernetes#53640 and kubernetes/kubernetes#55708.
- Avoid any kind of installation process (no additional config, users drop an executable in their
PATH
, for example, and they are then able to use that plugin withkubectl
). No additional configuration is needed, only the plugin executable. A plugin's filename determines the plugin's intention, such as which path in the command tree it applies to:/usr/bin/kubectl-educate-dolphins
would, for example be invoked under the commandkubectl educate dolphins --flag1 --flag2
. It is up to a plugin to parse any arguments and flags given to it. A plugin decides when an argument is a subcommand, as well as any limitations or constraints that its flags should have. - Relay all information given to
kubectl
(via command line args) to plugins as-is. Plugins receive all arguments and flags provided by users and are responsible for adjusting their behavior accordingly. - Provide a way to limit which command paths can and cannot be overridden by plugins in the command tree.
- The new plugin mechanism will not be a "plugin installer" or wizard. It will not have specific or baked-in knowledge regarding a plugin's location or composition, nor will it provide a way to download or unpack plugins in a correct location.
- Plugin discovery is not a main focus of this mechanism. As such, it will not attempt to collect data about every plugin that exists in an environment.
- Plugin management is out of the scope of this design. A mechanism for updating and managing lifecycle of existing plugins should be covered as a separate design (See kubernetes/community#2340).
- Provide a standard package of common cli utilities that is consumed by
kubectl
and plugins alike. This should be done as an independent effort of this plugin mechanism.
- Developer wants to create and expose a plugin to
kubectl
. They use a programming language of their choice and create an executable file. The executable's filename consists of the command path to implement, and is prefixed withkubectl-
. The executable file is placed on the user'sPATH
.
The proposed design passes through all environment variables, flags, input, and output streams exactly as they are given
to the parent kubectl
process. This has the effect of letting plugins run without the need for any special parsing
or case-handling in kubectl
.
In essence, a plugin binary must be able to run as a standalone process, completely independent of kubectl
.
- When
kubectl
is executed with a subcommand foo that does not exist in the command tree, it will attempt to look for a filenamekubectl-foo
(kubectl-foo.exe
on Windows) in the user'sPATH
and execute it, relaying all arguments given as well as all environment variables to the plugin child-process.
A brief example (not an actual prototype) is provided below to clarify the core logic of the proposed design:
// treat all args given by the user as pieces of a plugin binary's filename
// and short-circuit once we find an arg that appears to be a flag.
remainingArgs := []string{} // all "non-flag" arguments
for idx := range cmdArgs {
if strings.HasPrefix(cmdArgs[idx], "-") {
break
}
remainingArgs = append(remainingArgs, strings.Replace(cmdArgs[idx], "-", "_", -1))
}
foundBinaryPath := ""
// find binary in the user's PATH, starting with the longest possible filename
// based on the given non-flag arguments by the user
for len(remainingArgs) > 0 {
path, err := exec.LookPath(fmt.Sprintf("kubectl-%s", strings.Join(remainingArgs, "-")))
if err != nil || len(path) == 0 {
remainingArgs = remainingArgs[:len(remainingArgs)-1]
continue
}
foundBinaryPath = path
break
}
// if we are able to find a suitable plugin executable, perform a syscall.Exec call
// and relay all remaining arguments (in order given), as well as environment vars.
syscall.Exec(foundBinaryPath, append([]string{foundBinaryPath}, cmdArgs[len(remainingArgs):]...), os.Environ())
Under this proposal, kubectl
would identify plugins by looking for filenames beginning with the kubectl-
prefix.
A search for these names would occur on a user's PATH
. Only files that are executable and begin with this prefix
would be identified.
The current implementation details for the proposed design rely on using a plugin executable's name to determine what
command the plugin is adding.
For a given command kubectl foo --bar baz
, an executable kubectl-foo
will be matched on a user's PATH
,
and the arguments --bar baz
will be passed to it in that order.
A potential limitation of this could present itself in the order of arguments provided by a user.
A user could intend to run a plugin kubectl-foo-bar
with the flag --baz
with the following command
kubectl foo --baz bar
, but instead end up matching kubectl-foo
with the flag --baz
and the argument bar
based
on the placement of the flag --baz
.
A notable constraint of this design is that it excludes any form of plugin lifecycle management, or version compatibility. A plugin may depend on other plugins based on the decision of a plugin author, however the proposed design does nothing to facilitate such dependencies. It is up to the plugin's author (or a separate / independent plugin management system) to provide documentation or instructions on how to meet any dependencies required by a plugin.
Further, with the proposed design, plugins that rely on multiple "helper" files to properly function, should provide an
"entrypoint" executable (which is placed on a user's PATH
), with any additional files located elsewhere (e.g. ~/.kubeplugins/myplugin/helper1.py).
Unlike the existing alpha plugin mechanism, the proposed design does not constrain commands added by plugins to exist as subcommands of the
kubectl plugin
design. Commands provided by plugins under the new mechanism can be invoked as first-class commands (/usr/bin/kubectl-foo
provides the kubectl foo
parent command).
A potential risk associated with this could present in the form of a "land-rush" by plugin providers.
Multiple plugin authors would be incentivized to provide their own version of plugin foo
.
Users would be at the mercy of whichever variation of kubectl-foo
is discovered in their PATH
first when executing that command.
A way to mitigate the above scenario would be to have users take advantage of the proposed plugin mechanism's design by renaming multiple variations of kubectl-foo
to include the provider's name, for example: kubectl-acme-foo
, or kubectl-companyB-foo
.
Conflicts such as this one could further be mitigated by a plugin manager, which could perform conflict resolution among similarly named plugins on behalf of a user.
- Make this mechanism a part of
kubectl
's command-lookup logic.
This plugin design closely follows major aspects of the plugin system design for git
.
Implementing this design could potentially conflict with any ongoing work that depends on the current alpha plugin system.
The proposed design is flexible enough to accommodate future updates that could allow certain command paths to be overwritten or extended (with the addition of subcommands) via plugins.