diff --git a/serving/spdk/spdk-release.sh b/serving/spdk/spdk-release.sh index 2ca3cb7..326e78a 100755 --- a/serving/spdk/spdk-release.sh +++ b/serving/spdk/spdk-release.sh @@ -102,6 +102,20 @@ cmd_add() { [ -s "$dir/rootfs.erofs" ] || die "$dir/rootfs.erofs missing/empty" [ -s "$dir/rootfs.hash" ] || die "$dir/rootfs.hash missing/empty" + # Guard: the dir name must match the version inside verity.json. A mismatch + # means a stale/symlinked or mislabeled release dir (e.g. an old symlink into + # the version-pinned /artifacts that now resolves to a different build) — that + # would serve the wrong data/NQN/UUIDs under this name. Refuse loudly. (Legacy + # artifacts have no image_version field — skip the check for them.) + # Case-insensitive: the dir name comes from the OCI tag while verity.json's + # image_version comes from the build var, which can differ only in case — a + # benign skew. A genuine mismatch (e.g. pxe-… vs spdk-…) differs by far more. + local vv + vv=$(vfield "$v" .image_version) + if [ -n "$vv" ] && [ "${vv,,}" != "${v,,}" ]; then + die "release dir '$v' holds verity.json for version '$vv' (stale symlink / mislabeled dir). Refusing to add — remove '$dir' and re-stage." + fi + local nqn du hu s nqn=$(release_nqn "$v") du=$(vfield "$v" .data_ns_uuid) @@ -134,9 +148,11 @@ cmd_add() { rpc nvmf_subsystem_add_ns "$nqn" "rootfs_$s" -u "$du" rpc nvmf_subsystem_add_ns "$nqn" "roothash_$s" -u "$hu" rpc nvmf_subsystem_add_listener "$nqn" -t tcp -a "$TADDR" -s "$TPORT" - # Register the listener on the discovery subsystem too (best-effort; older - # SPDK answers discovery implicitly but logs a deprecation warning). - rpc nvmf_subsystem_add_listener "$DISCOVERY_NQN" -t tcp -a "$TADDR" -s "$TPORT" 2>/dev/null || true + # Ensure the discovery subsystem has this listener too. The target already + # registered it at startup, so this is normally a no-op that SPDK reports as + # "Listener already exists" — fully suppress stdout+stderr (rpc.py prints the + # JSON error to STDOUT, so 2>/dev/null alone wouldn't hide it). + rpc nvmf_subsystem_add_listener "$DISCOVERY_NQN" -t tcp -a "$TADDR" -s "$TPORT" >/dev/null 2>&1 || true echo "==> release $v ONLINE (existing connections untouched)" } diff --git a/serving/spdk/spdk-target.sh b/serving/spdk/spdk-target.sh index e448eb5..22021a5 100755 --- a/serving/spdk/spdk-target.sh +++ b/serving/spdk/spdk-target.sh @@ -65,23 +65,25 @@ rpc nvmf_create_transport -t TCP -u 16384 -m 8 -c 8192 \ || echo "WARNING: nvmf_create_transport failed (already present?)" >&2 echo "==> registering discovery listener ${TADDR}:${TPORT}" -rpc nvmf_subsystem_add_listener "$DISCOVERY_NQN" -t tcp -a "$TADDR" -s "$TPORT" 2>/dev/null || true +rpc nvmf_subsystem_add_listener "$DISCOVERY_NQN" -t tcp -a "$TADDR" -s "$TPORT" >/dev/null 2>&1 || true # Bootstrap convenience: if a single OCI artifact image is mounted at /artifacts # (the simple one-version `docker compose up` flow) and that version isn't yet # staged under $RELEASES_DIR, stage it so the long-lived target serves it too. -# Symlinks resolve inside the container (both are container paths); fall back to -# a copy if linking fails. +# +# COPY, never symlink: /artifacts is a version-pinned image volume that CHANGES +# when IMAGE_VERSION changes, so a symlink into it would silently re-point an +# already-staged release dir at the new version's data on the next bump. Each +# release dir must hold its own immutable copy. Self-heal: re-copy if the staged +# verity.json differs from the mounted artifact (repairs a dir left stale/symlinked +# by an older build). if [ -s /artifacts/verity.json ] && [ -n "${IMAGE_VERSION:-}" ]; then seed="$RELEASES_DIR/$IMAGE_VERSION" - if [ ! -e "$seed/verity.json" ]; then - echo "==> seeding bootstrap release $IMAGE_VERSION from /artifacts" + if [ ! -s "$seed/verity.json" ] || ! cmp -s /artifacts/verity.json "$seed/verity.json"; then + echo "==> seeding bootstrap release $IMAGE_VERSION from /artifacts (copy)" mkdir -p "$seed" - for f in rootfs.erofs rootfs.hash verity.json; do - ln -sf "/artifacts/$f" "$seed/$f" 2>/dev/null \ - || cp "/artifacts/$f" "$seed/$f" 2>/dev/null \ - || echo "WARNING: could not seed /artifacts/$f" >&2 - done + cp -f /artifacts/rootfs.erofs /artifacts/rootfs.hash /artifacts/verity.json "$seed/" \ + || echo "WARNING: failed to seed $IMAGE_VERSION from /artifacts" >&2 fi fi