le-app/docs/le-app-database-migration/phase2b-contained-runtime/tests/00-root-preinstall-validate.sh

105 lines
6.9 KiB
Bash
Raw Permalink Normal View History

#!/bin/sh
failures=0
artifact_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." 2>/dev/null && pwd)
pass() { printf 'PASS: %s\n' "$1"; }
fail() { printf 'FAIL: %s\n' "$1" >&2; failures=$((failures + 1)); }
run_check() {
label=$1
shift
if "$@"; then pass "$label"; else fail "$label"; fi
}
expect_stat() {
path=$1 expected=$2
actual=$(/usr/bin/stat -c '%u:%g:%a:%F' "$path" 2>/dev/null)
if [ "$actual" = "$expected" ]; then pass "$path ownership/mode"; else fail "$path expected $expected, got ${actual:-missing}"; fi
}
expect_absent() {
path=$1
if [ ! -e "$path" ] && [ ! -L "$path" ]; then pass "$path absent"; else fail "$path must not already exist"; fi
}
if [ "$(id -u)" = 0 ]; then pass 'running as root'; else fail 'must be run as root'; fi
if [ -n "$artifact_root" ] && [ -d "$artifact_root/payload" ]; then pass 'artifact root resolved'; else fail 'artifact root not found'; fi
if (cd "$artifact_root" && /usr/bin/sha256sum -c MANIFEST.sha256); then pass 'complete artifact manifest'; else fail 'complete artifact manifest'; fi
if (cd "$artifact_root/inventory" && /usr/bin/sha256sum -c SHA256SUMS); then pass 'original root inventory'; else fail 'original root inventory'; fi
run_check 'staged nftables syntax' /usr/bin/nft --check -f "$artifact_root/payload/etc/nftables.d/50-le-app-codex.nft"
system_unit_path="$artifact_root/payload/etc/systemd/system:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:/usr/lib/systemd/system"
systemd_output=$(SYSTEMD_UNIT_PATH="$system_unit_path" /usr/bin/systemd-analyze verify --recursive-errors=no user@1200.service user-1200.slice le-app-codex-netns.service 2>&1)
systemd_rc=$?
printf '%s\n' "$systemd_output"
systemd_unexpected=$(printf '%s\n' "$systemd_output" | /usr/bin/sed -e '\#le-app-codex-netns.service: Command /usr/local/libexec/le-app-codex-netns is not executable: No such file or directory#d')
if [ "$systemd_rc" -eq 0 ] || [ -z "$systemd_unexpected" ]; then pass 'staged system units/drop-ins via host search path'; else fail 'staged system units/drop-ins via host search path'; fi
user_unit_path="$artifact_root/payload/srv/le-app-codex/home/.config/systemd/user:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/lib/systemd/user"
user_systemd_output=$(SYSTEMD_UNIT_PATH="$user_unit_path" /usr/bin/systemd-analyze verify --recursive-errors=no docker.service 2>&1)
user_systemd_rc=$?
printf '%s\n' "$user_systemd_output"
user_systemd_unexpected=$(printf '%s\n' "$user_systemd_output" | /usr/bin/sed -e '\#docker.service: Command /usr/local/libexec/dockerd-rootless-29.6.1.sh is not executable: No such file or directory#d')
if [ "$user_systemd_rc" -eq 0 ] || [ -z "$user_systemd_unexpected" ]; then pass 'staged Docker user unit via host search path'; else fail 'staged Docker user unit via host search path'; fi
shell_failures=0
for file in "$artifact_root"/tests/*.sh "$artifact_root"/payload/usr/local/libexec/*; do
case "$file" in
*/30-install-stage1.sh|*/31-rollback-stage1.sh|*/32-stage1-state-tests.sh|*/stage1-state-lib.sh) /bin/bash -n "$file" || shell_failures=$((shell_failures + 1)) ;;
*) /bin/sh -n "$file" || shell_failures=$((shell_failures + 1)) ;;
esac
done
if [ "$shell_failures" -eq 0 ]; then pass 'shell syntax'; else fail 'shell syntax'; fi
json_failures=0
for file in "$artifact_root"/payload/srv/le-app-codex/home/.config/docker/daemon.json \
"$artifact_root"/payload/srv/le-app-codex/home/.docker/config.json \
"$artifact_root"/payload/srv/le-app-codex/home/.docker/contexts/meta/1b645e55bd6af77d420732b143423e7a27b12fbb5a71497e5d89c52481b6a535/meta.json \
"$artifact_root"/inventory/docker-networks.json; do
/usr/bin/jq empty "$file" || json_failures=$((json_failures + 1))
done
if [ "$json_failures" -eq 0 ]; then pass 'JSON syntax'; else fail 'JSON syntax'; fi
expect_stat /srv/le-app-codex '0:1200:750:directory'
expect_stat /srv/le-app-codex/home '1200:1200:700:directory'
expect_stat /srv/le-app-codex/lasereverything.net.db '1200:1200:700:directory'
expect_stat /srv/le-app-codex/database-source-readonly '0:1200:550:directory'
expect_stat /srv/le-app-codex/nonproduction-secrets '0:1200:750:directory'
expect_stat /srv/le-app-codex/container-runtime '1200:1200:700:directory'
expect_stat /srv/le-app-codex/testing-data '1200:1200:700:directory'
expect_stat /srv/le-app-codex/build-artifacts '1200:1200:700:directory'
project_mounts=$(/usr/bin/findmnt -rn -o TARGET | /usr/bin/awk '$0 == "/srv/le-app-codex" || index($0, "/srv/le-app-codex/") == 1 { print }')
if [ -z "$project_mounts" ]; then pass 'no project-rooted mounts'; else fail "unexpected project-rooted mounts: $project_mounts"; fi
expect_absent /etc/le-app-codex-runtime/OPERATOR-INPUTS-APPROVED
expect_absent /etc/le-app-codex-runtime/resolv.conf
expect_absent /etc/nftables.d/50-le-app-codex.nft
expect_absent /etc/systemd/system/le-app-codex-netns.service
expect_absent /etc/systemd/system/user-1200.slice.d/50-le-app-codex-resources.conf
expect_absent /etc/systemd/system/user@1200.service.d/50-le-app-codex-containment.conf
expect_absent /usr/local/libexec/dockerd-rootless-29.6.1.sh
expect_absent /usr/local/libexec/le-app-codex-netns
expect_absent /srv/le-app-codex/home/.config/systemd/user/docker.service
expect_absent /srv/le-app-codex/home/.config/docker/daemon.json
expect_absent /srv/le-app-codex/home/.docker/config.json
expect_absent /srv/le-app-codex/home/.docker/contexts/meta/1b645e55bd6af77d420732b143423e7a27b12fbb5a71497e5d89c52481b6a535/meta.json
expect_absent /srv/le-app-codex/phase2b-tests/00-root-preinstall-validate.sh
expect_absent /srv/le-app-codex/phase2b-tests/00-read-only-preflight.sh
expect_absent /srv/le-app-codex/phase2b-tests/10-inert-containment.sh
expect_absent /srv/le-app-codex/phase2b-tests/20-rootless-docker.sh
expect_absent /srv/le-app-codex/phase2b-tests/run-inert-without-user-manager.sh
active_state=$(/usr/bin/systemctl show user@1200.service -p ActiveState --value 2>/dev/null)
if [ "$active_state" = inactive ]; then pass 'user@1200.service inactive'; else fail "user@1200.service state is ${active_state:-unknown}"; fi
if [ ! -e /var/lib/systemd/linger/le_app_codex ] && [ ! -L /var/lib/systemd/linger/le_app_codex ] && [ ! -e /var/lib/systemd/linger/1200 ] && [ ! -L /var/lib/systemd/linger/1200 ]; then pass 'linger absent'; else fail 'linger present'; fi
uid_processes=$(/usr/bin/pgrep -u 1200 2>/dev/null)
if [ -z "$uid_processes" ]; then pass 'UID 1200 has no processes'; else fail "UID 1200 processes found: $uid_processes"; fi
if [ ! -e /run/user/1200/docker.sock ] && [ ! -L /run/user/1200/docker.sock ]; then pass 'rootless Docker socket absent'; else fail 'rootless Docker socket path exists'; fi
public_ipv4=$(/usr/bin/curl -4fsS --max-time 10 https://api.ipify.org 2>/dev/null)
if [ "$public_ipv4" = 74.67.173.56 ]; then pass 'public/NAT IPv4 unchanged'; else fail "public/NAT IPv4 drift or lookup failure: ${public_ipv4:-unavailable}"; fi
printf 'root pre-install validation failures=%s\n' "$failures"
[ "$failures" -eq 0 ]