Skip to content

Latest commit

 

History

History
234 lines (179 loc) · 11 KB

0024-kubectl-plugins.md

File metadata and controls

234 lines (179 loc) · 11 KB
kep-number title authors owning-sig participating-sigs reviewers approvers editor creation-date last-updated status see-also replaces superseded-by
24
Kubectl Plugins
@juanvallejo
sig-cli
sig-cli
@pwittrock
@deads2k
@liggitt
@soltysh
@pwittrock
@soltysh
juanvallejo
2018-07-24
2018-08-09
provisional
n/a
n/a

Kubectl Plugins

Table of Contents

Summary

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.

Motivation

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.

Limitations of the Existing Design

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.

Goals

  • 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 with kubectl). 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 command kubectl 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.

Non-Goals

  • 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.

Proposal

Scenarios

  • 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 with kubectl-. The executable file is placed on the user's PATH.

Implementation Details/Design

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 filename kubectl-foo (kubectl-foo.exe on Windows) in the user's PATH 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())

Naming Conventions

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.

Implementation Notes/Constraints

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).

Risks and Mitigations

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.

Graduation Criteria

  • Make this mechanism a part of kubectl's command-lookup logic.

Implementation History

This plugin design closely follows major aspects of the plugin system design for git.

Drawbacks

Implementing this design could potentially conflict with any ongoing work that depends on the current alpha plugin system.

Future Improvements/Considerations

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.