Files
unraid-podman/plugin/sbin/podman-preflight.sh
maggesandClaude Sonnet 5 e2fefcdf9c
Build Packages / Build .txz packages (push) Failing after 9s
Lint / ShellCheck (push) Failing after 43s
Lint / Validate .plg XML (push) Successful in 10s
Lint / EditorConfig (push) Failing after 6s
Add reproducible build system, native Unraid plugin, and WebUI
- versions.env pins podman, conmon, crun, netavark, aardvark-dns, passt,
  and fuse-overlayfs to verified upstream source checksums; SlackBuild
  recipes, scripts/build-packages.sh, checksums.sh, release.sh, and
  update-versions.sh implement the reproducible pipeline; GitHub Actions
  workflows build in a Slackware container and publish releases without
  committing any binaries.

- plugin/podman.plg installs/updates/removes all eight packages (the
  seven components plus the plugin's own unraid-podman scaffolding
  package) via upgradepkg, using the official Unraid array-event hook
  mechanism (event/disks_mounted, event/stopping) instead of editing
  /boot/config/go. rc.podman and the sbin/ helper scripts implement
  storage creation, config seeding/sync, preflight checks, autostart
  with per-container Safe-Mode, and package verify/update/rollback.

- webui/plugins/podman implements the Dashboard, Containers, Pods,
  Images, Volumes, Networks, Logs, Terminal, Compose, and Settings
  panels against the approved mockup (webui/mockups/prototype.html),
  talking to podman system service exclusively via PodmanClient.php
  (libpod REST API over the Unix socket), with two documented
  exceptions: Terminal's one-shot exec model and Compose's use of the
  podman compose CLI, since libpod has no REST equivalent for either.

- docs/ARCHITECTURE.md and docs/ROADMAP.md record the design decisions
  and honest current status (syntax-checked, unit- and
  integration-tested against fake sockets/servers; not yet run against
  a real Unraid/Podman/Slackware system).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:51:14 +00:00

151 lines
6.3 KiB
Bash
Executable File

