KSTool is a terminal UI for managing Kubernetes Jobs. It lists jobs in a namespace, lets you delete them, exec into their pods, view their manifests, and create new ones from a templated YAML — all without leaving the terminal.
It ships with EIDF-flavoured defaults (namespace, user label, GPU list) but
every cluster-specific value lives in ~/.kstool/config.yaml, so it works
against any cluster you can reach with kubectl.
- Async job dashboard: status, age, duration, pod count, GPU model and count.
- Filter by status (
all/running/failed/pending) and owner (all/mine). - Sort by age, duration, GPU count, or GPU type.
- Delete a job and exec into its pod, both gated on the
eidf/user-style ownership label. - View any job's live manifest read-only in
$EDITOR. - Create jobs from a templated YAML, with dropdowns for GPU product and
priority class. Server-side
--dry-runruns before the real apply. - Save and reuse env-var presets under
~/.kstool/env_config_list/. - Structured audit log at
~/.kstool/kstool.log(also best-effort to syslog).
- Go 1.21 or later (build only).
- A reachable Kubernetes cluster. KSTool tries in-cluster config first, then
$KUBECONFIG, then~/.kube/config. - An editor — defaults to
vim, override via$EDITOR.
kubectl and envsubst are not required at runtime: KSTool talks to the
API directly with client-go and renders templates in pure Go.
git clone https://github.com/Suchun-sv/KSTool.git
cd KSTool
go build -o kstool ./cmd/kstool
./kstoolFor a static binary that you can scp to a remote host:
CGO_ENABLED=0 go build -ldflags="-s -w" -o kstool ./cmd/kstoolIf you keep an SSH alias eidf (or any host), build.sh does a static
build and atomic-replace deploy in one go:
./build.shThe script scps the binary as kstool.new and then mvs it into place
on the remote, which means an existing kstool that's currently running
won't trigger Text file busy — mv swaps the directory entry; the
running process keeps executing on its old inode until it exits.
Pre-built release tarballs will return once a v2 release tag is cut.
KSTool keeps everything under ~/.kstool/. The directory and its contents
are created on first run with 0600/0700 permissions and atomic writes.
| Path | Purpose |
|---|---|
~/.kstool/config.yaml |
Tenancy: namespace, user_label, gpu_products, priority_classes, base_template_url. |
~/.kstool/base_apply.yaml |
Job template using ${VAR:-default} placeholders. Downloaded once if missing. |
~/.kstool/env_config_list/<name>.yaml |
Saved env-var presets. Names must match [A-Za-z0-9_.-]+. |
~/.kstool/kstool.log |
Audit log of create / delete / exec actions. |
Example config.yaml:
namespace: eidf029ns
user_label: eidf/user
gpu_products:
- NVIDIA-H200
- NVIDIA-H100-80GB-HBM3
- NVIDIA-A100-SXM4-80GB
- NVIDIA-A100-SXM4-40GB-MIG-3g.20gb
priority_classes:
- default-workload-priority
- batch-workload-priority
- short-workload-high-priority
base_template_url: https://raw.githubusercontent.com/Suchun-sv/KSTool/main/config/base_apply.yaml
logs_tail_lines: 2000 # 0 = unlimited
auto_refresh_seconds: 10 # 0 disables background refresh| Key | Action |
|---|---|
↑ / ↓ |
Move selection |
/ |
Filter by job-name substring (Esc clears) |
r |
Refresh now (throttled to 2 s; auto-refresh runs in the background) |
f |
Cycle status filter: All → Running → Failed → Pending |
h |
Toggle "only my jobs" |
s |
Cycle sort: Age↓ → Age↑ → GPU#↑ → GPU#↓ → Dur↓ → Dur↑ → GPU Type↓ → GPU Type↑ |
d |
Delete the selected job (owner-checked, with confirmation) |
e |
Exec into the selected job's running pod |
l |
Open the log viewer |
i |
Describe the job (status + conditions + events) |
c |
View the selected job's manifest in $EDITOR (read-only) |
n |
Open the create-job flow |
? |
Help overlay listing every binding |
q / Esc |
Quit |
Inside the log view: r re-snapshots, f toggles follow mode, / filters
lines by substring, and q / Esc returns to the job list. Inside the
describe view: r refreshes, q / Esc returns. Tail size and auto-refresh
interval are tunable via logs_tail_lines and auto_refresh_seconds in
config.yaml.
The status bar at the top reflects the current filter, owner toggle, and
sort. A ⟳ prefix indicates a refresh in flight.
n opens the saved-preset list:
- Pick Create new configuration or an existing preset.
- Edit env-vars in the form, or press
e(focused on a button) to open the YAML in$EDITORfor bulk edits. - Save writes the preset to
~/.kstool/env_config_list/<name>.yaml. - Apply renders the template, runs a server-side dry-run, then creates the Job. Errors from either step surface in a modal.
The template engine recognises one form: ${VAR:-default}. The first
occurrence's default wins, and braces are matched with depth so defaults can
contain } as long as they're balanced.
metadata:
generateName: ${USER:-default-user}-job-
spec:
template:
spec:
containers:
- image: ${IMAGE_NAME:-nvcr.io/nvidia/pytorch:23.12-py3}
resources:
limits:
nvidia.com/gpu: ${GPU_NUM:-1}
nodeSelector:
nvidia.com/gpu.product: ${GPU_PRODUCT:-NVIDIA-H100-80GB-HBM3}Notes:
USERauto-fills with the current OS user.- The literal substring
default-userinside any default value is replaced with the current user, sodefault-user-ws4becomes<you>-ws4. GPU_PRODUCTandPRIORITY_CLASSrender as dropdowns sourced fromconfig.yaml.- Bash-style
$pid/$!(no:-) are left untouched, so containerargs:scripts survive intact.
A working template lives at config/base_apply.yaml.
cmd/kstool/ main entrypoint (wiring only)
internal/
config/ ~/.kstool layout, atomic IO, schema
k8s/ client-go wrapper (List/Get/Delete/Create/Exec)
template/ pure-Go ${VAR:-default} extract + render
tui/ tview app, jobs view, create flow
model/ Job DTO, GPU parsing, filter/sort enums
editor/ $EDITOR shell-out
log/ slog + syslog audit log
Common commands:
go vet ./...
go test ./...
go build ./cmd/kstoolThe internal/template package carries the bulk of the unit-test coverage.
TUI and k8s layers are manually validated against a kind/EIDF cluster.
MIT.
