Skip to content

Commit aa29d8b

Browse files
authored
🤖 feat: add kind dev cluster scaffolding and k9s tooling (#27)
## Summary This PR adds a repeatable KIND-based local dev/demo workflow for the controller and k9s-driven cluster inspection. ## Background The repo already uses KIND for CI smoke tests, but local setup previously required manual steps and separate tool installation. This change makes the workflow repo-native and easier to run in Coder workspaces. ## Implementation - Added Kubernetes demo tools (`kubectl`, `kind`, `k9s`) to the Nix devshell. - Added `hack/kind-dev.sh` with commands for: - `up`, `ctx`, `load-image`, `k9s`, `status`, `down` - Added Makefile wrappers for all `kind-dev` script commands. - Added README documentation for the KIND + k9s development loop. - Added `.mux/skills/kind-dev/SKILL.md` for per-workspace agent usage. ## Validation - `bash -n hack/kind-dev.sh` - `make -n kind-dev-up kind-dev-ctx kind-dev-load-image kind-dev-status kind-dev-k9s kind-dev-down` - `make build` - `make lint` - `make test` - `make verify-vendor` ## Risks Low risk. Changes are additive and primarily developer tooling/documentation. Potential impact is limited to local dev ergonomics and optional scripts. --- <details> <summary>📋 Implementation Plan</summary> # Add k9s to Nix devshell + KIND dev-cluster scaffolding ## Context / Why You’re developing `coder-k8s` from a Coder workspace and want a **“real” Kubernetes cluster** so you can demo the operator with tools like **k9s** (instead of only `envtest`). The repo already has a minimal KIND-based smoke test in CI; the goal is to make that workflow **easy and repeatable locally** by: 1) adding `k9s` to the Nix devshell, and 2) adding small repo-native scripts (in `hack/`) that spin up a KIND cluster and install prerequisites (CRDs/RBAC + optional namespace/SA) so you can start the controller yourself and then demo it with k9s. ## Evidence (repo reality) - `flake.nix` devshell currently includes Go/tooling but **does not include `k9s`**. - CI’s `.github/workflows/ci.yaml` has an `e2e-kind` job that: - builds a **Linux** binary named `coder-k8s` in the repo root (`go build -o coder-k8s ./` with `GOOS=linux`, `GOARCH=amd64`, `CGO_ENABLED=0`) - builds a distroless image via `Dockerfile.goreleaser` - loads it into KIND and applies `config/crd/bases/`, `config/rbac/`, and `config/e2e/`, then applies the sample CR. - `Dockerfile.goreleaser` expects the binary at the repo root: `COPY coder-k8s /coder-k8s`. - `config/e2e/deployment.yaml` deploys the controller into namespace `coder-system` as deployment `coder-k8s` with image `ghcr.io/coder/coder-k8s:e2e` and `imagePullPolicy: Never` (so `kind load` is required). - `Makefile`’s `build` target runs `go build ./...` and **does not** produce the `./coder-k8s` binary needed by `Dockerfile.goreleaser`. - `hack/` currently contains standalone bash scripts (`update-*.sh`) and is the best-fit location for “dev loop” helpers. <details> <summary>Note on what the controller does today</summary> `internal/controller/codercontrolplane_controller.go` is currently a no-op skeleton: it fetches the CR and logs at verbosity V(1), but does not create any dependent objects or update Status. The dev-cluster scaffolding below will still be useful (you can demo CRD install + controller reconcile triggers/logs), but a future small enhancement could set `status.phase` or emit an Event to make k9s demos more visually interesting. </details> --- ## Implementation plan ### 1) Add `k9s`, `kubectl`, and `kind` to the Nix devshell **File:** `flake.nix` Add `k9s`, `kubectl`, and `kind` to the `packages` list of the `default` devShell. ```nix # flake.nix (devShells.default) packages = with pkgs; [ go gnumake git # Kubernetes dev/demo tools kubectl kind k9s goreleaser actionlint zizmor golangci-lint govulncheck docsPython ]; ``` This makes `nix develop` sufficient for spinning up a KIND cluster and running k9s demos without ad-hoc installs. --- ### 2) Add a KIND dev-cluster setup script: `hack/kind-dev.sh` **File:** `hack/kind-dev.sh` (new, executable) Create one entrypoint script with subcommands to avoid duplicating bash boilerplate across multiple files. #### Goals - **Mirror CI** for the bootstrap pieces (CRDs/RBAC + the `coder-system` namespace/ServiceAccount), but **do not start** the controller for you. - Be **idempotent** where practical (safe to re-run `up`). - Print clear “next steps” commands so you can start the controller yourself (out-of-cluster or in-cluster) and then demo with `k9s`. #### Interface - `./hack/kind-dev.sh up` — create cluster (if needed) + install CRDs/RBAC + create `coder-system` namespace/ServiceAccount/Binding (**does not deploy the controller**) + set current kubectl context to the cluster - `./hack/kind-dev.sh ctx` — set current kubectl context to the cluster (short for “context”; useful if you overrode `CLUSTER_NAME` or switched away) - `./hack/kind-dev.sh load-image` — build a Linux `./coder-k8s` binary, build the container image, and `kind load` it (pre-req for in-cluster deployment) - `./hack/kind-dev.sh k9s` — open k9s on the cluster context - `./hack/kind-dev.sh status` — print useful `kubectl` status output - `./hack/kind-dev.sh down` — delete the cluster #### Defaults / config knobs (env vars) - `CLUSTER_NAME` default: `coder-k8s-dev` (if `MUX_WORKSPACE_NAME` is set, default to `coder-k8s-${MUX_WORKSPACE_NAME}` to avoid collisions); override via `CLUSTER_NAME=...` - `KUBE_CONTEXT` derived: `kind-${CLUSTER_NAME}` - `NAMESPACE` default: `coder-system` - `DEPLOYMENT` default: `coder-k8s` - `IMAGE` default: `ghcr.io/coder/coder-k8s:e2e` (must match `config/e2e/deployment.yaml` unless you also patch manifests) - `GOARCH` default: `$(go env GOARCH)` (with `GOOS=linux`, `CGO_ENABLED=0`) #### Script shape (sketch) ```bash #!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${ROOT}" DEFAULT_CLUSTER_NAME="coder-k8s-dev" if [[ -n "${MUX_WORKSPACE_NAME:-}" ]]; then DEFAULT_CLUSTER_NAME="coder-k8s-${MUX_WORKSPACE_NAME}" fi CLUSTER_NAME=${CLUSTER_NAME:-"${DEFAULT_CLUSTER_NAME}"} KUBE_CONTEXT="kind-${CLUSTER_NAME}" NAMESPACE=${NAMESPACE:-coder-system} DEPLOYMENT=${DEPLOYMENT:-coder-k8s} IMAGE=${IMAGE:-ghcr.io/coder/coder-k8s:e2e} GOARCH=${GOARCH:-"$(go env GOARCH)"} require_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "assertion failed: missing required command: $1" >&2 exit 1 } } kubectl_ctx() { kubectl --context "${KUBE_CONTEXT}" "$@" } ensure_cluster() { if ! kind get clusters | grep -qx "${CLUSTER_NAME}"; then echo "assertion failed: kind cluster ${CLUSTER_NAME} does not exist (run: $0 up)" >&2 exit 1 fi } build_binary() { GOFLAGS=-mod=vendor CGO_ENABLED=0 GOOS=linux GOARCH="${GOARCH}" \ go build -o coder-k8s ./ } build_and_load_image() { docker build -f Dockerfile.goreleaser -t "${IMAGE}" . kind load docker-image "${IMAGE}" --name "${CLUSTER_NAME}" } cmd_up() { require_cmd kind require_cmd kubectl if ! kind get clusters | grep -qx "${CLUSTER_NAME}"; then kind create cluster --name "${CLUSTER_NAME}" fi # Ensure the apiserver is ready before applying manifests. kubectl_ctx wait --for=condition=Ready node --all --timeout=120s # Install CRDs + RBAC. kubectl_ctx apply -f config/crd/bases/ kubectl_ctx apply -f config/rbac/ # Prepare the namespace + ServiceAccount/Binding used by the in-cluster controller manifests. kubectl_ctx apply -f config/e2e/namespace.yaml kubectl_ctx apply -f config/e2e/serviceaccount.yaml kubectl_ctx apply -f config/e2e/clusterrole-binding.yaml # Convenience: switch kubectl's current context to this cluster. cmd_ctx echo echo "KIND cluster bootstrapped. Next steps:" echo echo "Run controller locally (out-of-cluster):" echo " GOFLAGS=-mod=vendor go run . --app=controller" echo echo "OR deploy controller in-cluster:" echo " $0 load-image" echo " kubectl apply -f config/e2e/deployment.yaml" echo " kubectl wait --for=condition=Available deploy/${DEPLOYMENT} -n ${NAMESPACE} --timeout=120s" echo echo "Then demo with k9s:" echo " $0 k9s # or: k9s" } cmd_ctx() { require_cmd kind require_cmd kubectl ensure_cluster # Ensure kubeconfig includes this kind cluster (helpful if KUBECONFIG changed). kind export kubeconfig --name "${CLUSTER_NAME}" >/dev/null kubectl config use-context "${KUBE_CONTEXT}" >/dev/null echo "Using kubectl context: ${KUBE_CONTEXT} (switch later with: $0 ctx)" } cmd_load_image() { require_cmd kind require_cmd docker require_cmd go ensure_cluster build_binary build_and_load_image echo "Loaded ${IMAGE} into kind cluster ${CLUSTER_NAME}." } cmd_k9s() { require_cmd k9s ensure_cluster exec k9s --context "${KUBE_CONTEXT}" } cmd_status() { ensure_cluster kubectl_ctx get nodes -o wide kubectl_ctx get codercontrolplanes -A || true kubectl_ctx -n "${NAMESPACE}" get deploy,pods -o wide || true } cmd_down() { require_cmd kind kind delete cluster --name "${CLUSTER_NAME}" } case "${1:-}" in up) cmd_up ;; ctx|context|use-context) cmd_ctx ;; load-image) cmd_load_image ;; k9s) cmd_k9s ;; status) cmd_status ;; down) cmd_down ;; *) echo "usage: $0 {up|ctx|load-image|k9s|status|down}" >&2 exit 2 ;; esac ``` Notes: - Keep the script consistent with existing repo patterns: `#!/usr/bin/env bash`, `set -euo pipefail`, repo-root resolution, and “assertion failed:” wording for missing prerequisites. - Use explicit `go build -o coder-k8s ./` (CI-compatible) rather than `make build`, because the Dockerfile requires the root binary. --- ### 3) (Recommended) Add Makefile wrappers for discoverability **File:** `Makefile` Add phony targets that call the script: ```makefile .PHONY: kind-dev-up kind-dev-ctx kind-dev-load-image kind-dev-down kind-dev-k9s kind-dev-status kind-dev-up: ./hack/kind-dev.sh up kind-dev-ctx: ./hack/kind-dev.sh ctx kind-dev-load-image: ./hack/kind-dev.sh load-image kind-dev-status: ./hack/kind-dev.sh status kind-dev-k9s: ./hack/kind-dev.sh k9s kind-dev-down: ./hack/kind-dev.sh down ``` This makes the KIND dev-cluster workflow discoverable via `make help` / tab completion and keeps the “entrypoint” stable. --- ### 4) (Recommended) Document the KIND dev loop in README **File:** `README.md` Add a short section like: ```md ## KIND development cluster (for k9s demos) Bootstrap a KIND cluster and install CRDs/RBAC (**this also switches your current kubectl context**): make kind-dev-up > Tip: to run multiple clusters in parallel, override the name: > > CLUSTER_NAME=my-cluster make kind-dev-up If you need to switch your kubectl context later: make kind-dev-ctx # or: CLUSTER_NAME=my-cluster make kind-dev-ctx Start the controller (pick one): - Out-of-cluster (fast iteration): GOFLAGS=-mod=vendor go run . --app=controller - In-cluster (closer to CI): make kind-dev-load-image kubectl apply -f config/e2e/deployment.yaml kubectl -n coder-system wait --for=condition=Available deploy/coder-k8s --timeout=120s Demo: make kind-dev-k9s Cleanup: make kind-dev-down Mux users: there is an optional agent skill (`kind-dev`) under `.mux/skills/` with agent-oriented instructions for running per-workspace KIND clusters. ``` ### 5) Add a Mux agent skill for per-workspace KIND clusters **Files:** `.mux/skills/kind-dev/SKILL.md` Create a lightweight Mux skill (docs-only, no bundled references) that agents can load on demand to run the KIND dev loop **in parallel** across workspaces. The skill should emphasize: - **Unique `CLUSTER_NAME` per workspace** (recommend `coder-k8s-${MUX_WORKSPACE_NAME}`) - Using the repo’s bootstrap script (`./hack/kind-dev.sh`) rather than re-encoding CI steps in agent prompts - Always using explicit contexts (`kubectl --context kind-${CLUSTER_NAME} ...`) to avoid acting on the wrong cluster Suggested `SKILL.md` skeleton: ```md --- name: kind-dev description: Per-workspace KIND clusters for coder-k8s dev + demos. --- # KIND dev clusters (coder-k8s) Load this skill only when you need a real Kubernetes cluster (KIND) during development or demos. ## Unique cluster names (parallel agents) Prefer a per-workspace name to avoid collisions: ```bash export CLUSTER_NAME="coder-k8s-${MUX_WORKSPACE_NAME:-dev}" ``` ## Bootstrap ```bash ./hack/kind-dev.sh up kubectl --context kind-${CLUSTER_NAME} get nodes ``` ## Start controller (out-of-cluster) ```bash # Ensure your current kubectl context points at the cluster (up already does this). ./hack/kind-dev.sh ctx GOFLAGS=-mod=vendor go run . --app=controller ``` ## (Optional) In-cluster controller ```bash ./hack/kind-dev.sh load-image kubectl --context kind-${CLUSTER_NAME} apply -f config/e2e/deployment.yaml kubectl --context kind-${CLUSTER_NAME} -n coder-system wait --for=condition=Available deploy/coder-k8s --timeout=120s ``` ## Demo with k9s ```bash k9s --context kind-${CLUSTER_NAME} ``` ## Cleanup ```bash ./hack/kind-dev.sh down ``` --- ## Validation (when implementing) 1) Nix tools available: - `nix develop -c kubectl version --client` - `nix develop -c kind version` - `nix develop -c k9s version` 2) Script sanity: `bash -n hack/kind-dev.sh` 3) Bootstrap smoke test (use a deterministic cluster name): - `CLUSTER_NAME=coder-k8s-dev ./hack/kind-dev.sh up` - `test "$(kubectl config current-context)" = "kind-coder-k8s-dev"` - `kubectl --context kind-coder-k8s-dev get crd codercontrolplanes.coder.com` - `kubectl --context kind-coder-k8s-dev get clusterrole manager-role` - `kubectl --context kind-coder-k8s-dev -n coder-system get sa coder-k8s` 4) Optional in-cluster controller smoke test: - `CLUSTER_NAME=coder-k8s-dev ./hack/kind-dev.sh load-image` - `kubectl --context kind-coder-k8s-dev apply -f config/e2e/deployment.yaml` - `kubectl --context kind-coder-k8s-dev -n coder-system wait --for=condition=Available deploy/coder-k8s --timeout=120s` 5) Skill sanity: - `test -f .mux/skills/kind-dev/SKILL.md` 6) Cleanup: - `CLUSTER_NAME=coder-k8s-dev ./hack/kind-dev.sh down` </details> --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.68`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=0.68 -->
1 parent 9ffb9df commit aa29d8b

5 files changed

Lines changed: 343 additions & 1 deletion

File tree

.mux/skills/kind-dev/SKILL.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
name: kind-dev
3+
description: Per-workspace KIND clusters for coder-k8s dev + demos.
4+
---
5+
6+
# KIND dev clusters (coder-k8s)
7+
8+
Load this skill only when you need a real Kubernetes cluster (KIND) during development or demos.
9+
10+
## Unique cluster names (parallel agents)
11+
12+
Prefer a per-workspace name to avoid collisions:
13+
14+
```bash
15+
export CLUSTER_NAME="coder-k8s-${MUX_WORKSPACE_NAME:-dev}"
16+
```
17+
18+
## Bootstrap
19+
20+
```bash
21+
./hack/kind-dev.sh up
22+
kubectl --context kind-${CLUSTER_NAME} get nodes
23+
```
24+
25+
## Start controller (out-of-cluster)
26+
27+
```bash
28+
# up already switches your current context; run ctx again if needed.
29+
./hack/kind-dev.sh ctx
30+
31+
GOFLAGS=-mod=vendor go run . --app=controller
32+
```
33+
34+
## Optional: in-cluster controller
35+
36+
```bash
37+
./hack/kind-dev.sh load-image
38+
kubectl --context kind-${CLUSTER_NAME} apply -f config/e2e/deployment.yaml
39+
kubectl --context kind-${CLUSTER_NAME} -n coder-system wait --for=condition=Available deploy/coder-k8s --timeout=120s
40+
```
41+
42+
## Demo with k9s
43+
44+
```bash
45+
k9s --context kind-${CLUSTER_NAME}
46+
```
47+
48+
## Cleanup
49+
50+
```bash
51+
./hack/kind-dev.sh down
52+
```
53+
54+
## Validation when implementing
55+
56+
1. Nix tools available:
57+
58+
```bash
59+
nix develop -c kubectl version --client
60+
nix develop -c kind version
61+
nix develop -c k9s version
62+
```
63+
64+
2. Script sanity:
65+
66+
```bash
67+
bash -n hack/kind-dev.sh
68+
```
69+
70+
3. Bootstrap smoke test (deterministic cluster name):
71+
72+
```bash
73+
CLUSTER_NAME=coder-k8s-dev ./hack/kind-dev.sh up
74+
test "$(kubectl config current-context)" = "kind-coder-k8s-dev"
75+
kubectl --context kind-coder-k8s-dev get crd codercontrolplanes.coder.com
76+
kubectl --context kind-coder-k8s-dev get clusterrole manager-role
77+
kubectl --context kind-coder-k8s-dev -n coder-system get sa coder-k8s
78+
```
79+
80+
4. Optional in-cluster controller smoke test:
81+
82+
```bash
83+
CLUSTER_NAME=coder-k8s-dev ./hack/kind-dev.sh load-image
84+
kubectl --context kind-coder-k8s-dev apply -f config/e2e/deployment.yaml
85+
kubectl --context kind-coder-k8s-dev -n coder-system wait --for=condition=Available deploy/coder-k8s --timeout=120s
86+
```
87+
88+
5. Skill file exists:
89+
90+
```bash
91+
test -f .mux/skills/kind-dev/SKILL.md
92+
```
93+
94+
6. Cleanup:
95+
96+
```bash
97+
CLUSTER_NAME=coder-k8s-dev ./hack/kind-dev.sh down
98+
```
99+
100+
Use explicit `kubectl --context kind-${CLUSTER_NAME}` commands whenever possible to avoid acting on the wrong cluster.

Makefile

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ MODULE_FILES := go.mod $(wildcard go.sum)
44
ENVTEST_K8S_VERSION ?= 1.35.x
55
ENVTEST_ASSETS_DIR := $(shell pwd)/bin/envtest
66

7-
.PHONY: vendor test test-integration setup-envtest build lint vuln verify-vendor codegen manifests docs-reference docs-reference-check docs-serve docs-build docs-check update-coder-docs-skill
7+
.PHONY: vendor test test-integration setup-envtest build lint vuln verify-vendor codegen manifests docs-reference docs-reference-check docs-serve docs-build docs-check update-coder-docs-skill kind-dev-up kind-dev-ctx kind-dev-load-image kind-dev-status kind-dev-k9s kind-dev-down
88

99
$(VENDOR_STAMP): $(MODULE_FILES)
1010
go mod tidy
@@ -67,3 +67,21 @@ docs-check: docs-reference-check
6767

6868
update-coder-docs-skill:
6969
bash ./hack/update-coder-docs-skill.sh
70+
71+
kind-dev-up:
72+
./hack/kind-dev.sh up
73+
74+
kind-dev-ctx:
75+
./hack/kind-dev.sh ctx
76+
77+
kind-dev-load-image:
78+
./hack/kind-dev.sh load-image
79+
80+
kind-dev-status:
81+
./hack/kind-dev.sh status
82+
83+
kind-dev-k9s:
84+
./hack/kind-dev.sh k9s
85+
86+
kind-dev-down:
87+
./hack/kind-dev.sh down

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,57 @@ kubectl apply -f config/samples/coder_v1alpha1_codercontrolplane.yaml
3232
kubectl get codercontrolplanes -A
3333
```
3434

35+
## KIND development cluster (for k9s demos)
36+
37+
Bootstrap a KIND cluster and install CRDs/RBAC (**this also switches your current kubectl context**):
38+
39+
```bash
40+
make kind-dev-up
41+
```
42+
43+
> Tip: to run multiple clusters in parallel, override the name:
44+
>
45+
> ```bash
46+
> CLUSTER_NAME=my-cluster make kind-dev-up
47+
> ```
48+
49+
If you need to switch your kubectl context later:
50+
51+
```bash
52+
make kind-dev-ctx
53+
# or: CLUSTER_NAME=my-cluster make kind-dev-ctx
54+
```
55+
56+
Start the controller (pick one):
57+
58+
- Out-of-cluster (fast iteration):
59+
60+
```bash
61+
GOFLAGS=-mod=vendor go run . --app=controller
62+
```
63+
64+
- In-cluster (closer to CI):
65+
66+
```bash
67+
make kind-dev-load-image
68+
kubectl apply -f config/e2e/deployment.yaml
69+
kubectl -n coder-system wait --for=condition=Available deploy/coder-k8s --timeout=120s
70+
```
71+
72+
Demo:
73+
74+
```bash
75+
make kind-dev-k9s
76+
```
77+
78+
Cleanup:
79+
80+
```bash
81+
make kind-dev-down
82+
```
83+
84+
Mux users: there is an optional agent skill (`kind-dev`) under `.mux/skills/` with agent-oriented instructions for running per-workspace KIND clusters.
85+
3586
## Essential commands
3687

3788
| Command | Description |

flake.nix

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
go
2828
gnumake
2929
git
30+
31+
# Kubernetes dev/demo tools
32+
kubectl
33+
kind
34+
k9s
35+
3036
goreleaser
3137
actionlint
3238
zizmor

hack/kind-dev.sh

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
cd "${ROOT_DIR}"
6+
7+
DEFAULT_CLUSTER_NAME="coder-k8s-dev"
8+
if [[ -n "${MUX_WORKSPACE_NAME:-}" ]]; then
9+
DEFAULT_CLUSTER_NAME="coder-k8s-${MUX_WORKSPACE_NAME}"
10+
fi
11+
12+
CLUSTER_NAME="${CLUSTER_NAME:-${DEFAULT_CLUSTER_NAME}}"
13+
KUBE_CONTEXT="kind-${CLUSTER_NAME}"
14+
NAMESPACE="${NAMESPACE:-coder-system}"
15+
DEPLOYMENT="${DEPLOYMENT:-coder-k8s}"
16+
IMAGE="${IMAGE:-ghcr.io/coder/coder-k8s:e2e}"
17+
GOARCH="${GOARCH:-}"
18+
NODE_READY_TIMEOUT="${NODE_READY_TIMEOUT:-300s}"
19+
20+
require_cmd() {
21+
local cmd="${1}"
22+
if ! command -v "${cmd}" >/dev/null 2>&1; then
23+
echo "assertion failed: missing required command: ${cmd}" >&2
24+
exit 1
25+
fi
26+
}
27+
28+
kubectl_ctx() {
29+
require_cmd kubectl
30+
kubectl --context "${KUBE_CONTEXT}" "$@"
31+
}
32+
33+
ensure_cluster() {
34+
require_cmd kind
35+
if ! kind get clusters | grep -qx "${CLUSTER_NAME}"; then
36+
echo "assertion failed: kind cluster ${CLUSTER_NAME} does not exist (run: ./hack/kind-dev.sh up)" >&2
37+
exit 1
38+
fi
39+
}
40+
41+
build_binary() {
42+
local resolved_goarch="${GOARCH}"
43+
if [[ -z "${resolved_goarch}" ]]; then
44+
resolved_goarch="$(go env GOARCH)"
45+
if [[ -z "${resolved_goarch}" ]]; then
46+
echo "assertion failed: go env GOARCH returned an empty value" >&2
47+
exit 1
48+
fi
49+
fi
50+
51+
GOFLAGS=-mod=vendor CGO_ENABLED=0 GOOS=linux GOARCH="${resolved_goarch}" go build -o coder-k8s ./
52+
}
53+
54+
build_and_load_image() {
55+
docker build -f Dockerfile.goreleaser -t "${IMAGE}" .
56+
kind load docker-image "${IMAGE}" --name "${CLUSTER_NAME}"
57+
}
58+
59+
cmd_up() {
60+
require_cmd kind
61+
require_cmd kubectl
62+
63+
if ! kind get clusters | grep -qx "${CLUSTER_NAME}"; then
64+
kind create cluster --name "${CLUSTER_NAME}"
65+
fi
66+
67+
kind export kubeconfig --name "${CLUSTER_NAME}" >/dev/null
68+
kubectl config use-context "${KUBE_CONTEXT}" >/dev/null
69+
70+
kubectl_ctx wait --for=condition=Ready node --all --timeout="${NODE_READY_TIMEOUT}"
71+
72+
kubectl_ctx apply -f config/crd/bases/
73+
kubectl_ctx apply -f config/rbac/
74+
75+
kubectl_ctx apply -f config/e2e/namespace.yaml
76+
kubectl_ctx apply -f config/e2e/serviceaccount.yaml
77+
kubectl_ctx apply -f config/e2e/clusterrole-binding.yaml
78+
79+
cmd_ctx
80+
81+
echo
82+
echo "KIND cluster bootstrapped for coder-k8s."
83+
echo
84+
echo "Run controller locally (out-of-cluster):"
85+
echo " GOFLAGS=-mod=vendor go run . --app=controller"
86+
echo
87+
echo "Or deploy controller in-cluster:"
88+
echo " ./hack/kind-dev.sh load-image"
89+
echo " kubectl apply -f config/e2e/deployment.yaml"
90+
echo " kubectl wait --for=condition=Available deploy/${DEPLOYMENT} -n ${NAMESPACE} --timeout=120s"
91+
echo
92+
echo "Demo with k9s:"
93+
echo " ./hack/kind-dev.sh k9s"
94+
}
95+
96+
cmd_ctx() {
97+
require_cmd kind
98+
require_cmd kubectl
99+
100+
ensure_cluster
101+
kind export kubeconfig --name "${CLUSTER_NAME}" >/dev/null
102+
kubectl config use-context "${KUBE_CONTEXT}" >/dev/null
103+
echo "Using kubectl context: ${KUBE_CONTEXT}"
104+
}
105+
106+
cmd_load_image() {
107+
require_cmd kind
108+
require_cmd docker
109+
require_cmd go
110+
111+
ensure_cluster
112+
build_binary
113+
build_and_load_image
114+
115+
echo "Loaded ${IMAGE} into cluster ${CLUSTER_NAME}."
116+
}
117+
118+
cmd_k9s() {
119+
require_cmd k9s
120+
ensure_cluster
121+
exec k9s --context "${KUBE_CONTEXT}"
122+
}
123+
124+
cmd_status() {
125+
require_cmd kubectl
126+
ensure_cluster
127+
128+
kubectl_ctx get nodes -o wide
129+
kubectl_ctx get codercontrolplanes -A || true
130+
kubectl_ctx -n "${NAMESPACE}" get deploy,pods -o wide || true
131+
}
132+
133+
cmd_down() {
134+
require_cmd kind
135+
kind delete cluster --name "${CLUSTER_NAME}"
136+
}
137+
138+
usage() {
139+
cat <<'EOF' >&2
140+
usage: ./hack/kind-dev.sh {up|ctx|load-image|k9s|status|down}
141+
EOF
142+
exit 2
143+
}
144+
145+
case "${1:-}" in
146+
up)
147+
cmd_up
148+
;;
149+
ctx | context | use-context)
150+
cmd_ctx
151+
;;
152+
load-image)
153+
cmd_load_image
154+
;;
155+
k9s)
156+
cmd_k9s
157+
;;
158+
status)
159+
cmd_status
160+
;;
161+
down)
162+
cmd_down
163+
;;
164+
*)
165+
usage
166+
;;
167+
esac

0 commit comments

Comments
 (0)