141 lines
6.1 KiB
Markdown
141 lines
6.1 KiB
Markdown
# sample-rig
|
|
|
|
A minimal, non-sensitive bundle that proves an installation works and shows what
|
|
shipped. Copy it, rename it, and you have another rig.
|
|
|
|
```bash
|
|
make manifest # generate the artifact — no cluster, no kubectl needed
|
|
make up # deploy it into the local cluster
|
|
make list # every rig in this cluster, with addresses
|
|
make dev # run the UI locally with vite, no cluster at all
|
|
```
|
|
|
|
`make up` prints an address. Open it and the page says **IT WORKS**, then lists
|
|
the tools and rigs in the bundle.
|
|
|
|
## What it is for
|
|
|
|
Three jobs, in the order you hit them:
|
|
|
|
1. **Prove the install.** kind is there, a cluster exists, MetalLB hands out an
|
|
address, a `type: LoadBalancer` Service actually resolves, and a pod serves.
|
|
If all of that works, the environment is sound.
|
|
2. **Say what shipped.** The page renders [`bundle.json`](bundle.json) —
|
|
standalone tools and rigs, flat, with none of soleprint's internal hierarchy.
|
|
Editing that file is the only step needed to change the listing.
|
|
3. **Stand in for the real thing.** Nothing here is sensitive. The real
|
|
architecture connects separately, against a setup already known to work.
|
|
|
|
## The UI is a complement, not the product
|
|
|
|
`rig-ui/` is just a vite app. It complements a rig; a rig is complete and useful
|
|
without it, and nothing depends on it being there. It is deliberately **not**
|
|
generated by kind or tilt — you copy the folder into a rig after that rig is
|
|
pulled, and apply one manifest:
|
|
|
|
```bash
|
|
kubectl apply -n <namespace> -f rig-ui/k8s.yaml
|
|
```
|
|
|
|
That file is the whole integration: one Pod running `npm run dev` on
|
|
`node:22-alpine`, one Service. A bare Pod rather than a Deployment because this
|
|
is a dev-loop convenience, not a workload to keep alive.
|
|
|
|
The app and `bundle.json` arrive as a ConfigMap, so nothing is baked into an
|
|
image and editing the bundle is the entire update cycle. The container runs
|
|
`npm install` at start, which needs egress to a registry — on a locked-down
|
|
cluster point npm at the internal one, or bake an image instead. Nothing else
|
|
changes if you do.
|
|
|
|
## One artifact, two destinations
|
|
|
|
`ctrl/manifest.py` emits `generated/<slug>.yaml` — namespace, the app and
|
|
bundle embedded in a ConfigMap, Pod, Service. It is self-contained and applies
|
|
unmodified anywhere:
|
|
|
|
```bash
|
|
kubectl apply -f generated/sample-rig.yaml # local kind, or an external cluster
|
|
```
|
|
|
|
`make up` applies **that same file**. There is no separate local path, so what
|
|
works here cannot quietly differ from what is applied elsewhere.
|
|
|
|
This is what `type: LoadBalancer` buys. MetalLB answers it on kind; the AWS load
|
|
balancer controller answers it on EKS. NodePort would not survive the trip — it
|
|
is a single cluster-wide port range, so two rigs would have to negotiate numbers.
|
|
|
|
**VPC-agnostic on purpose.** The target is EKS, but the Service carries no
|
|
annotations — no `aws-load-balancer-subnets`, no security groups, no `-scheme`,
|
|
no `-type: nlb`. Each of those encodes a specific network layout, and one of them
|
|
appearing here would pin the artifact to the account and VPC it was written
|
|
against, which is precisely what stops it also working on kind. Subnet discovery
|
|
is the cluster's business: EKS resolves it from the tags its own subnets carry.
|
|
|
|
That leaves one thing genuinely environment-specific — internal versus
|
|
internet-facing. A bare `LoadBalancer` provisions internet-facing, which a
|
|
regulated account will usually refuse, and should. That belongs in a
|
|
per-environment overlay applied on top, never inlined into this artifact.
|
|
|
|
**MetalLB only — no ingress-nginx.** Its controller supports a narrow window of
|
|
Kubernetes versions, so depending on it constrains which k8s a rig can be built
|
|
with. That undercuts running trailing-edge control planes to model a legacy
|
|
estate, which is the reason `versions.env` pins v1_33..v1_36. MetalLB carries no
|
|
such constraint, so reachability costs nothing in version coverage.
|
|
|
|
## Several rigs, one cluster
|
|
|
|
Identity follows the **folder name**, the same rule rig uses for cluster
|
|
identity. The namespace is the folder; resource names are generic, and names only
|
|
have to be unique within a namespace.
|
|
|
|
```bash
|
|
cp -r sample-rig corporate-rig
|
|
cd corporate-rig && make up # its own namespace, its own address
|
|
```
|
|
|
|
No edits, no collisions, both in the same local cluster. `make list` shows them
|
|
together. `make down` removes only this one — siblings, MetalLB and the cluster
|
|
are left alone.
|
|
|
|
Client rigs are gitignored (`*-rig/`, with `sample-rig/` the deliberate
|
|
exception): a rig's k8s files spell out a real architecture, and that is exactly
|
|
what must not land in this repo.
|
|
|
|
## Staging workstations
|
|
|
|
`ctrl/manifest.py` is stdlib-only on purpose: it runs on a bare machine before
|
|
anything is installed. The toolchain itself is rig's job — `make deps` installs
|
|
the pinned kind and tilt binaries, which is what makes a staging AWS workspace
|
|
reachable from the same commands as a laptop.
|
|
|
|
## Layout
|
|
|
|
```
|
|
sample-rig/
|
|
├── Makefile # thin — one target per ctrl/ script
|
|
├── bundle.json # what shipped; the UI renders THIS
|
|
├── rig-ui/ # the vite app — optional, copied into a rig to enable it
|
|
│ ├── k8s.yaml # how to plug it in: one Pod, one Service
|
|
│ ├── index.html
|
|
│ ├── package.json
|
|
│ ├── vite.config.js
|
|
│ └── src/{main.js,style.css}
|
|
├── ctrl/
|
|
│ ├── manifest.py # emits the artifact
|
|
│ └── bundle.sh # generate / deploy / inspect
|
|
└── generated/ # the artifact — committed, this is the deliverable
|
|
```
|
|
|
|
Editing `bundle.json` or anything in `rig-ui/` means re-running `make manifest`.
|
|
The ConfigMap carries a checksum of everything embedded, so a stale deployment is
|
|
visible rather than silent.
|
|
|
|
## Not built, but not foreclosed
|
|
|
|
Everything derives from `bundle.json` plus a target namespace. A Pulumi or
|
|
Terraform emitter would sit beside `ctrl/manifest.py` consuming the same inputs;
|
|
nothing above it assumes the artifact is YAML.
|
|
|
|
Licence terms for the compiled UI component belong in the soleprint-generated
|
|
bundle, not here — this sample carries no proprietary component.
|