Files
2026-06-27 19:29:19 +09:30

90 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# spdk-ctl.sh — host-side fleet release ops for the LONG-LIVED SPDK target.
#
# Stages a release's artifacts into ./releases/<version>/ and adds/removes it on
# the running nvmf_tgt ONLINE — no container restart, no dropped connections.
# This is the operator entrypoint for rolling updates and cleanup; it wraps the
# in-container spdk-release control script (driven via `docker compose exec`).
#
# ./spdk-ctl.sh load <version> [oci_ref] # stage + add live (oci_ref default: $OCI_IMAGE:<version>)
# ./spdk-ctl.sh unload <version> [--force] # drain-gated remove + delete staged files
# ./spdk-ctl.sh list # served versions + connected hosts
#
# Typical rollout:
# ./spdk-ctl.sh load 1.3.0 # serve the new version alongside the old
# # ... reboot hosts onto 1.3.0 (deployment manifest digest bump) ...
# ./spdk-ctl.sh list # watch 1.2.0 CONNS fall to 0
# ./spdk-ctl.sh unload 1.2.0 # cleanup once drained (refuses if hosts remain)
set -euo pipefail
cd "$(dirname "$0")"
RELEASES_DIR="${RELEASES_DIR:-./releases}"
SVC="${NVMEOF_SVC:-nvmeof}"
# Pull OCI_IMAGE default from the stack .env if present.
if [ -f ../.env ]; then
set -a; . ../.env; set +a
fi
# Run compose from the stack dir (one level up, where docker-compose.yml lives).
dc() { ( cd .. && docker compose "$@" ); }
usage() {
echo "usage: $0 {load <ver> [oci_ref] | unload <ver> [--force] | list}" >&2
exit 2
}
cmd_load() {
local v=${1:-}; [ -n "$v" ] || usage
local ref=${2:-}
if [ -z "$ref" ]; then
[ -n "${OCI_IMAGE:-}" ] || { echo "ERROR: set OCI_IMAGE in ../.env or pass an oci_ref" >&2; exit 1; }
ref="${OCI_IMAGE}:${v}"
fi
local dest="$RELEASES_DIR/$v"
if [ -s "$dest/verity.json" ]; then
echo "==> release $v already staged in $dest (skipping extract)"
else
echo "==> extracting $ref -> $dest"
mkdir -p "$dest"
local cid
cid=$(docker create "$ref")
# FROM-scratch artifact image: copy the three files the target needs.
docker cp "$cid:/artifacts/rootfs.erofs" "$dest/rootfs.erofs"
docker cp "$cid:/artifacts/rootfs.hash" "$dest/rootfs.hash"
docker cp "$cid:/artifacts/verity.json" "$dest/verity.json"
docker rm "$cid" >/dev/null
fi
echo "==> adding release $v on the live target (no restart)"
dc exec -T "$SVC" spdk-release add "$v"
dc exec -T "$SVC" spdk-release list
}
cmd_unload() {
local v="" force=""
local a
for a in "$@"; do
case "$a" in
--force) force="--force" ;;
*) v="$a" ;;
esac
done
[ -n "$v" ] || usage
echo "==> removing release $v from the live target"
if dc exec -T "$SVC" spdk-release remove "$v" ${force:+$force}; then
rm -rf "${RELEASES_DIR:?}/$v"
echo "==> staged artifacts for $v deleted from $RELEASES_DIR/$v"
else
echo "remove refused (version still has connected hosts?) — staged files left in place." >&2
echo "Drain those hosts first, or re-run with --force to evict them." >&2
exit 1
fi
}
case "${1:-}" in
load) shift; cmd_load "$@" ;;
unload|rm) shift; cmd_unload "$@" ;;
list|ls) dc exec -T "$SVC" spdk-release list ;;
*) usage ;;
esac