rig updates

This commit is contained in:
2026-09-17 00:39:00 -03:00
parent 730ebaff2f
commit de5b1b7ea8
21 changed files with 69 additions and 267 deletions

View File

@@ -8,13 +8,13 @@
# #
# spr depends on rig, never the other way round. Building and deleting a cluster # spr depends on rig, never the other way round. Building and deleting a cluster
# is rig's job, so up and down hand straight to rig/ctrl/cluster.sh, carrying the # is rig's job, so up and down hand straight to rig/ctrl/cluster.sh, carrying the
# four things that make this cluster spr's rather than rig's defaults: # the things that make this cluster spr's rather than rig's defaults:
# #
# CLUSTER=spr rooms deploy into the kind-spr context # CLUSTER=spr rooms deploy into the kind-spr context
# KIND_CONFIG spr's own shape, which maps the rooms' gateway NodePorts # KIND_CONFIG spr's own kind config, which maps the rooms' gateway NodePorts
# REGISTRY_MODE=none rooms load images straight into the node # REGISTRY_MODE=none rooms load images straight into the node
# PROFILE=minimal pinned here, so a change to rig's own ctrl/.env can never # PROFILE= ADDONS= set empty here, so rig's own ctrl/.env can never quietly
# quietly add addons to spr's cluster # pick a profile or add addons to spr's cluster
# #
# status stays here: it answers a question about rooms, not about the cluster. # status stays here: it answers a question about rooms, not about the cluster.
set -e set -e
@@ -26,7 +26,8 @@ rig() {
CLUSTER=spr \ CLUSTER=spr \
KIND_CONFIG="$SCRIPT_DIR/k8s/kind-config.yaml" \ KIND_CONFIG="$SCRIPT_DIR/k8s/kind-config.yaml" \
REGISTRY_MODE=none \ REGISTRY_MODE=none \
PROFILE=minimal \ PROFILE= \
ADDONS= \
bash "$RIG_CTRL/cluster.sh" "$@" bash "$RIG_CTRL/cluster.sh" "$@"
} }

View File

@@ -167,7 +167,7 @@ the fact that steps four and five were also going to fail. Run now, it should be
`ok` and `done` all the way down, and that is the point: it is the scoreboard, `ok` and `done` all the way down, and that is the point: it is the scoreboard,
not the installer. not the installer.
`make cluster up` builds the default `minimal` profile — one node, no addons, `make cluster up` builds rig's built-in defaults — one node, no addons,
boots fast. You do not need it to develop anything, but you do want to know that boots fast. You do not need it to develop anything, but you do want to know that
kind, the kubeconfig context and the derived port block work *before* a new kind, the kubeconfig context and the derived port block work *before* a new
project has any problems of its own to confuse them with. `make cluster down` project has any problems of its own to confuse them with. `make cluster down`

View File

@@ -7,7 +7,7 @@
# make cluster up -> ctrl/cluster.sh up # make cluster up -> ctrl/cluster.sh up
# #
# Config layers, weakest first: built-in defaults < ctrl/versions.env (pinned # Config layers, weakest first: built-in defaults < ctrl/versions.env (pinned
# toolchain) < ctrl/env.d/<profile>.env (optional shape) < ctrl/.env (local, # toolchain) < ctrl/env.d/<profile>.env (optional) < ctrl/.env (local,
# gitignored) < the environment. So `make cluster up PROFILE=<name>` beats them all. # gitignored) < the environment. So `make cluster up PROFILE=<name>` beats them all.
# #
# Start with: make setup (then: make cluster up && make docs) # Start with: make setup (then: make cluster up && make docs)

View File

