Files
serverAppConfigs/tests/caddy_quadlet.bats
T
Frank 8660209c35 caddy + pihole: make both boot-persistent and self-healing
Caddy left compose.yaml for a quadlet unit (caddy.service): joins
dankclaw_default for container-name upstreams, Pull=never for the local-only
image, Porkbun credentials via /home/dank/Containers/caddy/env written by
deploy from .env, bare-IP-catch-all health check. deploy_caddy installs,
enables, and starts the unit after retiring the bare container (state is in
host volumes).

Pi-hole: the repo now owns the sysctl drop-in
(dankclaw/pihole/sysctl/99-dnsmasq.conf, ip_unprivileged_port_start=53);
deploy_pihole installs it to /etc/sysctl.d/, stages conflicting later-sorted
drop-ins in the home trash, and applies 'sysctl --system' (the boot code
path). This fixes the 2026-08-26 crash loop (port-53 bind denied, restart
counter 1645) caused by a second drop-in resetting the value to 80.

Tests: new caddy_quadlet.bats seam; caddyfile/deployment_flow assertions
repointed from the removed compose service to the quadlet; pihole.bats pins
the sysctl drop-in and its deployment; test-changed.sh routes dankclaw/caddy/
and the pihole subtree to their seams. Docs: Caddy, Pi-hole, Dankclaw Stack,
work log.
2026-08-26 19:52:57 -07:00

64 lines
2.3 KiB
Bash

#!/usr/bin/env bats
# Seam: canonical Caddy as a boot-persistent, self-healing quadlet on
# dankclaw. It left compose.yaml on 2026-08-26 after a reboot exposed that a
# bare compose container never comes back (rootless Podman has no boot-time
# auto-start for bare containers; `restart: unless-stopped` does not survive
# the explicit stop that shutdown performs). See AGENTS.md "Boot Persistence
# & Self-Healing" and docs/Infrastructure/Caddy.md.
load helpers
CADDY_DIR="$REPO_ROOT/dankclaw/caddy"
QUADLET="$CADDY_DIR/quadlet/caddy.container"
ENV_EXAMPLE="$CADDY_DIR/env.example"
@test "the caddy quadlet container file exists" {
[ -f "$QUADLET" ]
}
@test "the caddy quadlet is installed for boot" {
grep -q 'WantedBy=default.target' "$QUADLET"
}
@test "the caddy quadlet restarts on failure (self-healing)" {
grep -q '^Restart=always' "$QUADLET"
grep -qE '^RestartSec=[1-9][0-9]*$' "$QUADLET"
}
@test "the caddy quadlet publishes 80 and 443 to the LAN" {
grep -qE '^PublishPort=0\.0\.0\.0:80:80/tcp$' "$QUADLET"
grep -qE '^PublishPort=0\.0\.0\.0:443:443/tcp$' "$QUADLET"
}
@test "the caddy quadlet joins the compose network for container-name upstreams" {
# The Caddyfile reverse-proxies to container names (authentik-server,
# grafana, ...); those resolve only on the shared bridge.
grep -q '^Network=dankclaw_default$' "$QUADLET"
}
@test "the caddy quadlet never pulls the local-only image at boot" {
grep -q '^Pull=never$' "$QUADLET"
}
@test "the caddy quadlet reads its env file and mounts its state" {
grep -q '^EnvironmentFile=/home/dank/Containers/caddy/env$' "$QUADLET"
grep -qE '^Volume=/home/dank/Containers/caddy/Caddyfile:/etc/caddy/Caddyfile:ro$' "$QUADLET"
grep -qE '^Volume=/home/dank/Containers/caddy/data:/data$' "$QUADLET"
grep -qE '^Volume=/home/dank/Containers/caddy/config:/config$' "$QUADLET"
grep -qE '^Volume=/srv/homepage:/srv/homepage:ro$' "$QUADLET"
}
@test "the caddy env example has placeholder values only" {
[ -f "$ENV_EXAMPLE" ]
grep -q '^PORKBUN_API_KEY=changeme$' "$ENV_EXAMPLE"
grep -q '^PORKBUN_API_SECRET_KEY=changeme$' "$ENV_EXAMPLE"
}
@test "no secret material in the caddy files" {
local f
while IFS= read -r f; do
has_no_leaked_secret "$f"
has_no_leaked_sudo_pw "$f"
done < <(find "$CADDY_DIR" -type f | sort)
}