Kubectl Plugins in just 3 steps

Introduction

Many times to fullfill our daily tasks we find ourselves in the situation of having to build our own tools to solve specific problems or situations. In this note I am going to show you how easy it is to create a plugin for kubectl.

A plugin is simply an executable program that allows you to extend the functionality of kubectl, allowing us to implement our own subcommands.
It is important to mention that we can code it in the language we like the most: Bash, Python, Go, etc…

What do we need?

There are certain conditions that we must meet for our plugin to work:

  1. The file name must begin with: kubectl- and must not contain an extension.
  2. It must be an executable file.
  3. It should be located in our $PATH and this will be all the installation it would require. Pretty simple, no?

We can list available plugins on our system using:

 

kubectl plugin list

This command will search on our PATH for all executable files that meet the required conditions.

Let’s do it!

Create a test environment

Let’s make a simple plugin, more entertaining than the trivial «Hello World!», that sends the logs of all running pods to a single output to be able to view this output jointly.

To make it more entertaining we will add the name of the pod in colors for each log line in the output. And we can also select with the numeric keys from 1 to N (where N is the nth pod: 1 on-the-fly and see only the output of that pod.

And we will do all this in Bash!

For this demo we are going to use a docker image nginx-log-generator that generates Nginx fake-logs. We will first create a kubernetes deployment that will contain the pods. We create a new file called fake-logs.yaml with the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-fake-logger
spec:
  template:
    metadata:
      name: logger-1
      labels:
        app: logger
    spec:
      containers:
        - name: logger
          image: kscarlett/nginx-log-generator
  replicas: 3
  selector:
    matchLabels:
      app: logger

Apply the deployment:

kubectl create -f fake-logs.yaml

Let’s check that our deploy is working:

$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
my-fake-logger-66dcffbccd-6kx8b   1/1     Running   4          6d2h
my-fake-logger-66dcffbccd-7q4tm   1/1     Running   1          3d1h
my-fake-logger-66dcffbccd-cff4s   1/1     Running   1          3d1h

$ kubectl get deploy
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
my-fake-logger   3/3     3            3           6d2h

Create the plugin:

First: We will create our plugin file named kubectl-demo with the following script.

#!/bin/bash
trap ctrl_c INT
function ctrl_c() {
echo "**************************************** Bye Bye ****************************************"
for pid in ${PIDS[@]}
do
kill -TERM $pid 
done
rm $NAMED_PIPE
rm $sync
exit 0
}
function colorize() {
pod=$1
counter=$2
# colors from 31 to 39
pre_colour="\033[3${counter}m"
post_colour="\033[0m"
if [ "$colorize_output" = true ]
then
colour_pod="${pre_colour}[${pod}]${post_colour}"
else
colour_pod="[${pod}]"
fi
}
function show_logs() {
local sync="$1"
grep -E -f $sync $NAMED_PIPE &
}
function banner() {
echo ""
echo "==================================================="
echo "+ Showing logs for:"
echo "+ $1"
echo "==================================================="
echo ""
}
function start_log() {
show_logs $sync 
shl_pid=$!
disown $shl_pid
PIDS+=$shl_pid" "  
}
function usage() {
echo "~~~~~~~~~~~"
echo " U S A G E"
echo "~~~~~~~~~~~"
echo "Usage: kubectl demo [option]"
echo "  options:"
echo "    --no-colorize: no colorize [default: colorize]"
echo "    -h: Show this help"
echo ""
echo "When running you can use the numbers 1 to N to filter N-pod and only show it's output."
echo "0 resets and shows all outputs again"
echo ""
}
NAMED_PIPE="/tmp/my_named_pipe"
PODS=$(kubectl get pods --no-headers=true -o=custom-columns=NAME:.metadata.name)
TOTAL_PODS=$(echo $PODS | sed -s 's/ /n/g' | wc -l)
colorize_output=true
if [[ $@ ]]; then
case "$@" in
"--no-colorize")
colorize_output=false
;;
"-h")
usage
exit 1
;;
*) echo "Invalid option"
exit 1
;;
esac
fi
# create named pipe
if [ ! -p $NAMED_PIPE ]
then
mkfifo $NAMED_PIPE
chmod a+rw $NAMED_PIPE
fi
PIDS=()
declare -A pods_index
counter=1
for pod in $(echo $PODS)
do
colour_pod=""
colorize $pod $counter
kubectl logs -f $pod | awk -v pod_name=$colour_pod '{print pod_name" "$0}' > $NAMED_PIPE &
PIDS+=$!" " # save all PIDs
pods_index[$counter]=$pod
counter=$((counter+1))
done
# Trick: Shared memory segment for inter process comunication. 
sync=/dev/shm/syntest-$$  # allocate a shared memory segment name for this pid
echo '' > $sync           # init the shm
start_log
input="*"
re='^[0-9]+$' # we match only numbers
while true
do
read -t 0.25 -N 1 input
if  [[ $input =~ $re ]]  && [ "$input" -ge "0" ] && [ "$input" -le "$TOTAL_PODS" ]
then
if [ "$input" -eq "0" ]
then
banner "All Pods"
echo $ > $sync    # grep everything
else
banner ${pods_index[$input]}
echo ${pods_index[$input]} > $sync  # grep only pod name
fi
kill -SIGTERM $shl_pid
PIDS=$(echo $PID | sed -e "s/$shl_pid//g" | tr -s " ")  # remove unused pid
start_log
fi
done

