Root cause of a fresh-install "cannot reach the Podman API socket" report (a friend's Unraid box, cache pool present and mounted): unlike Docker-for-Unraid's docker.img path, this plugin never auto-created STORAGE_PATH itself — only podman.img inside it. A perfectly normal, already-mounted cache pool still failed preflight/storage-create with "does not exist", just because its own .../system/podman subdirectory had never been created. Fixed by walking up to the nearest existing ancestor and checking whether it's on a different device than / (real mount vs. nothing mounted at all) — see podman-common.sh's new podman_path_has_real_mount_ancestor(), used by both podman-preflight.sh and podman-storage.sh. Settings gets a "Podman Service" card (status chip + Start/Restart, backed by new ajax/settings.php service_status/start/restart actions that just shell out to rc.podman) so a fresh install that failed to start can be diagnosed and retried without SSH/terminal access at all — exactly what was missing when this was first needed live. Also adds "Format a Disk for Podman Storage" (new ajax/disks.php) for a single-disk system with no cache pool at all. Only ever lists disks with literally no existing partition/filesystem/RAID-or-ZFS-membership signature and that aren't Unraid's boot flash — found live, twice, during development: the boot USB (FAT, labeled "UNRAID") passed the initial mounted-only check because this host's /boot is backed by a ZFS dataset rather than a direct partition mount, and active RAID-member cache disks passed a data-vs-blank *warning* rather than a hard exclusion. Both are now excluded outright, not just flagged — see disks.php's device_or_children_labeled_unraid() and the hasData exclusion in list_candidate_disks(). A disk formatted this way is remounted by UUID on every boot via a new plugin/sbin/podman-mount-managed-disk.sh, called from plugin/event/disks_mounted before rc.podman start. Unrelated fix bundled in: scripts/lib/slackbuild-common.sh now sets SOURCE_DATE_EPOCH (derived from the repo's last commit) before calling makepkg, so two separate builds of the same commit produce byte-identical .txz files — makepkg already supports this (`--clamp-mtime` when $SOURCE_DATE_EPOCH is set, confirmed by reading a real host's /sbin/makepkg) but nothing was setting the variable, so release.yml's "rebuild in CI and verify it matches the committed checksums" step was guaranteed to fail on the first package it checked alphabetically (observed live: aardvark-dns). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
159 lines
6.8 KiB
Bash
Executable File
159 lines
6.8 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
|
|
elif podman_path_has_real_mount_ancestor "$STORAGE_PATH"; then
|
|
# The leaf directory doesn't exist yet, but a real filesystem IS mounted
|
|
# somewhere along its path (e.g. the cache pool itself) — podman-storage.sh
|
|
# create will mkdir -p it. Not a failure; see that check's own comment
|
|
# for the live bug this used to cause (a normal, already-mounted cache
|
|
# pool failing preflight just because its .../system/podman subdirectory
|
|
# had never been created).
|
|
ok "STORAGE_PATH ($STORAGE_PATH) doesn't exist yet, but resolves onto a mounted filesystem — will be created"
|
|
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
|