Files
unraid-podman/plugin/sbin/podman-backup.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

169 lines
5.5 KiB
Bash
Executable File

#!/bin/bash
# =============================================================================
# plugin/sbin/podman-backup.sh
#
# Rollback support — see docs/ARCHITECTURE.md section 14 (Rollback). Two
# independent kinds of backup, matching the two kinds of thing an update can
# break:
#
# PACKAGES plugin/podman.plg downloads each release's .txz files
# straight into $PODMAN_BOOT_DIR/backup/packages/<plugin-version>/
# — all 7 component packages of one plugin release share a
# single directory named after the PLUGIN version (not each
# component's own version), since a rollback means "go back to
# plugin release X" as one unit — rather than into a scratch
# dir that gets discarded, so the previously installed
# version's packages are automatically still on disk after an
# update, with no separate copy step. `restore-packages
# <plugin-version>` reinstalls every package from one of those
# directories.
#
# CONFIG Before a config schema migration (see podman.cfg's
# CONFIG_SCHEMA_VERSION), the plg's postinstall calls
# `snapshot-config` to copy the current *.conf + podman.cfg
# into $PODMAN_BOOT_DIR/backup/config/<timestamp>/.
# `restore-config <timestamp>` copies them back.
#
# `prune` bounds how many old snapshots of each kind are kept, since flash
# space is limited (see ARCHITECTURE.md section 14, "Rollback-Historie
# begrenzen").
#
# Usage:
# podman-backup.sh snapshot-config
# podman-backup.sh restore-config <timestamp>
# podman-backup.sh restore-packages <version>
# podman-backup.sh list
# podman-backup.sh prune [--keep N]
# =============================================================================
set -eu
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./podman-common.sh
. "$SCRIPT_DIR/podman-common.sh"
DEFAULT_KEEP=3
cmd_snapshot_config() {
local ts
ts=$(date -u +%Y%m%dT%H%M%SZ)
local dest="$PODMAN_BACKUP_DIR/config/$ts"
mkdir -p "$dest"
local f
for f in podman.cfg containers.conf storage.conf registries.conf policy.json; do
[ -f "$PODMAN_BOOT_DIR/$f" ] && cp "$PODMAN_BOOT_DIR/$f" "$dest/"
done
podman_log "backup: config snapshot saved to $dest"
echo "$ts"
}
cmd_restore_config() {
local ts="${1:?usage: podman-backup.sh restore-config <timestamp>}"
local src="$PODMAN_BACKUP_DIR/config/$ts"
if [ ! -d "$src" ]; then
podman_log_error "backup: no config snapshot found at $src"
exit 1
fi
local f
for f in "$src"/*; do
[ -f "$f" ] || continue
cp "$f" "$PODMAN_BOOT_DIR/$(basename "$f")"
done
podman_log "backup: config restored from snapshot $ts — restart podman (rc.podman restart) to apply"
}
cmd_restore_packages() {
local version="${1:?usage: podman-backup.sh restore-packages <version>}"
local src="$PODMAN_BACKUP_DIR/packages/$version"
if [ ! -d "$src" ]; then
podman_log_error "backup: no package backup found for version $version at $src"
exit 1
fi
podman_require_command upgradepkg
local txz
local restored=0
for txz in "$src"/*.txz; do
[ -f "$txz" ] || continue
podman_log "backup: reinstalling $(basename "$txz") from backup (version $version)"
upgradepkg --reinstall --install-new "$txz"
restored=$((restored + 1))
done
if [ "$restored" -eq 0 ]; then
podman_log_error "backup: no .txz files found in $src"
exit 1
fi
podman_notify "Podman packages rolled back" \
"Reinstalled $restored package(s) from the version $version backup. Run 'rc.podman restart' to apply." \
"warning"
podman_log "backup: package rollback to version $version complete ($restored package(s))"
}
cmd_list() {
echo "Config snapshots ($PODMAN_BACKUP_DIR/config/):"
if [ -d "$PODMAN_BACKUP_DIR/config" ]; then
find "$PODMAN_BACKUP_DIR/config" -mindepth 1 -maxdepth 1 -type d -printf ' %f\n' 2> /dev/null | sort -r
fi
echo
echo "Package backups ($PODMAN_BACKUP_DIR/packages/):"
if [ -d "$PODMAN_BACKUP_DIR/packages" ]; then
find "$PODMAN_BACKUP_DIR/packages" -mindepth 1 -maxdepth 1 -type d -printf ' %f\n' 2> /dev/null | sort -r
fi
}
cmd_prune() {
local keep="$DEFAULT_KEEP"
if [ "${1:-}" = "--keep" ] && [ -n "${2:-}" ]; then
keep="$2"
fi
local kind
for kind in config packages; do
local dir="$PODMAN_BACKUP_DIR/$kind"
[ -d "$dir" ] || continue
# Sort newest-first by directory name (timestamps and semver both sort
# correctly lexicographically here — timestamps are zero-padded ISO8601,
# versions are compared via `sort -V`), then remove everything beyond
# $keep.
local sorted
if [ "$kind" = "config" ]; then
sorted=$(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -r)
else
sorted=$(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -rV)
fi
local i=0
local name
while IFS= read -r name; do
[ -z "$name" ] && continue
i=$((i + 1))
if [ "$i" -gt "$keep" ]; then
podman_log "backup: pruning old $kind backup: $name"
rm -rf "${dir:?}/$name"
fi
done <<< "$sorted"
done
}
case "${1:-}" in
snapshot-config) cmd_snapshot_config ;;
restore-config) shift; cmd_restore_config "$@" ;;
restore-packages) shift; cmd_restore_packages "$@" ;;
list) cmd_list ;;
prune) shift; cmd_prune "$@" ;;
*)
echo "usage: $0 {snapshot-config|restore-config <ts>|restore-packages <version>|list|prune [--keep N]}" >&2
exit 1
;;
esac