feat: new-service and delete-service scaffolder
One command from nothing to a running service: generate repo from template, Actions secrets, namespace + unleash token, gitops render, kick CI, register with ArgoCD once the first image exists, optional --wait health check. delete-service reverses all of it. Idempotent, --dry-run supported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
253b6033c3
7 changed files with 809 additions and 0 deletions
96
README.md
Normal file
96
README.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# platform
|
||||
|
||||
One command from nothing to a running, observed service on the homelab k3s
|
||||
cluster, and one to take it all away again. Bash + `curl`, `jq`, `git`,
|
||||
`kubectl` (`kustomize` optional; falls back to `kubectl kustomize`).
|
||||
|
||||
```sh
|
||||
new-service orders --kind backend --with postgres,redis --wait
|
||||
new-service orders-web --kind frontend --api orders --wait
|
||||
delete-service orders-web --yes && delete-service orders --yes
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
git clone http://192.168.1.230:3000/nholtware/platform.git ~/Code/forgejo.local/nholtware/platform
|
||||
cd ~/Code/forgejo.local/nholtware/platform
|
||||
cp platform.env.example platform.env # gitignored; defaults match the homelab
|
||||
export PATH="$PWD/bin:$PATH" # not installed globally
|
||||
```
|
||||
|
||||
`platform.env` points at the homelab repo's gitignored `.secrets/` token files,
|
||||
the gitops clone, the kubeconfig and the ingress domain. The scripts refuse to
|
||||
run if any of them is missing, the gitops clone is dirty or not on `main`, the
|
||||
Forgejo token is rejected, or the cluster is unreachable.
|
||||
|
||||
## new-service
|
||||
|
||||
```
|
||||
new-service NAME --kind backend|frontend [--with postgres,redis] [--api BACKEND]
|
||||
[--wait] [--timeout SECONDS] [--dry-run]
|
||||
```
|
||||
|
||||
| Step | What | Skipped when |
|
||||
|------|------|--------------|
|
||||
| 1 | validate `NAME` (`^[a-z][a-z0-9-]{1,30}$`, not reserved), kind, flags; `--api` must be an existing backend | — |
|
||||
| 2 | generate public repo from `service-template-express` / `service-template-react` | repo exists |
|
||||
| 3 | set Actions secrets `REGISTRY_USER`, `REGISTRY_TOKEN`, `GITOPS_TOKEN` | never (PUT is idempotent) |
|
||||
| 4 | create namespace + Secret `unleash-client-token` (copied from `unleash/unleash-init-tokens`) | they exist |
|
||||
| 5 | render gitops `templates/<kind>` (+ `postgres`, `redis`) → `manifests/NAME`, `kustomize build` lint, commit, push | dir exists |
|
||||
| 6 | clone repo to `$CODE_DIR/NAME`, push empty commit `chore: kick ci` | CI already deployed, or a kick is running/succeeded |
|
||||
| 7 | wait for CI's `gitops-bump` to replace `newTag: latest` (fails fast if the CI run fails) | tag already bumped |
|
||||
| 8 | write `apps/NAME.yaml`, commit, push, refresh ArgoCD `root` | file exists |
|
||||
| 9 | `--wait`: ArgoCD app `Synced/Healthy`, then `/readyz` (backend) or `/healthz` + `/api/readyz` (frontend) → 200 | no `--wait` |
|
||||
|
||||
Then it prints the service, ArgoCD, Kibana (pre-filtered on `service:"NAME"`)
|
||||
and Unleash URLs, and the elapsed time.
|
||||
|
||||
**Why `apps/NAME.yaml` comes last:** ArgoCD only sees the service once CI has
|
||||
pushed a real image. Registering it earlier made the backend's migrate Job sit
|
||||
in `ImagePullBackOff` on `:latest` and hold the sync forever (Plan 04 caveat).
|
||||
|
||||
**Resumable:** every step checks before acting. If a run dies (CI failure,
|
||||
timeout, Ctrl-C), fix the cause and re-run the same command; a failed CI run is
|
||||
re-kicked automatically.
|
||||
|
||||
**Kinds:**
|
||||
|
||||
- `backend` — Express/TS. Runtime config (`NAME-runtime-config` ConfigMap) is
|
||||
always included. `--with postgres` adds a CNPG `NAME-db` cluster + migrate
|
||||
Job, `--with redis` adds `NAME-redis`. Without them the env vars and Job are
|
||||
dropped.
|
||||
- `frontend` — Vite/React on nginx. `--api BACKEND` deploys into the backend's
|
||||
namespace and routes `NAME.<domain>/api/*` to it (prefix stripped). Without
|
||||
`--api` it gets its own namespace and no `/api` route.
|
||||
|
||||
## delete-service
|
||||
|
||||
```
|
||||
delete-service NAME [--yes] [--dry-run] [--timeout SECONDS]
|
||||
```
|
||||
|
||||
Removes `apps/NAME.yaml` + `manifests/NAME` from gitops (the Application's
|
||||
`resources-finalizer` makes ArgoCD delete its workloads when `root` prunes it),
|
||||
waits for the Application to go, deletes the namespace if it is the service's
|
||||
own (CNPG PVCs go with it), deletes every version of the container package and
|
||||
the Forgejo repo, and removes the local clone. Asks you to type the name unless
|
||||
`--yes`. Refuses while a frontend still deploys into the service's namespace
|
||||
(delete the frontend first). Leaves a shared namespace and its
|
||||
`unleash-client-token` Secret alone.
|
||||
|
||||
## --dry-run
|
||||
|
||||
Runs every read-only check for real (config, repo exists?, namespace exists?,
|
||||
render + `kustomize build` lint into a temp dir) and prints each mutating API
|
||||
call (secret values redacted), git push and kubectl change instead of doing it.
|
||||
|
||||
## Notes
|
||||
|
||||
- The Forgejo admin token has no package scope; package list/delete uses the
|
||||
registry push token (same user).
|
||||
- Git pushes use the admin token through an inline credential helper; it is
|
||||
never written into a remote URL or `argv`.
|
||||
- Platform UIs: ArgoCD, Kibana and Unleash at `argocd.` / `kibana.` /
|
||||
`unleash.192.168.1.83.nip.io`. Credentials and conventions: homelab
|
||||
`PLATFORM.md`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue