Extending kubectl

Kubectl

*Kubectl Plugin*

As you probably already know, kubectl is the official tool to interact with Kubernetes from the command line. This tool, besides all the functionality that it already provides, allows us to extend its functionality through plugins.

A kubectl plugin is nothing but a file with the following three requirements:

  • It has to be an executable (binary or script)
  • It must be in your system’s PATH
  • Its name must start with kubectl- (including the dash!)

The plugin system in kubectl was introduced as alpha in version 1.8.0 and it was rewritten in version 1.12.0, which is the minimum version recommended if you are going to play with this feature.

Let’s write our first plugin. It will be a bash script named kubectl-hello. This is its content:

#!/bin/bash

echo "Hello, World!"

Prefixing the file name with kubectl- is one of the requirements. Another requirement is to make it an executable:

chmod +x kubectl-hello

The last requirement is to make sure the file is somewhere in the PATH. You can either copy the file to a directory that is already in the PATH, or make the directory you created the file in, part of the PATH. In our case, we’ll take the second approach:

export PATH=$PATH:~/tmp/kplugin

This command only takes effect in the session where you run it. Once that session is closed (or opening a different terminal), your directory will no longer be part of your PATH. To make it persistent you will need to add that command to your ~/.bashrc, ~/.zshrc or something similar.

Now we can run our plugin:

kubectl hello
Hello, World!

Let’s create another plugin, a little bit more useful this time. In this case will also be a bash script. This plugin will generate a configuration file with the given account’s credentials (in this case a service account). This is pretty handy when you need to interact with a Kubernetes cluster from, let’s say your CI/CD server. In order to get your server access to the cluster, you will need to provide some type of credential. So you can use this plugin to create the configuration file based on a service account with the necessary credentials and cluster information.

Our file will have the following content:

#!/bin/bash

set -e

usage="
USAGE: 
  kubectl kubeconf-generator -a SERVICE_ACCOUNT -n NAMESPACE
"

while getopts a:n: option
do
case "${option}"
in
a) SA=${OPTARG};;
n) NAMESPACE=${OPTARG};;
esac
done

[[ -z "$SA" ]] && { echo "Service account is required" ; echo "$usage" ; exit 1; }
[[ -z "$NAMESPACE" ]] && { echo "Namespace is required" ; echo "$usage" ; exit 1; }

# Get secret name
SECRET_NAME=($(kubectl get sa $SA -n $NAMESPACE -o jsonpath='{.secrets[0].name}'))
  
# Get secret value
SECRET=$(kubectl get secret $SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.token}' | base64 -D)

# Get cluster server name
SERVER=$(kubectl config view --minify -o json | jq -r '.clusters[].cluster.server')
# Get cluster name
CLUSTER_NAME=$(kubectl config view --minify -o json | jq -r '.clusters[].name')

# Get cluster certs
CERTS=$(kubectl config view --raw --minify -o json | jq -r '.clusters[].cluster."certificate-authority-data"')

cat << EOM
apiVersion: v1
kind: Config
users:
- name: $SA
  user:
    token: $SECRET
clusters:
- cluster:
    certificate-authority-data: $CERTS
    server: $SERVER
  name: $CLUSTER_NAME
contexts:
- context:
    cluster: $CLUSTER_NAME
    user: $SAS
  name: svcs-acct-context
current-context: svcs-acct-context
EOM

As you can see, this plugin, besides kubectl also uses jp. So to make it work, you need to have such tool installed as well.

The plugin expects two parameters: the service account and the namespace for such account.

Let’s dump the content listed above into a file named kubectl-kubeconf_generator. Pay attention to the underscore character. We’ll come back to this later.

Let’s run our plugin without any parameters:

kubectl kubeconf_generator
Service account is required

USAGE:
  kubectl-kubeconf_generator -a SERVICE_ACCOUNT -n NAMESPACE

Now let’s call it with the service account (default) and namespace (default). Now this time the plugin will try to fetch some information from the cluster defined in your current context. In this case I’m using minikube (if you are too, make sure it is up and running):

kubectl kubeconf_generator -a default -n default
apiVersion: v1
kind: Config
users:
- name: default
  user:
    token: REDACTADO
clusters:
- cluster:
    certificate-authority-data: null
    server: https://192.168.99.110:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user:
  name: svcs-acct-context
current-context: svcs-acct-context

The output is a configuration file we could use to provide access to our minikube using the default service account from the default namespace (with whatever permissions granted to that account).

Let’s go back to the name of the file. Remember, in our case we named our file as kubectl-kubeconf_generator. When kubectl sees an underscore character in the plugin name, it will allow us to call the plugin either with the underscore or a dash:

kubectl kubeconf_generator -a default -n default
...

o kubectl kubeconf-generator -a default -n default …

Now, if you rename our file to kubectl-kubeconf-generator (replace the underscore with a dash), kubectl recognizes as a command and subcommand. So to call it this time, we will have to have a blank space between kubeconf and generator:

kubectl kubeconf generator -a default -n default
...

Following this pattern we can create subcommands for our plugin command. For instance, imagine we want to be able to generate two different output formats: yaml and json. We could create two files:

kubectl-kubeconf-generator-yaml
kubectl-kubeconf-generator-json

This way to invoke them:

kubectl kubeconf generator json ...
...
kubectl kubeconf generator yaml ...
...

This example is not the best way to customize the output. For that you may want to use a parameter, in this case probably -o to align with kubectl “standards”.

To wrap up, it is worth to mention that you can’t override an existing kubectl command. For instance, kubectl provides the version command, so if you create a new plugin and named kubectl-version, when you call kubectl version, the internal version command will be called and not your plugin. Also there are some rules about name conflicts, when you have two plugins with the same name in different directories, etc, but I’m not going to touch on those rules in this entry. You can always consult the official documentation linked at the beginning of this post.

Last but not least, I just want to mention that kubectl has a plugin command with the list option which will show us the kubectl plugins in our system:

kubectl plugin list
The following compatible plugins are available:

/Users/tuxotron/tmp/kplugin/kubectl-hello
/Users/tuxotron/tmp/kplugin/kubectl-kubeconf-generator

In a future entry I will talk about a cleaner way to handle and install plugins.