Skip to content

Commit

Permalink
update plugin documentation under docs>tasks>extend-kubectl
Browse files Browse the repository at this point in the history
  • Loading branch information
juanvallejo committed Sep 27, 2018
1 parent 26f0a81 commit cb9dc1f
Showing 1 changed file with 175 additions and 67 deletions.
242 changes: 175 additions & 67 deletions content/en/docs/tasks/extend-kubectl/kubectl-plugins.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
title: Extend kubectl with plugins
reviewers:
- fabianofranz
- juanvallejo
- soltysh
description: With kubectl plugins, you can extend the functionality of the kubectl command by adding new subcommands.
content_template: templates/task
---
Expand All @@ -10,131 +11,238 @@ content_template: templates/task

{{< feature-state state="alpha" >}}

This guide shows you how to install and write extensions for [kubectl](/docs/user-guide/kubectl/). Usually called *plugins* or *binary extensions*, this feature allows you to extend the default set of commands available in `kubectl` by adding new subcommands to perform new tasks and extend the set of features available in the main distribution of `kubectl`.
This guide shows you how to install and write extensions for [kubectl](/docs/reference/kubectl/kubectl/). By thinking of core `kubectl` commands as essential building blocks for interacting with a Kubernetes cluster, we can think
of plugins as a means of utilizing these building blocks to create more complex behavior. Plugins extend `kubectl` with new sub-commands, allowing for new and custom features not included in the main distribution of `kubectl`.

{{% /capture %}}

{{% capture prerequisites %}}

You need to have a working `kubectl` binary installed.
{{< note >}}
**Note:** Plugins were officially introduced as an alpha feature in the v1.8.0 release. So, while some parts of the plugins feature were already available in previous versions, a `kubectl` version of 1.8.0 or later is recommended.
**Note:** Plugins were officially introduced as an alpha feature in the v1.8.0 release. They have been re-worked in the v1.12.0 release to support a wider range of use-cases. So, while some parts of the plugins feature were already available in previous versions, a `kubectl` version of 1.12.0 or later is recommended if you are following these docs.
{{< /note >}}

Until a GA version is released, plugins will only be available under the `kubectl plugin` subcommand.
Until a GA version is released, plugins should be considered unstable, and their underlying mechanism is prone to change.

{{% /capture %}}

{{% capture steps %}}

## Installing kubectl plugins

A plugin is nothing more than a set of files: at least a **plugin.yaml** descriptor, and likely one or more binary, script, or assets files. To install a plugin, copy those files to one of the locations in the filesystem where `kubectl` searches for plugins.
A plugin is nothing more than a standalone executable file, whose name begins with `kubectl-`. To install a plugin, simply move this executable file to anywhere on your PATH.

