Files
soleprint/rig/sample-rig/README.md
2026-08-20 11:24:42 -03:00

6.1 KiB

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.

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 — 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:

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:

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.

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.