@@ -138,14 +138,14 @@ directory beside it.
**rig needs no profile.** With none named it runs on built-in defaults: one node, **rig needs no profile.** With none named it runs on built-in defaults: one node,
no addons, a local registry, the newest Kubernetes version it pins. A profile is no addons, a local registry, the newest Kubernetes version it pins. A profile is
an optional overlay — a file in `ctrl/env.d/`, named by `PROFILE` — for when you an optional overlay — a file in `ctrl/env.d/`, named by `PROFILE` — for when you
want a different shape. want different addons or registry.
rig ships **examples**, not active profiles, because each one is a use case rather rig ships **examples**, not active profiles, because each one is a use case rather
than something every rig needs. Copy one to use it: than something every rig needs. Copy one to use it:
| example | shape | | example | what it changes |
| --- | --- | | --- | --- |
| `client.env.example` | multi-node, audit on, images through a mirror of a corporate registry | | `client.env.example` | images through a mirror of a corporate registry |
| `offline.env.example` | air-gapped: everything from a preloaded local registry | | `offline.env.example` | air-gapped: everything from a preloaded local registry |
| `data.env.example` | databases and a scheduler: postgres, redis, airflow | | `data.env.example` | databases and a scheduler: postgres, redis, airflow |
@@ -156,17 +156,9 @@ PROFILE=data make addons install
make addons # what the active profile wants, and what exists make addons # what the active profile wants, and what exists
``` ```
A profile names a **cluster shape** — a file in `ctrl/k8s/` — rather than The **cluster itself** is one file, `ctrl/k8s/kind-config.yaml.tpl` (one node).
restating node count and audit as variables: To change it — more nodes, other port mappings — edit it and `make cluster
reset`. The node count is read back out of it, so there is nothing to drift. The layout under `ctrl/k8s/` is the
| shape | nodes | audit | used by |
| --- | --- | --- | --- |
| `kind-config.yaml.tpl` | 1 | off | the default; the `data` example |
| `kind-config.audit.yaml.tpl` | 1 | on | the `offline` example |
| `kind-config.client.yaml.tpl` | 3 | on | the `client` example |
Both numbers are read back out of the chosen file, so the YAML is the only place
that decides and there is nothing to drift. The layout under `ctrl/k8s/` is the
same as every other project here — a kind config, a kustomize `base/`, an same as every other project here — a kind config, a kustomize `base/`, an
`overlays/dev/` — see [`ctrl/k8s/README.md`](ctrl/k8s/README.md). `overlays/dev/` — see [`ctrl/k8s/README.md`](ctrl/k8s/README.md).

View File

@@ -23,7 +23,7 @@ load_config
echo echo
echo "config" echo "config"
echo " profile ${PROFILE_NAME} (nodes=${NODES} audit=${AUDIT})" echo " profile ${PROFILE_NAME} (nodes=${NODES})"
echo " cluster ${CLUSTER} (context ${KUBECONTEXT})" echo " cluster ${CLUSTER} (context ${KUBECONTEXT})"
echo " registry ${REGISTRY_MODE}" echo " registry ${REGISTRY_MODE}"
echo " ingress ${INGRESS_MODE}" echo " ingress ${INGRESS_MODE}"

View File