Second: Set execution permissions

chmod +x kubectl-demo

Third: And last step, we need our script to be accessible from our PATH.

ln -s $PWD/kubectl-demo /usr/local/bin/kubectl-demo

From now on, and with nothing else to do, we can make use of our newly created plugin.

We can check that kubectl is ready to use it by listing the plugins it finds:

$ kubectl plugin list
The following compatible plugins are available:
/home/user/kubectl-demo

Let’s test it!

We show the possible options:

$ kubectl demo -h
~~~~~~~~~~~
U S A G E
~~~~~~~~~~~
Usage: kubectl demo [option]
options:
--no-colorize: no colorize [default: colorize]
-h: Show this help
When running you can use the numbers 1 to N to filter N-pod and only show it's output.
0 resets and shows all outputs again

As an example of how we can pass parameters to our sub-commands, we have added the option to allow not to paint the output.

Outputs

Default:

[my-fake-logger-66dcffbccd-7q4tm] 214.216.236.37--[02/Jan/2021:10:17:17+0000] 
"GET/moderator/Reduced.cssHTTP/1.1"....
[my-fake-logger-66dcffbccd-6kx8b] 132.56.98.34--[02/Jan/2021:10:19:22+0000] 
"GET/standardization.svgHTTP/1.1"2001908 
[my-fake-logger-66dcffbccd-cff4s] 159.240.103.70--[02/Jan/2021:10:24:45+0000] 
"GET/Virtual.htmHTTP/1.1"2002288"-"....

We can choose which POD want to display with the numbers 1 to 3 in this case:

# 1
===================================================
+ Showing logs for:
+ my-fake-logger-66dcffbccd-6kx8b
===================================================
[my-fake-logger-66dcffbccd-6kx8b]  16.211.208.91 - - [02/Jan/2021:10:34:33 +0000]  "GET /bottom-line%20Advanced/Upgradable/intermediate.svg HTTP/1.1" ....

And we can reset too default view by using number 0 and have all the logs again:

===================================================
+ Showing logs for:
+ All Pods
===================================================
[my-fake-logger-66dcffbccd-7q4tm] 214.216.236.37--[02/Jan/2021:10:17:17+0000] "GET/moderator/Reduced.cssHTTP/1.1"30143"-"....
[my-fake-logger-66dcffbccd-6kx8b]  16.211.208.91 - - ....

As we can see, the script is pretty simple and I invite you to try all its options, modify it to your liking and extend its functionality.

Conclusion

We saw that it is extremely easy to create a kubectl plugin, it is enough to program what our imagination wants and comply with the executable name and location.

Because of this facility, there is no excuse for not making a plugin for practically every specific need. It is also necessary to comment that there is a large number of plugins provided by the community. It’s worth taking a look!

I hope this note has helped you to learn something new and continue to expand your knowledge.

I invite you if you need information about the DevOps world or Kubernetes, contact us and keep checking our blog to find other useful publications.

Leave a Reply

Your email address will not be published. Required fields are marked *