{{< note >}}
**Note:** Kubernetes does not provide a package manager or anything similar to install or update plugins. It is your responsibility to place the plugin files in the correct location. We recommend that each plugin be stored in its own directory so that installing a plugin distributed as a compressed file is as simple as extracting it to one of the locations specified in the [Plugin loader](#plugin-loader) section.
**Note:** Kubernetes does not provide a package manager or anything similar to install or update plugins. It is your responsibility to ensure that plugin executables have a filename that begins with `kubectl-`, and that they are placed somewhere on your PATH.
{{< /note >}}

### Plugin loader
### Discovering plugins

The plugin loader is responsible for searching plugin files in the filesystem locations specified below, and checking if the plugin provides the minimum amount of information required for it to run. Files placed in the right location that don't provide the minimum amount of information, for example an incomplete *plugin.yaml* descriptor, are ignored.
`kubectl` provides a command `kubectl plugin list` that searches your PATH for valid plugin executables.
Executing this command causes a traversal of all files in your PATH. Any files that are executable, and begin with `kubectl-` will show up *in the order in which they are present in your PATH* in this command's output.
A warning will be included for any files beginning with `kubectl-` that are *not* executable.
A warning will also be included for any valid plugin files that overlap each other's name.

#### Search order
#### Limitations

The plugin loader uses the following search order:
It is currently not possible to create plugins that overwrite existing `kubectl` commands. For example, creating a plugin `kubectl-version` will cause that plugin to never be executed, as the existing `kubectl version` command will always take precedence over it. Due to this limitation, it is also *not* possible to use plugins to add new subcommands to existing `kubectl` commands. For example, adding a subcommand `kubectl create foo` by naming your plugin `kubectl-create-foo` will cause that plugin to be ignored. Warnings will appear under the output of `kubectl plugin list` for any valid plugins that attempt to do this.

1. `${KUBECTL_PLUGINS_PATH}` If specified, the search stops here.
2. `${XDG_DATA_DIRS}/kubectl/plugins`
3. `~/.kube/plugins`
## Writing kubectl plugins

You can write a plugin in any programming language or script that allows you to write command-line commands.

If the `KUBECTL_PLUGINS_PATH` environment variable is present, the loader uses it as the only location to look for plugins.
The `KUBECTL_PLUGINS_PATH` environment variable is a list of directories. In Linux and Mac, the list is colon-delimited. In
Windows, the list is semicolon-delimited.
There is no plugin installation or pre-loading required. Plugin executables receive the inherited environment from the `kubectl` binary.
A plugin determines which command path it wishes to implement based on its name. For example, a plugin wanting to provide a new command
`kubectl foo`, would simply be named `kubectl-foo`, and live somewhere in the user's PATH.

If `KUBECTL_PLUGINS_PATH` is not present, the loader searches these additional locations:
### Example plugin

```
#!/bin/bash
# optional argument handling
if [[ "$1" == "version" ]]
then
echo "1.0.0"
exit 0
fi
# optional argument handling
if [[ "$1" == "config" ]]
then
echo $KUBECONFIG
exit 0
fi
echo "I am a plugin named kubectl-foo"
```

First, one or more directories specified according to the
[XDG System Directory Structure](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
specification. Specifically, the loader locates the directories specified by the `XDG_DATA_DIRS` environment variable,
and then searches `kubectl/plugins` directory inside of those.
If `XDG_DATA_DIRS` is not specified, it defaults to `/usr/local/share:/usr/share`.
### Using a plugin

Second, the `plugins` directory under the user's kubeconfig dir. In most cases, this is `~/.kube/plugins`.
To use the above plugin, simply make it executable:

```shell
# Loads plugins from both /path/to/dir1 and /path/to/dir2
KUBECTL_PLUGINS_PATH=/path/to/dir1:/path/to/dir2 kubectl plugin -h
```
sudo chmod +x ./kubectl-foo
```

## Writing kubectl plugins
and place it anywhere in your PATH:

You can write a plugin in any programming language or script that allows you to write command-line commands.
A plugin does not necessarily need to have a binary component. It could rely entirely on operating system utilities
like `echo`, `sed`, or `grep`. Or it could rely on the `kubectl` binary.
```
sudo mv ./kubectl-foo /usr/local/bin
```

The only strong requirement for a `kubectl` plugin is the `plugin.yaml` descriptor file. This file is responsible for declaring at least the minimum attributes required to register a plugin and must be located under one of the locations specified in the [Search order](#search-order) section.
You may now invoke your plugin as a `kubectl` command:

### The plugin.yaml descriptor
```
$ kubectl foo
I am a plugin named kubectl-foo
```

The descriptor file supports the following attributes:
All args and flags are passed as-is to the executable:

```
name: "targaryen" # REQUIRED: the plugin command name, to be invoked under 'kubectl'
shortDesc: "Dragonized plugin" # REQUIRED: the command short description, for help
longDesc: "" # the command long description, for help
example: "" # command example(s), for help
command: "./dracarys" # REQUIRED: the command, binary, or script to invoke when running the plugin
flags: # flags supported by the plugin
- name: "heat" # REQUIRED for each flag: flag name
shorthand: "h" # short version of the flag name
desc: "Fire heat" # REQUIRED for each flag: flag description
defValue: "extreme" # default value of the flag
tree: # allows the declaration of subcommands
- ... # subcommands support the same set of attributes
$ kubectl foo version
1.0.0
```

All environment variables are also passed as-is to the executable:

```bash
$ export KUBECONFIG=~/.kube/config
$ kubectl foo config
/home/<user>/.kube/config

$ KUBECONFIG=/etc/kube/config kubectl foo config
/etc/kube/config
```

The preceding descriptor declares the `kubectl plugin targaryen` plugin, which has one flag named `-h | --heat`.
When the plugin is invoked, it calls the `dracarys` binary or script, which is located in the same directory as the descriptor file. The [Accessing runtime attributes](#accessing-runtime-attributes) section describes how the `dracarys` command accesses the flag value and other runtime context.
Additionally, the first argument that is passed to a plugin will always be the full path to the location where it was invoked (`$0` would equal `/usr/local/bin/kubectl-foo` in our example above).

### Naming a plugin

As seen in the example above, a plugin determines the command path that it will implement based on its filename. Every sub-command in the command path that a plugin targets, is separated by a dash (`-`).
For example, a plugin that wishes to be invoked whenever the command `kubectl foo bar baz` is invoked by the user, would have the filename of `kubectl-foo-bar-baz`.

#### Flags and argument handling

Taking our `kubectl-foo-bar-baz` plugin from the above scenario, we further explore additional cases where users invoke our plugin while providing additional flags and arguments.
For example, in a situation where a user invokes the command `kubectl foo bar baz arg1 --flag=value arg2`, the plugin mechanism will first try to find the plugin with the longest possible name, which in this case
would be `kubectk-foo-bar-baz-arg1`. Upon not finding that plugin, it then treats the last dash-separated value as an argument (`arg1` in this case), and attempts to find the next longest possible name, `kubectl-foo-bar-baz`.
Upon finding a plugin with this name, it then invokes that plugin, passing all args and flags after its name to the plugin executable.

Example:

```bash
# create a plugin
$ echo '#!/bin/bash\n\necho "My first command-line argument was $1"' > kubectl-foo-bar-baz
$ sudo chmod +x ./kubectl-foo-bar-baz

### Recommended directory structure
# "install" our plugin by placing it on our PATH
$ sudo mv ./kubectl-foo-bar-baz /usr/local/bin

It is recommended that each plugin has its own subdirectory in the filesystem, preferably with the same name as the plugin command. The directory must contain the `plugin.yaml` descriptor and any binary, script, asset, or other dependency it might require.
# ensure our plugin is recognized by kubectl
$ kubectl plugin list
The following kubectl-compatible plugins are available:

For example, the directory structure for the `targaryen` plugin could look like this:
/usr/local/bin/kubectl-foo-bar-baz

# test that calling our plugin via a "kubectl" command works
# even when additional arguments and flags are passed to our
# plugin executable by the user.
$ kubectl foo bar baz arg1 --meaningless-flag=true
My first command-line argument was arg1
```
~/.kube/plugins/
└── targaryen
├── plugin.yaml
└── dracarys

As you can see, our plugin was found based on the `kubectl` command specified by a user, and all extra arguments and flags were passed as-is to the plugin executable once it was found.

#### Names with dashes and underscores

Although the `kubectl` plugin mechanism uses the dashes (`-`) in plugin filenames to determine the sequence of sub-commands that should invoke them, it is still possible to create a plugin
command containing dashes in its commandline invocation by using underscores `_` in its filename.

Example:

```bash
# create a plugin containing an underscore in its filename
$ echo '#!/bin/bash\n\necho "I am a plugin with a dash in my name"' > ./kubectl-foo_bar
$ sudo chmod +x ./kubectl-foo_bar

# move the plugin into your PATH
$ sudo mv ./kubectl-foo_bar /usr/local/bin

# our plugin can now be invoked from `kubectl` like so:
$ kubectl foo-bar
I am a plugin with a dash in my name
```

Note that the introduction of underscores to a plugin filename does not prevent us from having commands such as `kubectl foo_bar`.
The command from the above example, can be invoked using either a dash (`-`) or an underscore (`_`):

```bash
# our plugin can be invoked with a dash
$ kubectl foo-bar
I am a plugin with a dash in my name

# it can also be inovked using an underscore
$ kubectl foo_bar
I am a plugin with a dash in my name
```

### Accessing runtime attributes
#### Name conflicts and overshadowing

In most use cases, the binary or script file you write to support the plugin must have access to some contextual information provided by the plugin framework. For example, if you declared flags in the descriptor file, your plugin must have access to the user-provided flag values at runtime. The same is true for global flags. The plugin framework is responsible for doing that, so plugin writers don't need to worry about parsing arguments. This also ensures the best level of consistency between plugins and regular `kubectl` commands.
It can be possible to have multiple pluins with the same filename in different locations throughout your PATH.
For example, given a PATH with the following value: `PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins`, a copy of plugin `kubectl-foo` could exist in `/usr/local/bin/plugins` and `/usr/local/bin/moreplugins`,
such that the output of the `kubectl plugin list` command is:

Plugins have access to runtime context attributes through environment variables. So to access the value provided through a flag, for example, just look for the value of the proper environment variable using the appropriate function call for your binary or script.
```
$ PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins kubectl plugin list
The following kubectl-compatible plugins are available:
/usr/local/bin/plugins/kubectl-foo
/usr/local/bin/moreplugins/kubectl-foo
- warning: /usr/local/bin/moreplugins/kubectl-foo is overshadowed by a similarly named plugin: /usr/local/bin/plugins/kubectl-foo
error: one plugin warning was found
```

In the above scenario, the warning under `/usr/local/bin/moreplugins/kubectl-foo` tells us that this plugin will never be executed. Instead, the executable that appears first in our PATH, `/usr/local/bin/plugins/kubectl-foo`, willalways be found and executed first by the `kubectl` plugin mechanism.

A way to resolve this issue is to ensure that the location of the plugin that you wish to use with `kubectl` always comes first in your PATH. For example, if we wanted to always use `/usr/local/bin/moreplugins/kubectl-foo` anytime that the `kubectl` command `kubectl foo` was invoked, we would simply change the value of our PATH to be `PATH=/usr/local/bin/moreplugins:/usr/local/bin/plugins`.

The supported environment variables are:
### Checking for plugin warnings

* `KUBECTL_PLUGINS_CALLER`: The full path to the `kubectl` binary that was used in the current command invocation.
As a plugin writer, you don't have to implement logic to authenticate and access the Kubernetes API. Instead, you can invoke `kubectl` to obtain the information you need, through something like `kubectl get --raw=/apis`.
You can use the aforementioned `kubectl plugin list` command to ensure that your plugin is visible by `kubectl`, and verify that there are no warnings preventing it from being called as a `kubectl` command.

```
$ kubectl plugin list
The following kubectl-compatible plugins are available:
test/fixtures/pkg/kubectl/plugins/kubectl-foo
/usr/local/bin/kubectl-foo
- warning: /usr/local/bin/kubectl-foo is overshadowed by a similarly named plugin: test/fixtures/pkg/kubectl/plugins/kubectl-foo
plugins/kubectl-invalid
- warning: plugins/kubectl-invalid identified as a kubectl plugin, but it is not executable
error: 2 plugin warnings were found
```

* `KUBECTL_PLUGINS_CURRENT_NAMESPACE`: The current namespace that is the context for this call. This is the actual namespace to be used, meaning it was already processed in terms of the precedence between what was provided through the kubeconfig, the `--namespace` global flag, environment variables, and so on.
### Using the command line runtime package

* `KUBECTL_PLUGINS_DESCRIPTOR_*`: One environment variable for every attribute declared in the `plugin.yaml` descriptor.
For example, `KUBECTL_PLUGINS_DESCRIPTOR_NAME`, `KUBECTL_PLUGINS_DESCRIPTOR_COMMAND`.
As part of the plugin mechanism update in the v1.12.0 release, an additional set of utilities have been made available to plugin authors. These utilities
exist under the [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) repository, and can be used by plugins written in Go to parse and update
a user's KUBECONFIG file, obtain REST clients to talk to the API server, and automatically bind flags associated with configuration and printing.

* `KUBECTL_PLUGINS_GLOBAL_FLAG_*`: One environment variable for every global flag supported by `kubectl`.
For example, `KUBECTL_PLUGINS_GLOBAL_FLAG_NAMESPACE`, `KUBECTL_PLUGINS_GLOBAL_FLAG_V`.
Plugins *do not* have to be written in Go in order to be recognized as valid plugins by `kubectl`, but they do have to use Go in order to take advantage of
the tools and utilities in the CLI Runtime repository.

* `KUBECTL_PLUGINS_LOCAL_FLAG_*`: One environment variable for every local flag declared in the `plugin.yaml` descriptor. For example, `KUBECTL_PLUGINS_LOCAL_FLAG_HEAT` in the preceding `targaryen` example.
See the [Sample CLI Plugin](https://github.com/kubernetes/sample-cli-plugin) for an example usage of the tools provided in the CLI Runtime repo.

{{% /capture %}}

{{% capture whatsnext %}}

* Check the repository for [some more examples](https://github.com/kubernetes/kubernetes/tree/release-1.11/pkg/kubectl/plugins/examples) of plugins.
* Check the Sample CLI Plugin repository for [a detailed example](https://github.com/kubernetes/sample-cli-plugin) of a plugin written in Go.
* In case of any questions, feel free to reach out to the [CLI SIG team](https://github.com/kubernetes/community/tree/master/sig-cli).
* Binary plugins is still an alpha feature, so this is the time to contribute ideas and improvements to the codebase. We're also excited to hear about what you're planning to implement with plugins, so [let us know](https://github.com/kubernetes/community/tree/master/sig-cli)!

Expand Down

0 comments on commit cb9dc1f

Please sign in to comment.