@@ -23,15 +23,14 @@ up() {
echo "cluster '$CLUSTER' exists — converging" echo "cluster '$CLUSTER' exists — converging"
else else
# Say what this profile locks in BEFORE spending minutes building it: # Say what this profile locks in BEFORE spending minutes building it:
# the audit policy is an apiserver flag and cannot be changed later. # the kind config is fixed at creation and cannot be changed later.
echo "creating cluster '$CLUSTER' from profile '$PROFILE_NAME'" echo "creating cluster '$CLUSTER' from profile '$PROFILE_NAME'"
echo " shape ${KIND_CONFIG_SHOWN}" echo " kind config ${KIND_CONFIG}"
echo " nodes $NODES" echo " nodes $NODES"
echo " image $NODE_IMAGE" echo " image $NODE_IMAGE"
echo " audit $AUDIT"
echo " ingress $INGRESS_MODE" echo " ingress $INGRESS_MODE"
echo " registry $REGISTRY_MODE" echo " registry $REGISTRY_MODE"
echo " (audit is fixed at creation — 'make cluster reset' to change it)" echo " (fixed at creation — edit the kind config, then 'make cluster reset')"
echo echo
render_kind_config | kind create cluster --config - render_kind_config | kind create cluster --config -
@@ -69,7 +68,7 @@ down() {
} }
# The escape hatch for a wedged cluster, and the only way to change a # The escape hatch for a wedged cluster, and the only way to change a
# creation-time setting such as the audit policy. # creation-time setting such as the node count or port mappings.
reset() { reset() {
down down
echo echo

View File

@@ -3,16 +3,11 @@
# directory and name it — PROFILE=client in ctrl/.env, or on the command line. It # directory and name it — PROFILE=client in ctrl/.env, or on the command line. It
# then overlays the defaults; anything it does not set, they still supply. # then overlays the defaults; anything it does not set, they still supply.
# #
# client — the regulated-estate shape. Multi-node so taints, affinity and # client — images through a pull-through cache of the corporate registry, with
# topology are real; apiserver audit on; images through a pull-through cache of # TLS and metrics addons. More nodes or port mappings: edit k8s/kind-config.yaml.tpl.
# the corporate registry.
#
# Costs roughly 4-6 GB. Check `make cluster list` before starting this alongside
# other work — see the memory note in the README.
PROFILE_NAME=client PROFILE_NAME=client
K8S_VERSION=v1_36 K8S_VERSION=v1_36
KIND_CONFIG=kind-config.client.yaml.tpl
ADDONS="metallb cert-manager metrics-server" ADDONS="metallb cert-manager metrics-server"
REGISTRY_MODE=mirror REGISTRY_MODE=mirror
INGRESS_MODE=hostport INGRESS_MODE=hostport

View File

@@ -14,7 +14,6 @@
PROFILE_NAME=data PROFILE_NAME=data
K8S_VERSION=v1_36 K8S_VERSION=v1_36
KIND_CONFIG=kind-config.yaml.tpl
# Order matters: addons.sh installs in the order listed, and airflow refuses to # Order matters: addons.sh installs in the order listed, and airflow refuses to
# start without a metadata database, so postgres comes first. # start without a metadata database, so postgres comes first.
ADDONS="metallb postgres redis airflow" ADDONS="metallb postgres redis airflow"

View File

@@ -11,7 +11,6 @@
PROFILE_NAME=offline PROFILE_NAME=offline
K8S_VERSION=v1_36 K8S_VERSION=v1_36
KIND_CONFIG=kind-config.audit.yaml.tpl
ADDONS="metallb" ADDONS="metallb"
REGISTRY_MODE=local REGISTRY_MODE=local
INGRESS_MODE=hostport INGRESS_MODE=hostport

View File

@@ -1,13 +1,12 @@
# `ctrl/k8s` — cluster shape, and what runs on it # `ctrl/k8s` — the cluster, and what runs on it
Same layout as every other project here: a kind config, a kustomize `base/`, Same layout as every other project here: a kind config, a kustomize `base/`,
and an `overlays/dev/` that patches it. and an `overlays/dev/` that patches it.
``` ```
kind-config*.yaml.tpl the cluster itself — nodes, ports, audit kind-config.yaml.tpl the cluster itself — nodes, ports
base/ the components, as plain manifests base/ the components, as plain manifests
overlays/dev/ how this rig differs from the base overlays/dev/ how this rig differs from the base
audit-policy.yaml mounted into the apiserver by the audit shapes
``` ```
## Why the cluster config is a template ## Why the cluster config is a template
@@ -22,21 +21,12 @@ directory name — so a literal would make every copy collide on both.
`gettext-base`, which a minimal Debian does not have, and Docker being the only `gettext-base`, which a minimal Debian does not have, and Docker being the only
prerequisite is the one promise rig makes. prerequisite is the one promise rig makes.
**The chosen file is the source of truth for node count and audit.** **To change the cluster, edit this file** — more nodes, other port mappings —
`lib/config.sh` reads both back out of it, so a profile names a shape and does then `make cluster reset`: a kind config is fixed at creation, not re-applied.
not restate what the YAML already says. `lib/config.sh` reads the node count back out of it, so nothing restates it.
| file | nodes | audit | profiles | A project that builds its own cluster through rig passes its own file as
| --- | --- | --- | --- | `KIND_CONFIG=<path>`; it is rendered the same way.
| `kind-config.yaml.tpl` | 1 | off | the default; the `data` example |
| `kind-config.audit.yaml.tpl` | 1 | on | the `offline` example |
| `kind-config.client.yaml.tpl` | 3 | on | the `client` example |
With no profile the default shape is used; a profile picks another with
`KIND_CONFIG`. Adding a shape is adding a file — there is no dispatcher to edit.
Audit is an apiserver flag and therefore fixed at creation: changing it is
`make cluster reset`, not a re-apply.
## `base/` — replace these ## `base/` — replace these

View File

@@ -1,44 +0,0 @@
# Apiserver audit policy. Mounted into the control plane at creation when a
# profile sets AUDIT=on — an apiserver flag, so it cannot be added to a running
# cluster without recreating it.
#
# Deliberately modest: enough to make "who changed what, and when" answerable
# during onboarding without filling the disk. Read the log with:
# docker exec <cluster>-control-plane cat /var/log/kubernetes/audit.log
apiVersion: audit.k8s.io/v1
kind: Policy
# Never log the request body for these — they contain credentials.
omitStages:
- RequestReceived
rules:
# Secrets/configmaps: record that access happened, never the contents.
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps"]
# Authn/authz decisions — the part an auditor actually asks about.
- level: Metadata
nonResourceURLs:
- /apis*
- /api*
# Mutations to workloads and policy: full request, so a diff is reconstructable.
- level: Request
verbs: ["create", "update", "patch", "delete"]
resources:
- group: ""
resources: ["pods", "services", "serviceaccounts", "namespaces"]
- group: "apps"
- group: "networking.k8s.io"
- group: "rbac.authorization.k8s.io"
# Everything else that changes state: metadata only.
- level: Metadata
verbs: ["create", "update", "patch", "delete"]
# Reads are dropped entirely — otherwise controller polling drowns the log.
- level: None
verbs: ["get", "list", "watch"]

View File

@@ -1,57 +0,0 @@
# Cluster shape: one node, apiserver audit ON. Used by the `offline` profile.
#
# Audit is an apiserver flag, so it is fixed when the cluster is created —
# changing it means `make cluster reset`, not a re-apply. That is why it is a
# property of the cluster file rather than something switched at runtime.
#
# k8s >= 1.31 uses kubeadm v1beta4, where extraArgs is a LIST of name/value
# pairs. The older map form is silently ignored — it does not error, audit
# simply never turns on.
#
# Substituted by ctrl/cluster.sh: CLUSTER, NODE_IMAGE, HTTP_PORT, HOST_WORKDIR
# (named without the ${...} braces so this line survives the substitution)
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ${CLUSTER}
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
- name: audit-policy-file
value: /etc/kubernetes/audit/policy.yaml
- name: audit-log-path
value: /var/log/kubernetes/audit.log
- name: audit-log-maxage
value: "7"
extraVolumes:
- name: audit-policy
hostPath: /etc/kubernetes/audit
mountPath: /etc/kubernetes/audit
readOnly: true
- name: audit-log
hostPath: /var/log/kubernetes
mountPath: /var/log/kubernetes
readOnly: false
nodes:
- role: control-plane
image: ${NODE_IMAGE}
# hostPath is resolved by the HOST dockerd, so this must be a host path even
# when cluster.sh runs inside the installer container. HOST_WORKDIR says where
# this rig lives on the host; bare on a host it is just the repo root.
extraMounts:
- hostPath: ${HOST_WORKDIR}/ctrl/k8s/audit-policy.yaml
containerPath: /etc/kubernetes/audit/policy.yaml
readOnly: true
extraPortMappings:
- containerPort: 30080
hostPort: ${HTTP_PORT}
listenAddress: "0.0.0.0"
protocol: TCP

View File

@@ -1,55 +0,0 @@
# Cluster shape: three nodes, apiserver audit ON. Used by the `client` profile —
# the regulated-estate shape.
#
# Multi-node so taints, affinity and topology spread are real rather than
# vacuously satisfied by a single node. It costs roughly 4-6 GB; run
# `make cluster list` before starting this alongside other work.
#
# Substituted by ctrl/cluster.sh: CLUSTER, NODE_IMAGE, HTTP_PORT, HOST_WORKDIR
# (named without the ${...} braces so this line survives the substitution)
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ${CLUSTER}
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
- name: audit-policy-file
value: /etc/kubernetes/audit/policy.yaml
- name: audit-log-path
value: /var/log/kubernetes/audit.log
- name: audit-log-maxage
value: "7"
extraVolumes:
- name: audit-policy
hostPath: /etc/kubernetes/audit
mountPath: /etc/kubernetes/audit
readOnly: true
- name: audit-log
hostPath: /var/log/kubernetes
mountPath: /var/log/kubernetes
readOnly: false
nodes:
- role: control-plane
image: ${NODE_IMAGE}
extraMounts:
- hostPath: ${HOST_WORKDIR}/ctrl/k8s/audit-policy.yaml
containerPath: /etc/kubernetes/audit/policy.yaml
readOnly: true
extraPortMappings:
- containerPort: 30080
hostPort: ${HTTP_PORT}
listenAddress: "0.0.0.0"
protocol: TCP
- role: worker
image: ${NODE_IMAGE}
- role: worker
image: ${NODE_IMAGE}

View File

@@ -1,4 +1,5 @@
# Cluster shape: one node, no audit. Used by the `minimal` and `data` profiles. # The cluster. One node by default — add nodes or port mappings by editing this
# file, then `make cluster reset`.
# #
# A TEMPLATE rather than a plain kind-config.yaml because a rig is copied and # A TEMPLATE rather than a plain kind-config.yaml because a rig is copied and
# renamed to make a second environment, and both the cluster name and the host # renamed to make a second environment, and both the cluster name and the host
@@ -9,8 +10,8 @@
# #
# Substituted by ctrl/cluster.sh: CLUSTER, NODE_IMAGE, HTTP_PORT, HOST_WORKDIR # Substituted by ctrl/cluster.sh: CLUSTER, NODE_IMAGE, HTTP_PORT, HOST_WORKDIR
# (named without the ${...} braces so this line survives the substitution) # (named without the ${...} braces so this line survives the substitution)
# Node count and audit are READ BACK from this file by lib/config.sh, so this # The node count is READ BACK from this file by lib/config.sh, so this YAML is
# YAML is the source of truth for both — there is no second place to update. # the source of truth for it — there is no second place to update.
kind: Cluster kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4 apiVersion: kind.x-k8s.io/v1alpha4
name: ${CLUSTER} name: ${CLUSTER}

View File

@@ -7,7 +7,7 @@
# #
# built-in defaults below; fill only what nothing else set # built-in defaults below; fill only what nothing else set
# ctrl/versions.env pinned toolchain + image digests (committed) # ctrl/versions.env pinned toolchain + image digests (committed)
# ctrl/env.d/<profile> cluster shape — OPTIONAL, examples ship as *.env.example # ctrl/env.d/<profile> addons, registry — OPTIONAL, examples ship as *.env.example
# ctrl/.env machine-local values and secrets (gitignored) # ctrl/.env machine-local values and secrets (gitignored)
# the caller's env `make cluster up PROFILE=<name>` (always wins) # the caller's env `make cluster up PROFILE=<name>` (always wins)
# #
@@ -19,9 +19,8 @@
# Values a user can reasonably override per-invocation. Anything set in the # Values a user can reasonably override per-invocation. Anything set in the
# environment when load_config runs is restored after the files are read. # environment when load_config runs is restored after the files are read.
# NODES and AUDIT are deliberately NOT here: they are properties of the chosen # NODES is deliberately NOT here: it is read back out of the kind config below,
# ctrl/k8s/kind-config*.yaml.tpl and are read back out of it below, so there is # so the file is the one place that decides it.
# one place that decides the shape of the cluster rather than two that can drift.
# #
# REGISTRY_PORT and MANIFESTS_DIR were missing here while ctrl/.env set them, so # REGISTRY_PORT and MANIFESTS_DIR were missing here while ctrl/.env set them, so
# the caller's env silently LOST to the file for those two — the one precedence # the caller's env silently LOST to the file for those two — the one precedence
@@ -136,32 +135,18 @@ load_config() {
exit 1 exit 1
fi fi
# The cluster's shape is a file in ctrl/k8s/, named by the profile. Adding a # The cluster is one file: k8s/kind-config.yaml.tpl. To change it, edit it.
# shape is adding a file; there is no dispatcher to edit. # KIND_CONFIG is only "use this file instead", for a project that builds its
# # own cluster through rig (a path relative to ctrl/, or absolute).
# A host that needs its own shape — extra port mappings, more nodes — passes KIND_CONFIG="${KIND_CONFIG:-./k8s/kind-config.yaml.tpl}"
# an absolute path instead, and rig renders it exactly like one of its own: if [ ! -f "$KIND_CONFIG" ]; then
# ${CLUSTER} and ${NODE_IMAGE} are substituted either way. The shape stays in echo "no kind config at KIND_CONFIG=${KIND_CONFIG}" >&2
# the host's tree, because what a host's cluster needs is the host's business;
# rig only knows how to build whatever it is handed.
KIND_CONFIG="${KIND_CONFIG:-kind-config.yaml.tpl}"
case "$KIND_CONFIG" in
/*) KIND_CONFIG_PATH="$KIND_CONFIG"; KIND_CONFIG_SHOWN="$KIND_CONFIG" ;;
*) KIND_CONFIG_PATH="./k8s/${KIND_CONFIG}"; KIND_CONFIG_SHOWN="ctrl/k8s/${KIND_CONFIG}" ;;
esac
if [ ! -f "$KIND_CONFIG_PATH" ]; then
echo "no such cluster shape: ${KIND_CONFIG_SHOWN}" >&2
echo "rig's own: $(ls k8s/kind-config*.yaml.tpl 2>/dev/null | xargs -n1 basename | tr '\n' ' ')" >&2
echo "or pass an absolute path to a shape of your own" >&2
exit 1 exit 1
fi fi
# Read the shape back out of the YAML rather than trusting a profile to # Read the node count back out of the file rather than restating it:
# restate it. check.sh sizes the memory warning on NODES, and cluster.sh # check.sh and the memory tool size their budget on NODES.
# prints AUDIT before spending minutes building something that cannot be NODES=$(grep -c '^ - role:' "$KIND_CONFIG")
# changed afterwards — both would mislead if the numbers drifted.
NODES=$(grep -c '^ - role:' "$KIND_CONFIG_PATH")
if grep -q 'audit-policy-file' "$KIND_CONFIG_PATH"; then AUDIT=on; else AUDIT=off; fi
# What one node costs, measured rather than guessed. On 2026-09-11 a minimal # What one node costs, measured rather than guessed. On 2026-09-11 a minimal
# control-plane node ran at 620 MiB idle and ~728 MiB with a small mock, plus # control-plane node ran at 620 MiB idle and ~728 MiB with a small mock, plus
@@ -175,7 +160,7 @@ load_config() {
NODE_MB=800 NODE_MB=800
} }
# Render a cluster shape to stdout. sed rather than envsubst: envsubst is # Render the kind config to stdout. sed rather than envsubst: envsubst is
# gettext-base, absent from a minimal Debian, and Docker is meant to be the only # gettext-base, absent from a minimal Debian, and Docker is meant to be the only
# prerequisite. The variable list is explicit so a template cannot quietly start # prerequisite. The variable list is explicit so a template cannot quietly start
# depending on something the caller does not set. # depending on something the caller does not set.
@@ -188,7 +173,7 @@ render_kind_config() {
-e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" \ -e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" \
-e "s|\${HTTP_PORT}|${HTTP_PORT}|g" \ -e "s|\${HTTP_PORT}|${HTTP_PORT}|g" \
-e "s|\${HOST_WORKDIR}|${host_workdir}|g" \ -e "s|\${HOST_WORKDIR}|${host_workdir}|g" \
"$KIND_CONFIG_PATH" "$KIND_CONFIG"
} }
_config_restore() { _config_restore() {

View File

@@ -99,7 +99,9 @@ test_value() {
# profile, template or pinned version to be present for this to run. # profile, template or pinned version to be present for this to run.
PROFILE) config_profiles | head -1 ;; PROFILE) config_profiles | head -1 ;;
K8S_VERSION) (set -a; source ./versions.env; compgen -v NODE_IMAGE_v | sort -V | head -1 | sed 's/^NODE_IMAGE_//') ;; K8S_VERSION) (set -a; source ./versions.env; compgen -v NODE_IMAGE_v | sort -V | head -1 | sed 's/^NODE_IMAGE_//') ;;
KIND_CONFIG) ls k8s/kind-config*.yaml.tpl 2>/dev/null | sort | head -1 | xargs -r basename ;; # An absolute path, as a project passing its own file does. Never equal
# to the default, so the check cannot pass by accident.
KIND_CONFIG) echo "$PWD/k8s/kind-config.yaml.tpl" ;;
*_PORT) echo "19999" ;; *_PORT) echo "19999" ;;
CLUSTER) echo "selftest-name" ;; CLUSTER) echo "selftest-name" ;;
MANIFESTS_DIR) echo "../elsewhere/overlays/dev" ;; MANIFESTS_DIR) echo "../elsewhere/overlays/dev" ;;

View File

@@ -32,7 +32,7 @@ digraph rig_environment {
fontcolor="#8892a8" fontcolor="#8892a8"
versions [label="versions.env\npinned toolchain" fillcolor="#121829"] versions [label="versions.env\npinned toolchain" fillcolor="#121829"]
profile [label="env.d/<profile>.env\nnodes · CNI · audit · addons" fillcolor="#121829"] profile [label="env.d/<profile>.env\noptional: addons · registry" fillcolor="#121829"]
localenv [label="ctrl/.env\nsecrets, overrides" fillcolor="#121829"] localenv [label="ctrl/.env\nsecrets, overrides" fillcolor="#121829"]
shell [label="the environment\nPROFILE=client make …" fillcolor="#1a3a1a" fontcolor="#00c853"] shell [label="the environment\nPROFILE=client make …" fillcolor="#1a3a1a" fontcolor="#00c853"]
} }

View File

@@ -122,7 +122,7 @@
<title>profile</title> <title>profile</title>
<polygon fill="#121829" stroke="#1e2a4a" points="781.5,-318.58 612.5,-318.58 612.5,-282.58 781.5,-282.58 781.5,-318.58"/> <polygon fill="#121829" stroke="#1e2a4a" points="781.5,-318.58 612.5,-318.58 612.5,-282.58 781.5,-282.58 781.5,-318.58"/>
<text xml:space="preserve" text-anchor="middle" x="697" y="-303.63" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e8eaf0">env.d/&lt;profile&gt;.env</text> <text xml:space="preserve" text-anchor="middle" x="697" y="-303.63" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e8eaf0">env.d/&lt;profile&gt;.env</text>
<text xml:space="preserve" text-anchor="middle" x="697" y="-290.13" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e8eaf0">nodes · CNI · audit · addons</text> <text xml:space="preserve" text-anchor="middle" x="697" y="-290.13" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e8eaf0">optional: addons · registry</text>
</g> </g>
<!-- versions&#45;&gt;profile --> <!-- versions&#45;&gt;profile -->
<g id="edge6" class="edge"> <g id="edge6" class="edge">

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -302,15 +302,15 @@ make cluster up <span class="c"># build the cluster for the active profile</spa
<h3>3 &middot; make cluster up</h3> <h3>3 &middot; make cluster up</h3>
<p>Builds the cluster for the active profile. It prints what the profile <p>Builds the cluster for the active profile. It prints what the profile
locks in <i>before</i> spending the time, because the CNI and the audit locks in <i>before</i> spending the time, because the kind config is
policy are fixed at creation and cannot be changed afterwards.</p> fixed at creation and cannot be changed afterwards.</p>
<p>Re-running is safe and, more importantly, <b>convergent</b>: if a first <p>Re-running is safe and, more importantly, <b>convergent</b>: if a first
attempt was interrupted before the CNI was installed, running it again attempt was interrupted before the CNI was installed, running it again
finishes the job rather than reporting "already exists" and leaving every finishes the job rather than reporting "already exists" and leaving every
node permanently NotReady.</p> node permanently NotReady.</p>
<pre><code>make cluster up <span class="c"># built-in defaults — no profile needed</span> <pre><code>make cluster up <span class="c"># built-in defaults — no profile needed</span>
make cluster up PROFILE=client <span class="c"># after copying env.d/client.env.example: three nodes, audit, cached registry</span> make cluster up PROFILE=client <span class="c"># after copying env.d/client.env.example: cached registry</span>
make cluster reset <span class="c"># destroy and rebuild — the only way to change CNI or audit</span> make cluster reset <span class="c"># destroy and rebuild — how an edited kind config takes effect</span>
</code></pre> </code></pre>
<h3>4 &middot; make docs</h3> <h3>4 &middot; make docs</h3>
@@ -416,19 +416,20 @@ docker load &lt; rig.tgz &amp;&amp; make cluster up PROFILE=offline <span clas
<section class="section" id="profiles"> <section class="section" id="profiles">
<h2>Profiles</h2> <h2>Profiles</h2>
<p class="lede">Cluster shape is declared, not baked in.</p> <p class="lede">Optional overlays — rig needs none.</p>
<div class="prose"> <div class="prose">
<table> <table>
<tr><th>profile</th><th>nodes</th><th>audit</th><th>registry</th><th>for</th></tr> <tr><th>example</th><th>registry</th><th>for</th></tr>
<tr><td><code>minimal</code></td><td>1</td><td>off</td><td>none</td><td>first boot; assumes nothing</td></tr> <tr><td><i>none</i></td><td>local</td><td>the built-in defaults; no profile needed</td></tr>
<tr><td><code>client</code></td><td>3</td><td>on</td><td>mirror</td><td>the regulated shape</td></tr> <tr><td><code>client.env.example</code></td><td>mirror</td><td>images through a corporate registry</td></tr>
<tr><td><code>offline</code></td><td>1</td><td>on</td><td>local</td><td>air-gapped</td></tr> <tr><td><code>offline.env.example</code></td><td>local</td><td>air-gapped</td></tr>
<tr><td><code>data.env.example</code></td><td>local</td><td>postgres, redis, airflow</td></tr>
</table> </table>
<div class="note"><p><b>The audit policy cannot be changed later.</b> It is an <div class="note"><p><b>The kind config cannot be re-applied.</b> Edit
apiserver flag, fixed when the cluster is created. <code>cluster up</code> <code>ctrl/k8s/kind-config.yaml.tpl</code>; it takes effect when the cluster is created. <code>cluster up</code> prints what it
prints what a profile locks in before spending the time, and locks in before spending the time, and <code>make cluster reset</code> is
<code>make cluster reset</code> is the way out.</p></div> the way out.</p></div>
<h3>LoadBalancer services</h3> <h3>LoadBalancer services</h3>
<p>Real manifests use <code>type: LoadBalancer</code>, because a real <p>Real manifests use <code>type: LoadBalancer</code>, because a real

View File

@@ -36,7 +36,7 @@ derive_port_base ()
render_kind_config () render_kind_config ()
{ {
local host_workdir="${HOST_WORKDIR:-$(cd .. && pwd)}"; local host_workdir="${HOST_WORKDIR:-$(cd .. && pwd)}";
sed -e "s|\${CLUSTER}|${CLUSTER}|g" -e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" -e "s|\${HTTP_PORT}|${HTTP_PORT}|g" -e "s|\${HOST_WORKDIR}|${host_workdir}|g" "$KIND_CONFIG_PATH" sed -e "s|\${CLUSTER}|${CLUSTER}|g" -e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" -e "s|\${HTTP_PORT}|${HTTP_PORT}|g" -e "s|\${HOST_WORKDIR}|${host_workdir}|g" "$KIND_CONFIG"
} }
# ── configuration, frozen for profile 'default' ── # ── configuration, frozen for profile 'default' ──
@@ -47,7 +47,6 @@ load_config() {
done done
declare -g ADDONS="" declare -g ADDONS=""
declare -gx AIRFLOW_IMAGE="apache/airflow:2.10.4" declare -gx AIRFLOW_IMAGE="apache/airflow:2.10.4"
declare -g AUDIT="off"
declare -gx CERT_MANAGER_VERSION="v1.21.1" declare -gx CERT_MANAGER_VERSION="v1.21.1"
declare -g CLUSTER="rig" declare -g CLUSTER="rig"
declare -gx COMPOSE_SHA256="db1889184726840f75c4f9c001048430d4f25b3be3cb084d3ddd762bc0aed576" declare -gx COMPOSE_SHA256="db1889184726840f75c4f9c001048430d4f25b3be3cb084d3ddd762bc0aed576"
@@ -64,9 +63,7 @@ load_config() {
declare -gx JQ_URL="https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-amd64" declare -gx JQ_URL="https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-amd64"
declare -gx JQ_VERSION="1.8.2" declare -gx JQ_VERSION="1.8.2"
declare -g K8S_VERSION="v1_36" declare -g K8S_VERSION="v1_36"
declare -g KIND_CONFIG="kind-config.yaml.tpl" declare -g KIND_CONFIG="./k8s/kind-config.yaml.tpl"
declare -g KIND_CONFIG_PATH="./k8s/kind-config.yaml.tpl"
declare -g KIND_CONFIG_SHOWN="ctrl/k8s/kind-config.yaml.tpl"
declare -gx KIND_SHA256="50030de23cf40a18505f20426f6a8506bedf13c6e509244bd1fa9463721b0f54" declare -gx KIND_SHA256="50030de23cf40a18505f20426f6a8506bedf13c6e509244bd1fa9463721b0f54"
declare -gx KIND_URL="https://github.com/kubernetes-sigs/kind/releases/download/v0.32.0/kind-linux-amd64" declare -gx KIND_URL="https://github.com/kubernetes-sigs/kind/releases/download/v0.32.0/kind-linux-amd64"
declare -gx KIND_VERSION="v0.32.0" declare -gx KIND_VERSION="v0.32.0"

View File

@@ -36,7 +36,7 @@ derive_port_base ()
render_kind_config () render_kind_config ()
{ {
local host_workdir="${HOST_WORKDIR:-$(cd .. && pwd)}"; local host_workdir="${HOST_WORKDIR:-$(cd .. && pwd)}";
sed -e "s|\${CLUSTER}|${CLUSTER}|g" -e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" -e "s|\${HTTP_PORT}|${HTTP_PORT}|g" -e "s|\${HOST_WORKDIR}|${host_workdir}|g" "$KIND_CONFIG_PATH" sed -e "s|\${CLUSTER}|${CLUSTER}|g" -e "s|\${NODE_IMAGE}|${NODE_IMAGE}|g" -e "s|\${HTTP_PORT}|${HTTP_PORT}|g" -e "s|\${HOST_WORKDIR}|${host_workdir}|g" "$KIND_CONFIG"
} }
# ── configuration, frozen for profile 'default' ── # ── configuration, frozen for profile 'default' ──
@@ -47,7 +47,6 @@ load_config() {
done done
declare -g ADDONS="" declare -g ADDONS=""
declare -gx AIRFLOW_IMAGE="apache/airflow:2.10.4" declare -gx AIRFLOW_IMAGE="apache/airflow:2.10.4"
declare -g AUDIT="off"
declare -gx CERT_MANAGER_VERSION="v1.21.1" declare -gx CERT_MANAGER_VERSION="v1.21.1"
declare -g CLUSTER="rig" declare -g CLUSTER="rig"
declare -gx COMPOSE_SHA256="db1889184726840f75c4f9c001048430d4f25b3be3cb084d3ddd762bc0aed576" declare -gx COMPOSE_SHA256="db1889184726840f75c4f9c001048430d4f25b3be3cb084d3ddd762bc0aed576"
@@ -64,9 +63,7 @@ load_config() {
declare -gx JQ_URL="https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-amd64" declare -gx JQ_URL="https://github.com/jqlang/jq/releases/download/jq-1.8.2/jq-linux-amd64"
declare -gx JQ_VERSION="1.8.2" declare -gx JQ_VERSION="1.8.2"
declare -g K8S_VERSION="v1_36" declare -g K8S_VERSION="v1_36"
declare -g KIND_CONFIG="kind-config.yaml.tpl" declare -g KIND_CONFIG="./k8s/kind-config.yaml.tpl"
declare -g KIND_CONFIG_PATH="./k8s/kind-config.yaml.tpl"
declare -g KIND_CONFIG_SHOWN="ctrl/k8s/kind-config.yaml.tpl"
declare -gx KIND_SHA256="50030de23cf40a18505f20426f6a8506bedf13c6e509244bd1fa9463721b0f54" declare -gx KIND_SHA256="50030de23cf40a18505f20426f6a8506bedf13c6e509244bd1fa9463721b0f54"
declare -gx KIND_URL="https://github.com/kubernetes-sigs/kind/releases/download/v0.32.0/kind-linux-amd64" declare -gx KIND_URL="https://github.com/kubernetes-sigs/kind/releases/download/v0.32.0/kind-linux-amd64"
declare -gx KIND_VERSION="v0.32.0" declare -gx KIND_VERSION="v0.32.0"