# The dev loop. `make tilt` from the project root, or `cd ctrl && tilt up`. # # This file ships with rig and works unedited: rig's own k8s/base already boots, # so `make tilt` comes up with a running cluster and no editing at all. What it # deploys is two EXAMPLES — replace them, and add your own images and resources # in the two marked sections near the bottom. The catalogue after them has the # blocks to paste, with the parts that are easy to get wrong already commented. # # rig supplies this file; it does not own it. Nothing in rig reads it back, and # nothing here is regenerated — edit it freely, the way you would edit # k8s/base/example-mock.yaml. rig owns the machine, you own the workload. # # Nothing below is hardcoded to this directory, deliberately. Every other # project here writes its slug into the Tiltfile five or six times by hand, so a # copy of the project deploys into the original's cluster until someone # remembers to edit all of them. A rig is meant to be copied and renamed, so it # asks instead. # ── who we are, and on which ports ───────────────────────────────────────── # One question to rig, answered by ctrl/ports.sh, which resolves it through # lib/config.sh — the same path every other rig script takes. That is the point: # the cluster name is NOT the bare directory name (it is lowercased and reduced # to a DNS label), and the ports honour anything pinned in ctrl/.env. Recomputing # either of those here in Starlark is how two copies end up disagreeing about # which cluster they are talking to. _facts = str(local('bash ports.sh active', quiet=True)).split() CLUSTER = _facts[0] CTX = _facts[1] HTTP = _facts[2] HTTPS = _facts[3] TILT = _facts[4] REGISTRY = _facts[5] # Where the manifests live. rig's own are the default; point MANIFESTS_DIR in # ctrl/.env at an overlay versioned somewhere else and rig stops owning them — # see k8s/README.md. Real manifests usually change on a different cadence, by # different people, under different review. # # The value is REPO-ROOT relative, because that is the root everything else in # rig is expressed against. This file runs in ctrl/, so prefix rather than # assume: '../' + 'ctrl/k8s/overlays/dev' and '../' + '../platform/overlays/dev' # are both right, where stripping a leading 'ctrl/' would only fix the first. MANIFESTS = '../' + _facts[6] # ── refuse to deploy into the wrong cluster ──────────────────────────────── # Tilt snapshots the kubectl context at startup, BEFORE parsing this file, so it # cannot be switched from here — only refused. `make tilt` passes --context for # you; this catches a bare `tilt up` after some other project moved the global # context. allow_k8s_contexts(CTX) if k8s_context() != CTX: fail("Wrong kubectl context: '%s'. This is %s — run: make tilt, or tilt up --context %s" % (k8s_context(), CLUSTER, CTX)) # The namespace has to exist before anything lands in it, and kustomize does not # guarantee ordering across resources. Creating it here is idempotent. local('kubectl --context %s create namespace %s --dry-run=client -o yaml | kubectl --context %s apply -f -' % (CTX, CLUSTER, CTX), quiet=True) # ── images go to this environment's own registry ─────────────────────────── # Fail closed. Tilt can usually infer the kind registry on its own, but "usually" # is an inference, and when it misses, an unqualified name like 'app' quietly # means docker.io/library/app — a push to the public index instead of the # registry two lines away. rig runs that registry; name it. default_registry('localhost:' + REGISTRY) k8s_yaml(kustomize(MANIFESTS)) # ── Images ───────────────────────────────────────────────────────────────── # (nothing yet — rig's examples run upstream images. Add docker_build calls here.) # ── Resources ────────────────────────────────────────────────────────────── # (nothing yet — add k8s_resource calls here to name and order what you deploy.) # Everything with no dev loop of its own, gathered so it does not clutter the UI. k8s_resource( objects=[CLUSTER + ':namespace'], new_name='infra', ) # ═══════════════════════════════════════════════════════════════════════════ # Catalogue — paste what you need, delete the rest. # # These are the shapes that recur across every project here, with the reasoning # kept next to them. They are comments so this file runs as-is. # ═══════════════════════════════════════════════════════════════════════════ # # ── build an image ───────────────────────────────────────────────────────── # The one genuinely non-obvious thing in the whole corpus: `context` and # `dockerfile` are relative to DIFFERENT directories, in adjacent arguments, # and nothing warns you. # # context= the REPO ROOT — this file is in ctrl/, so '..' # dockerfile= relative to THIS file — so 'Dockerfile.api' is ctrl/Dockerfile.api # # Every COPY inside those Dockerfiles is therefore repo-root relative: a file # sitting BESIDE the Dockerfile is still reached as `COPY ctrl/nginx.conf`. # # docker_build( # CLUSTER + '-api', # must match `image:` in the manifest — # context='..', # that string is the only thing # dockerfile='Dockerfile.api', # connecting the two # ignore=['.git', 'def', '.venv', 'node_modules', '__pycache__'], # live_update=[sync('../api', '/app/api')], # ) # # ── name and order a resource ────────────────────────────────────────────── # k8s_resource('api', resource_deps=['postgres'], labels=['app']) # k8s_resource('gateway', resource_deps=['api', 'ui'], labels=['app']) # # ── reload the gateway when its config changes ───────────────────────────── # A Caddyfile arriving via configMapGenerator with disableNameSuffixHash does # NOT roll the pod — the ConfigMap name never changes, so nothing tells the # Deployment anything happened. Without this you edit the routes and watch # nothing take effect. # # local_resource( # 'gateway-reload', # cmd='kubectl --context %s -n %s rollout restart deployment/gateway' % (CTX, CLUSTER), # deps=['k8s/base/Caddyfile'], # resource_deps=['gateway'], # auto_init=False, # ) # # ── an overlay whose secretGenerator reads outside its own directory ─────── # kustomize refuses to read above the kustomization root unless told to. Only # add this if you actually have such a generator; it loosens a safety check. # # k8s_yaml(kustomize(MANIFESTS, flags=['--load-restrictor=LoadRestrictionsNone'])) # # ── reach a service directly, bypassing the gateway ──────────────────────── # For a DB client or an admin UI. Prefer routing through the gateway: host ports # are a single shared namespace across every project on this machine, which is # why rig derives a block per environment in the first place. If you do need # one, take it from this environment's own block rather than picking a number. # # k8s_resource('postgres', port_forwards=[str(int(HTTP) + 5) + ':5432'])