#!/bin/bash
# =============================================================================
# plugin/sbin/podman-preflight.sh
#
# Startup validation, run by `rc.podman start` BEFORE anything is mounted or
# started. The goal is to fail fast with one clear message, instead of
# letting `podman system service` fail three steps later with a cryptic
# error. See docs/ARCHITECTURE.md section 16.1 (Startup-Robustheit).
#
# Every check below is independent and all of them run even if an earlier
# one fails, so a single invocation reports every problem at once rather
# than forcing the user through a fix-one-rerun-find-the-next loop.
#
# Exit code: 0 if every check passed, 1 if any failed (with all failures
# already logged/notified by that point).
# =============================================================================
set -u # deliberately not -e: see the "run every check" note above
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./podman-common.sh
. "$SCRIPT_DIR/podman-common.sh"
podman_load_cfg
FAILURES=0
fail() {
podman_log_error "preflight: $1"
FAILURES=$((FAILURES + 1))
}
ok() {
podman_log "preflight: OK - $1"
}
# -----------------------------------------------------------------------------
# 1. Required binaries are actually installed.
# -----------------------------------------------------------------------------
for bin in podman conmon crun mount umount xfs_repair mkfs.xfs; do
if command -v "$bin" > /dev/null 2>&1; then
ok "$bin found"
else
fail "required binary '$bin' not found on PATH — is the plugin fully installed?"
fi
done
for helper in /usr/libexec/podman/netavark /usr/libexec/podman/aardvark-dns; do
if [ -x "$helper" ]; then
ok "$helper found"
else
fail "required helper '$helper' not found — is the plugin fully installed?"
fi
done
# -----------------------------------------------------------------------------
# 2. Storage path configured, safe, and mounted (cache pool / disk, not
# /mnt/user — see docs/ARCHITECTURE.md section 4.3).
# -----------------------------------------------------------------------------
if podman_storage_path_is_safe "$STORAGE_PATH" 2> /tmp/podman-preflight-storage-safety.log; then
ok "STORAGE_PATH ($STORAGE_PATH) is not under /mnt/user"
else
fail "$(cat /tmp/podman-preflight-storage-safety.log)"
fi
rm -f /tmp/podman-preflight-storage-safety.log
if [ -d "$STORAGE_PATH" ] && mountpoint -q "$STORAGE_PATH" 2> /dev/null; then
ok "STORAGE_PATH ($STORAGE_PATH) is a mounted filesystem"
elif [ -d "$STORAGE_PATH" ]; then
# Not every valid target is a mountpoint itself (e.g. a subdirectory of a
# cache pool root) — warn rather than fail, but only if the parent chain
# is mounted somewhere.
if findmnt -T "$STORAGE_PATH" > /dev/null 2>&1; then
ok "STORAGE_PATH ($STORAGE_PATH) resolves onto a mounted filesystem"
else
fail "STORAGE_PATH ($STORAGE_PATH) does not appear to be on a mounted filesystem"
fi
else
fail "STORAGE_PATH ($STORAGE_PATH) does not exist — is the configured cache pool/disk present and started?"
fi
# -----------------------------------------------------------------------------
# 3. Free space check (only meaningful once STORAGE_PATH exists).
# -----------------------------------------------------------------------------
if [ -d "$STORAGE_PATH" ]; then
available_kb=$(df --output=avail -k "$STORAGE_PATH" 2> /dev/null | tail -n1 | tr -d '[:space:]')
if [ -n "${available_kb:-}" ]; then
available_gb=$((available_kb / 1024 / 1024))
if [ -f "$PODMAN_STORAGE_IMAGE" ]; then
# Image already exists — just warn if the pool itself is nearly full,
# since podman.img growth or new image pulls need headroom too.
if [ "$available_gb" -lt 2 ]; then
fail "less than 2G free on $STORAGE_PATH (${available_gb}G) — image pulls will likely fail"
else
ok "${available_gb}G free on $STORAGE_PATH"
fi
elif [ "$available_gb" -lt "$STORAGE_IMAGE_SIZE_GB" ]; then
fail "not enough free space on $STORAGE_PATH to create a ${STORAGE_IMAGE_SIZE_GB}G podman.img (only ${available_gb}G free)"
else
ok "${available_gb}G free on $STORAGE_PATH (enough for a fresh ${STORAGE_IMAGE_SIZE_GB}G podman.img)"
fi
fi
fi
# -----------------------------------------------------------------------------
# 4. Kernel supports cgroup v2 (required by crun/netavark's expectations).
# -----------------------------------------------------------------------------
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
ok "cgroup v2 unified hierarchy is active"
else
fail "cgroup v2 unified hierarchy not detected (/sys/fs/cgroup/cgroup.controllers missing) — check Unraid's syslinux cgroup boot parameters"
fi
# -----------------------------------------------------------------------------
# 5. No orphaned socket/pidfile from a previous unclean shutdown.
# -----------------------------------------------------------------------------
if [ -S "$PODMAN_SOCKET" ]; then
if [ -f "$PODMAN_SERVICE_PID_FILE" ] && kill -0 "$(cat "$PODMAN_SERVICE_PID_FILE")" 2> /dev/null; then
fail "podman system service already appears to be running (pid $(cat "$PODMAN_SERVICE_PID_FILE")) — is rc.podman already started?"
else
podman_log "preflight: removing orphaned socket $PODMAN_SOCKET from a previous unclean shutdown"
rm -f "$PODMAN_SOCKET"
ok "cleared orphaned socket"
fi
else
ok "no orphaned podman.sock"
fi
# -----------------------------------------------------------------------------
# 6. Boot-config directory exists (i.e. 'seed' has run at least once).
# -----------------------------------------------------------------------------
if [ -f "$PODMAN_CFG_FILE" ]; then
ok "$PODMAN_CFG_FILE present"
else
fail "$PODMAN_CFG_FILE missing — run 'podman-config.sh seed' (should happen automatically on install)"
fi
# -----------------------------------------------------------------------------
# Summary
# -----------------------------------------------------------------------------
if [ "$FAILURES" -gt 0 ]; then
podman_notify "Podman preflight checks failed" \
"$FAILURES check(s) failed — podman was not started. See $PODMAN_PLUGIN_LOG for details." \
"alert"
podman_log_error "preflight: $FAILURES check(s) failed, aborting start"
exit 1
fi
podman_log "preflight: all checks passed"
exit 0