v0.20.0: golden stacks-dir bind + per-guest hostname/CT + bake base-infra images
build-golden.sh: same-path /opt/docker/stacks host bind (Section-G fix, breaks all bind-mounted stacks without it) + --hostname <customer-id> from bootstrap.json (portable sed parse, no jq) + bake the 3 pinned public base-infra images with a manifest-inspect hard gate. Provision --selftest defaults -hostname to the DNS-safe-sanitized customer-id so the CT/LXC is named meaningfully. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+39
-4
@@ -73,6 +73,25 @@ pct exec "$VMID" -- bash -c "docker logout '$REGISTRY_HOST' >/dev/null 2>&1 || t
|
||||
# Record the baked image ref for the bootstrap unit (so the unit needs no login/pull).
|
||||
pct exec "$VMID" -- bash -c "printf '%s\n' '$CONTROLLER_IMAGE' > /etc/felhom-controller-image"
|
||||
|
||||
# Bake the base-infrastructure images (traefik, cloudflared, filebrowser) so the controller's
|
||||
# first-boot bring-up (EnsureBaseStack) is OFFLINE-capable — no registry pull at deploy. These are
|
||||
# PUBLIC Docker Hub images (no cred needed). The PINNED tags MUST match the controller's
|
||||
# internal/infra constants (TraefikImage / CloudflaredImage / FileBrowserImage); a drift means the
|
||||
# golden bakes one image and the controller requests another (→ a pull at deploy, defeating the goal).
|
||||
INFRA_IMAGES=(
|
||||
"traefik:v3.6.7"
|
||||
"cloudflare/cloudflared:2026.6.0"
|
||||
"gtstef/filebrowser:1.3.3-stable"
|
||||
)
|
||||
echo "[golden] baking base-infra images: ${INFRA_IMAGES[*]} …"
|
||||
for img in "${INFRA_IMAGES[@]}"; do
|
||||
# Hard gate: fail loudly BEFORE pulling if a pinned tag doesn't resolve (a bad pin otherwise fails
|
||||
# mid-bake with a confusing error).
|
||||
pct exec "$VMID" -- bash -c "docker manifest inspect '$img' >/dev/null 2>&1" \
|
||||
|| { echo "[golden] FATAL: pinned base-infra image does not resolve: $img"; exit 1; }
|
||||
pct exec "$VMID" -- bash -c "docker pull '$img'"
|
||||
done
|
||||
|
||||
echo "[golden] baking the controller-bootstrap unit (deploys the BAKED controller from the config mount) …"
|
||||
pct push "$VMID" /dev/stdin /usr/local/sbin/felhom-controller-bootstrap.sh --perms 700 <<'BOOTSH'
|
||||
#!/bin/bash
|
||||
@@ -85,16 +104,32 @@ CFG=/etc/felhom-bootstrap/bootstrap.json
|
||||
[ -r "$CFG" ] || { echo "[ctrl-bootstrap] no $CFG — not provisioned, nothing to do"; exit 0; }
|
||||
IMAGE=$(cat /etc/felhom-controller-image 2>/dev/null || true)
|
||||
[ -n "$IMAGE" ] || { echo "[ctrl-bootstrap] FATAL: /etc/felhom-controller-image missing"; exit 1; }
|
||||
echo "[ctrl-bootstrap] deploying $IMAGE from $CFG"
|
||||
# Per-guest container hostname (slice base-infra/3A): derive from the bootstrap's customer.id so the
|
||||
# controller's os.Hostname() (its hub-reported hostname) is the customer id, not the Docker container
|
||||
# ID. Portable, dependency-free parse (NO jq in the golden) — bootstrap.json has exactly one "id" key
|
||||
# (customer.id). Falls back to no --hostname if the parse yields nothing (fail-safe).
|
||||
CUSTOMER_ID=$(sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$CFG" | head -1)
|
||||
HOSTNAME_ARG=""
|
||||
[ -n "$CUSTOMER_ID" ] && HOSTNAME_ARG="--hostname $CUSTOMER_ID"
|
||||
echo "[ctrl-bootstrap] deploying $IMAGE from $CFG (hostname=${CUSTOMER_ID:-<unset>})"
|
||||
docker rm -f felhom-controller >/dev/null 2>&1 || true
|
||||
# Section-G fix (base-infra slice): the controller writes app/infra compose stacks under
|
||||
# /opt/docker/stacks INSIDE the container, but `docker compose up` is executed by the GUEST daemon
|
||||
# (shared socket), which resolves every relative bind source on the GUEST filesystem. Without a
|
||||
# SAME-PATH host bind for /opt/docker/stacks, the daemon can't see those bind sources and silently
|
||||
# creates empty dirs → every bind-mounted stack (base infra AND customer apps) breaks. A named volume
|
||||
# would NOT fix this (it resolves to /var/lib/docker/volumes/...). Pre-create the dir, then same-path bind.
|
||||
mkdir -p /opt/docker/stacks
|
||||
# slice 8C: the controller is DE-PRIVILEGED — disk execution (scan/format/mount/migrate) is the
|
||||
# host agent's job now, so this run grants NO disk privileges: no --privileged, no /dev, no
|
||||
# /etc/fstab, no rshared /mnt. Only the bootstrap config (ro), the data volume, and the docker
|
||||
# socket (app/stack management). The controller reaches the agent's local API for disk management.
|
||||
docker run -d --name felhom-controller --restart unless-stopped \
|
||||
# /etc/fstab, no rshared /mnt. Only the bootstrap config (ro), the data volume, the stacks dir
|
||||
# (same-path host bind), and the docker socket (app/stack management). The controller reaches the
|
||||
# agent's local API for disk management.
|
||||
docker run -d --name felhom-controller --restart unless-stopped $HOSTNAME_ARG \
|
||||
-e FELHOM_BOOTSTRAP_PATH=/etc/felhom-bootstrap/bootstrap.json \
|
||||
-v /etc/felhom-bootstrap:/etc/felhom-bootstrap:ro \
|
||||
-v felhom-controller-data:/opt/docker/felhom-controller \
|
||||
-v /opt/docker/stacks:/opt/docker/stacks \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
"$IMAGE"
|
||||
echo "[ctrl-bootstrap] controller started"
|
||||
|
||||
Reference in New Issue
Block a user