Seven units installed INSIDE the Mac VM (/home/core/.config/containers/ systemd) by a new deploy_frankbook_stack handler: gluetun (ProtonVPN WireGuard, env-file credentials, publishes qbit's 8080/6881), qbittorrent (Network=gluetun.container, verbatim /Volumes data roots, first-boot config seed with fresh machine-local WebUI password and the Finished Torrents category), and prowlarr/sonarr/radarr/lidarr/readarr (grab-only, config migrated from dankserver at provisioning, readarr pinned to amd64-1.0.980). deploy.sh routes the new target through the ssh-config alias with REMOTE_SHELL=/usr/local/bin/bash (macOS bash 3.2 lacks associative arrays); the planner learns FRANKBOOK_SERVICES and the gluetun->qbittorrent netns pairing; new seam test pins the contract.
81 lines
3.8 KiB
Bash
Executable File
81 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bats
|
|
# Seam: the FrankBook-Pro quadlet units (gluetun, qbittorrent, five *arrs).
|
|
# These units are installed INSIDE the Mac's podman machine VM by the
|
|
# frankbook-pro handler in deploy-remote.sh. The contract pinned here:
|
|
# boot persistence, self-healing, the gluetun netns invariant (qBittorrent
|
|
# publishes no port of its own; gluetun publishes both), the verbatim
|
|
# plan-locked data-root mounts, and zero literal secrets in tracked files.
|
|
|
|
load helpers
|
|
|
|
FRANKBOOK_UNITS=(
|
|
gluetun/quadlet/gluetun
|
|
qbittorrent/quadlet/qbittorrent
|
|
prowlarr/quadlet/prowlarr
|
|
sonarr/quadlet/sonarr
|
|
radarr/quadlet/radarr
|
|
lidarr/quadlet/lidarr
|
|
readarr/quadlet/readarr
|
|
)
|
|
|
|
@test "all seven frankbook-pro units exist under frankbook-pro/<svc>/quadlet/" {
|
|
for unit in "${FRANKBOOK_UNITS[@]}"; do
|
|
[ -f "$REPO_ROOT/frankbook-pro/$unit.container" ] || return 1
|
|
done
|
|
}
|
|
|
|
@test "every frankbook-pro unit is boot-persistent and self-healing" {
|
|
for unit in "${FRANKBOOK_UNITS[@]}"; do
|
|
grep -q '^WantedBy=default.target$' "$REPO_ROOT/frankbook-pro/$unit.container" || return 1
|
|
grep -q '^Restart=always$' "$REPO_ROOT/frankbook-pro/$unit.container" || return 1
|
|
done
|
|
}
|
|
|
|
@test "qbittorrent joins gluetun's netns and publishes no port of its own" {
|
|
local unit="$REPO_ROOT/frankbook-pro/qbittorrent/quadlet/qbittorrent.container"
|
|
grep -q '^Network=gluetun.container$' "$unit" || return 1
|
|
grep -q '^Wants=gluetun.service$' "$unit" || return 1
|
|
grep -q '^After=gluetun.service$' "$unit" || return 1
|
|
! grep -q '^PublishPort=' "$unit" || return 1
|
|
}
|
|
|
|
@test "gluetun publishes qbittorrent's WebUI and torrent ports and needs NET_ADMIN+tun" {
|
|
local unit="$REPO_ROOT/frankbook-pro/gluetun/quadlet/gluetun.container"
|
|
grep -q '^PublishPort=8080:8080$' "$unit" || return 1
|
|
grep -q '^PublishPort=6881:6881$' "$unit" || return 1
|
|
grep -q '^PublishPort=6881:6881/udp$' "$unit" || return 1
|
|
grep -q '^AddCapability=NET_ADMIN$' "$unit" || return 1
|
|
grep -q '^AddDevice=/dev/net/tun:/dev/net/tun$' "$unit" || return 1
|
|
}
|
|
|
|
@test "gluetun credentials come from a machine-local env file, never literals" {
|
|
local unit="$REPO_ROOT/frankbook-pro/gluetun/quadlet/gluetun.container"
|
|
grep -q '^EnvironmentFile=/home/core/Containers/gluetun/env$' "$unit" || return 1
|
|
# Only ${VAR} expansions may carry the WireGuard material.
|
|
if grep -qE '^Environment=WIREGUARD_PRIVATE_KEY=[^$]' "$unit"; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
@test "data roots follow the plan-locked virtiofs layout" {
|
|
for svc in qbittorrent prowlarr sonarr radarr lidarr readarr; do
|
|
grep -q "^Volume=/Volumes/COMPLETE/$svc-data:/config$" \
|
|
"$REPO_ROOT/frankbook-pro/$svc/quadlet/$svc.container" || return 1
|
|
done
|
|
# qBittorrent additionally mounts both external disks verbatim: the
|
|
# seeded conf references the literal /Volumes paths.
|
|
local unit="$REPO_ROOT/frankbook-pro/qbittorrent/quadlet/qbittorrent.container"
|
|
grep -q '^Volume=/Volumes/COMPLETE:/Volumes/COMPLETE$' "$unit" || return 1
|
|
grep -q '^Volume=/Volumes/INCOMPLETE:/Volumes/INCOMPLETE$' "$unit" || return 1
|
|
}
|
|
|
|
@test "every arr serves its standard port and readarr keeps its pinned image" {
|
|
grep -q '^PublishPort=9696:9696$' "$REPO_ROOT/frankbook-pro/prowlarr/quadlet/prowlarr.container" || return 1
|
|
grep -q '^PublishPort=8989:8989$' "$REPO_ROOT/frankbook-pro/sonarr/quadlet/sonarr.container" || return 1
|
|
grep -q '^PublishPort=7878:7878$' "$REPO_ROOT/frankbook-pro/radarr/quadlet/radarr.container" || return 1
|
|
grep -q '^PublishPort=8686:8686$' "$REPO_ROOT/frankbook-pro/lidarr/quadlet/lidarr.container" || return 1
|
|
grep -q '^PublishPort=8787:8787$' "$REPO_ROOT/frankbook-pro/readarr/quadlet/readarr.container" || return 1
|
|
grep -q '^Image=lscr.io/linuxserver/readarr:amd64-1.0.980$' \
|
|
"$REPO_ROOT/frankbook-pro/readarr/quadlet/readarr.container" || return 1
|
|
}
|