Files
unraid-podman/plugin/sbin/podman-autostart.sh
T
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

173 lines
6.0 KiB
Bash
Executable File

#!/bin/bash
# =============================================================================
# plugin/sbin/podman-autostart.sh
#
# "Autostart einrichten" — starts containers listed in
# /boot/config/plugins/podman/autostart, in file order, via the running
# Podman API service. Run by `rc.podman start` after the service is up.
# See docs/ARCHITECTURE.md section 12 (Autostart).
#
# Design constraints from the architecture doc, both implemented below:
# - A single container failing to start must NOT abort the rest of the
# chain (one broken container shouldn't take down everything else).
# - After repeated consecutive failures, a container is automatically
# paused out of the autostart chain ("Safe-Mode pro Container") so a
# persistently crash-looping container doesn't waste boot time or
# resources forever — with a GUI notification explaining why.
#
# File formats:
# autostart one container name per line; '#' starts a comment;
# blank lines ignored. Order = start order.
# autostart-delay optional "<container-name>=<seconds>" lines — sleep
# that many seconds AFTER starting that container
# before moving to the next (for startup dependencies,
# e.g. a database before the app that needs it).
#
# Failure tracking: a small per-container counter file under
# $PODMAN_BOOT_DIR/autostart-failures/<name> holds the consecutive-failure
# count. Reset to 0 on any successful start.
# =============================================================================
set -u
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./podman-common.sh
. "$SCRIPT_DIR/podman-common.sh"
podman_load_cfg
# After this many consecutive failed autostart attempts, a container is
# skipped and flagged rather than retried again on the next boot, until a
# human clears it (see clear_failure_flag below).
MAX_CONSECUTIVE_FAILURES=3
FAILURES_DIR="$PODMAN_BOOT_DIR/autostart-failures"
mkdir -p "$FAILURES_DIR"
# -----------------------------------------------------------------------------
# get_delay <container-name>
#
# Looks up an optional post-start delay for a container from
# autostart-delay ("name=seconds" lines). Returns 0 if not configured.
# -----------------------------------------------------------------------------
get_delay() {
local name="$1"
if [ -f "$PODMAN_AUTOSTART_DELAY_FILE" ]; then
awk -F= -v n="$name" '$1 == n { print $2; found=1 } END { if (!found) print 0 }' \
"$PODMAN_AUTOSTART_DELAY_FILE"
else
echo 0
fi
}
failure_count() {
local name="$1"
local f="$FAILURES_DIR/$name"
[ -f "$f" ] && cat "$f" || echo 0
}
record_failure() {
local name="$1"
local count
count=$(($(failure_count "$name") + 1))
echo "$count" > "$FAILURES_DIR/$name"
echo "$count"
}
clear_failure_flag() {
local name="$1"
rm -f "$FAILURES_DIR/$name"
}
# -----------------------------------------------------------------------------
# start_one <container-name>
#
# Starts a single container via `podman start`, which talks to the already
# running API service over $PODMAN_SOCKET rather than spawning an unrelated
# podman process tree — see docs/ARCHITECTURE.md section 11 (Container).
# -----------------------------------------------------------------------------
start_one() {
local name="$1"
local prior_failures
prior_failures=$(failure_count "$name")
if [ "$prior_failures" -ge "$MAX_CONSECUTIVE_FAILURES" ]; then
podman_log_error "autostart: skipping '$name' — $prior_failures consecutive prior failures (Safe-Mode). Remove $FAILURES_DIR/$name to re-enable."
return 1
fi
podman_log "autostart: starting '$name'"
if podman --url "unix://$PODMAN_SOCKET" start "$name" > /tmp/podman-autostart-"$name".log 2>&1; then
clear_failure_flag "$name"
podman_log "autostart: '$name' started successfully"
return 0
fi
local new_count
new_count=$(record_failure "$name")
podman_log_error "autostart: '$name' failed to start (attempt $new_count/$MAX_CONSECUTIVE_FAILURES) — see /tmp/podman-autostart-$name.log"
if [ "$new_count" -ge "$MAX_CONSECUTIVE_FAILURES" ]; then
podman_notify "Podman container disabled from autostart" \
"'$name' failed to start $new_count times in a row and has been paused from autostart. Fix the underlying issue, then remove $FAILURES_DIR/$name to re-enable." \
"warning"
fi
return 1
}
# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
if [ ! -f "$PODMAN_AUTOSTART_FILE" ]; then
podman_log "autostart: no autostart file at $PODMAN_AUTOSTART_FILE, nothing to do"
exit 0
fi
if [ ! -S "$PODMAN_SOCKET" ]; then
podman_log_error "autostart: podman API socket ($PODMAN_SOCKET) not present — is the service running?"
exit 1
fi
started=0
skipped=0
failed=0
while IFS= read -r raw_line || [ -n "$raw_line" ]; do
# Strip comments and surrounding whitespace; skip blank lines.
line="${raw_line%%#*}"
line="$(echo "$line" | xargs || true)"
[ -z "$line" ] && continue
if start_one "$line"; then
started=$((started + 1))
else
prior=$(failure_count "$line")
if [ "$prior" -ge "$MAX_CONSECUTIVE_FAILURES" ]; then
skipped=$((skipped + 1))
else
failed=$((failed + 1))
fi
# A single container's failure does not abort the loop — see file
# header. Continue to the next entry.
continue
fi
delay=$(get_delay "$line")
if [ "$delay" -gt 0 ] 2> /dev/null; then
podman_log "autostart: waiting ${delay}s after '$line' (configured dependency delay)"
sleep "$delay"
fi
done < "$PODMAN_AUTOSTART_FILE"
podman_log "autostart: complete (started=$started failed=$failed skipped-safe-mode=$skipped)"
# Exit non-zero only if EVERY entry failed outright (as opposed to a mix,
# or entries already in Safe-Mode) — rc.podman treats that as worth
# flagging loudly, whereas a partial failure is already individually
# notified above.
if [ "$started" -eq 0 ] && [ "$failed" -gt 0 ]; then
exit 1
fi
exit 0