Hacktoberfest is almost over but since there’re plenty of opportunities to contribute, I decided to takeover the task of re-writing a kubectl plugin called view-secret. For those of you who are not familar with kubectl, it’s the CLI tool to work with Kubernetes. In this post I’d like to shed some light on krew and what’s necessary to create your very own plugin. Okay, so what’s krew?

Krew is one of the many Kubernetes Special Interest Groups (SIG) and aims at solving the package management issue for kubectl. There’s a limited amount of core functionality that ships with kubectl so krew is all about allowing developers to create their own extensions and contribute them back to the community. All available plugins are stored in the krew-index, a central repo that plugin maintainers use to publish/update their plugins. If you haven’t used krew before, make sure to install it first.

Some early plugins have been written in Bash and the maintainer was asking the community to take over the re-write in Go/Python and ultimately the maintenance of the plugins. Since my day job requires me to create & manage Kubernetes-based deployments, I find myself in need of decoding secrets all the time. A secret - as the name may reveal already - is used to store secret information Base64 encoded. This resource type is often used to populate environment configuration for deployments, to store docker registry auth information or tls secrets.

The typical workflow to decode a secret without view-secret looks as follows:

  1. kubectl get secret <secret> -o yaml
  2. Copy base64 encoded secret
  3. echo "b64string" | base64 -d

This gets quite cumbersome especially if you just want to check the entirety of a secret to see if everything looks ok. There are solutions like kubedecode or the previous view-secret implementation that aim at solving this problem but lack either native kubectl integration, are outdated/not maintained anymore or require you to always provide e.g. the namespace as a parameter.

So I went ahead and created a new implementation for view-secret that is backward-compatible to the existing implementation but also adds a new capability, namely decoding all contents of a secret. My contribution has been accepted and the plugin is available now, so let me walk you through the process.

As it turns out, creating your own plugin is super simple and well documented here. All you have to do is create a binary with the prefix kubectl-, make it executable and place it somewhere in your $PATH. A sample plugin can be as easy as this:

# kubectl-hello plugin
cat <<EOF > kubectl-hello
#!/usr/bin/env bash
echo "hello from krew"
EOF

# Make executable
chmod +x kubectl-hello

# Copy into some $PATH location
cp kubectl-hello /usr/local/bin

# Run plugin
kubectl hello
## prints "hello from krew"

Since my language of choice for this project was Go, I created a new project and added integrations such as GoReleaser to simplify shipping the binary for mulitple platforms and Travis CI to automate running builds/creating releases. To simplify the build/test process I also added a Makefile. At this point my project repo had the following layout:

cmd
  kubectl-view-secret.go
pkg
  cmd
    view-secret.go
    view-secret_test.go
go.mod
go.sum
.goreleaser.yml
.travis.yml
Makefile

The established workflow was pretty straight-forward:

  1. push changes to master –> triggers travis to run tests
  2. tag commit –> triggers travis to use goreleaser

I previously wrote about the usage of Makefiles in Go projects but for this project the targets are much simpler:

SOURCES := $(shell find . -name '*.go')
BINARY := kubectl-view-secret

build: kubectl-view-secret

test: $(SOURCES)
  go test -v -short -race -timeout 30s ./...

$(BINARY): $(SOURCES)
  CGO_ENABLED=0 go build -o $(BINARY) -ldflags="-s -w" ./cmd/$(BINARY).go

For the actual implementation I used spf13/cobra to parse flags and process user input. To get the secret contents I use exec.Command thus shelling out to the OS instead of using the kubernetes go client or cli runtime as they add a huge overhead for such a small functionality.

After I finished the implementation, all I had to do was update the plugins/view-secret.yaml spec in the krew index to use my new plugin. This meant changing the plugin description, the download links for the new binaries and the sha256 checksums. Once the Pull Request got merged, the local plugin index had to be updated via kubectl krew update and the plugin can be installed via kubectl krew install view-secret.

Now the workflow to decode secrets is as simple as this:

# print secret keys
kubectl view-secret <secret>

# decode specific entry
kubectl view-secret <secret> <key>

# decode all secret contents
kubectl view-secret <secret> -a/--all

# print keys for secret in different namespace
kubectl view-secret <secret> -n/--namespace foo

# suppress info output
kubectl view-secret <secret> -q/--quiet

This was my first CNCF contribution & I’m happy about the feedback I got from @ahmetb & @corneliusweig throughout the process.

The full plugin code is available on GitHub.

Thanks for reading! As always please reach out if you have any questions. :wave: