A small Go wrapper around kubectl that makes everyday cluster work nicer:
- Decodes secrets automatically on
-o yamloutput. - Cleans up YAML by stripping the noise Kubernetes injects (
uid,creationTimestamp,status), so output is paste-ready for a manifest. - Lists pods with their init containers, containers, and images.
- Client-side api-server load balancing — point a single context at a
comma-delimited list of api-server endpoints and
kelperwill fail over to the next live one automatically.
Grab the latest binary for your OS/arch from the Releases page, then:
chmod +x kelper-linux-amd64
sudo mv kelper-linux-amd64 /usr/local/bin/kelper
alias k=kelper # optional
k --helpdocker pull ghcr.io/jthunderbird/kelper:latest
docker run --rm -v ~/.kube:/root/.kube ghcr.io/jthunderbird/kelper:latest get pods -AThe image bundles kubectl, so it works standalone.
git clone https://github.com/jthunderbird/kelper.git
cd kelper
go build -o kelper ./cmd/kelperkelper shells out to kubectl, so kubectl must be on your PATH (the
container image already includes it).
kelper has a few native commands and otherwise passes input straight through
to kubectl. For get ... -o yaml, it intercepts output to decode secrets and
tidy YAML:
kelper <command> [args] # run a native command
kelper <any kubectl args> # anything else is forwarded to kubectl
kelper --kubeconfig <path> <args> # use a specific kubeconfig| Command | Description |
|---|---|
list-pods |
List pods with their init containers, containers, and images. |
kubectl |
Run a raw kubectl command (explicit passthrough; use -- to lead with flags). |
version |
Print the kelper version. |
help |
Show help for kelper or a specific command. |
| Global flag | Default | Description |
|---|---|---|
--kubeconfig |
discovery* | Path to the kubeconfig file (comma-delimited servers for failover). |
-h, --help |
Show help. |
* When unset, the standard KUBECONFIG env var / ~/.kube/config discovery
is used.
Every command supports --help, and kelper help <command> prints the same
detail:
$ kelper help list-pods
list-pods - List pods with their init containers, containers, and images
USAGE:
kelper list-pods [flags]
FLAGS:
-n, --namespace string Namespace to list pods from (default "default")
--kubeconfig string Path to the kubeconfig file
-h, --help Show this help
EXAMPLES:
kelper list-pods
kelper list-pods -n kube-systemAny get secret ... -o yaml has its data base64-decoded in place:
$ kelper get secret -n kiali grafana-auth -o yaml
apiVersion: v1
kind: Secret
metadata:
name: grafana-auth
namespace: kiali
data:
password: prom-operatorFor any non-secret get ... -o yaml, kelper removes the auto-mutated fields
(metadata.uid, metadata.creationTimestamp, status) so the result is ready
to drop into a YAML file:
$ kelper get po -n flux-system helm-controller-678f5576df-g7scx -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
labels:
app: helm-controller
name: helm-controller-678f5576df-g7scx
namespace: flux-system
spec:
containers:
- image: docker.io/fluxcd/helm-controller:v1.4.0
name: manager
...$ kelper list-pods -n kyverno
Pod: kyverno-admission-controller-5d8986c8b6-2g7gr, Namespace: kyverno
Init Containers:
Name: kyverno-pre, Image: registry1.dso.mil/ironbank/opensource/kyverno/kyvernopre:v1.13.4
Containers:
Name: kyverno, Image: registry1.dso.mil/ironbank/opensource/kyverno:v1.13.4kubectl accepts one — and only one — server: per cluster in a kubeconfig.
If that endpoint is down, you are stuck. kelper lifts that limit: list multiple
api-server endpoints, comma-delimited, in the server: field, and kelper
probes them in order and uses the first one that is reachable.
# ~/.kube/config
apiVersion: v1
kind: Config
clusters:
- name: prod
cluster:
certificate-authority-data: <ca>
# comma-delimited list of api-server endpoints
server: https://10.0.0.1:6443,https://10.0.0.2:6443,https://10.0.0.3:6443
contexts:
- name: prod
context:
cluster: prod
user: prod-admin
current-context: prod
users:
- name: prod-admin
user:
client-certificate-data: <cert>
client-key-data: <key>On each invocation kelper:
- Reads the current context's
serverfield and splits it on commas. - Probes each endpoint in order (a 2s TCP dial).
- Logs a line to stdout for every endpoint that is down and moves on.
- Uses the first reachable endpoint by rewriting a temporary single-server
kubeconfig and handing it to
kubectl/ the client. - If every endpoint is exhausted, exits non-zero with a
not connectederror.
Example, first endpoint down:
$ kelper get pods -n flux-system
api-server https://10.0.0.1:6443 unreachable (dial tcp 10.0.0.1:6443: i/o timeout); trying next endpoint...
api-server https://10.0.0.2:6443 reachable; using it
NAME READY STATUS RESTARTS AGE
helm-controller-678f5576df-g7scx 1/1 Running 0 20hAll endpoints down:
$ kelper get pods
api-server https://10.0.0.1:6443 unreachable (dial tcp 10.0.0.1:6443: i/o timeout); trying next endpoint...
api-server https://10.0.0.2:6443 unreachable (dial tcp 10.0.0.2:6443: i/o timeout); trying next endpoint...
api-server https://10.0.0.3:6443 unreachable (dial tcp 10.0.0.3:6443: i/o timeout); trying next endpoint...
not connected: all 3 api-server endpoints unreachableA single (non-delimited) server: value behaves exactly as before — no probing,
no temp kubeconfig.
Every push to main runs the release
workflow, which:
- cross-compiles
kelperfor linux/macOS (amd64 + arm64) and windows (amd64) and attaches them to a GitHub Release, and - builds and pushes the container image to
ghcr.io/jthunderbird/kelper(:latest, the release tag, and the commit SHA).
See LICENSE.
