#!/usr/bin/env bash # assert-bundle.sh — bundle invariant checks for the diagnostics e2e gate. # # Given a finalized tarball produced by `swactor-diag-collector`, runs # the post-processor against it and verifies the structural properties # the S12 plan requires: # # 1. The manifest lists the orchestrator and every stage. # 2. Each node has at least one snapshot. # 3. Events from each tier (1: SWIM/dial/message; 2: iroh-internal; # 3: probe/host/process) appear somewhere in the bundle. # 4. `swactor-diag-postproc` exits 0. # 5. The produced `summary.md` is non-empty. # # Usage: # assert-bundle.sh [] # # Exits 0 on success, prints a diagnostic to stderr and exits 1 on any # failure. Designed to be called from `docker-diag-e2e.sh` but usable # standalone for ad-hoc verification of a captured bundle. set -euo pipefail BUNDLE="${1:?usage: assert-bundle.sh [postproc]}" EXPECTED_STAGES="${2:?usage: assert-bundle.sh [postproc]}" POSTPROC_BIN="${3:-swactor-diag-postproc}" if [ ! -s "$BUNDLE" ]; then echo "assert-bundle: $BUNDLE does not exist or is empty" >&2 exit 1 fi WORKDIR="$(mktemp -d -t pp-diag-assert.XXXXXX)" trap 'rm -rf "$WORKDIR"' EXIT EXTRACTED="$WORKDIR/extracted" mkdir -p "$EXTRACTED" tar -xzf "$BUNDLE" -C "$EXTRACTED" # The bundle's top-level dir is named after the run id. We don't need to # know the name in advance — walk one level down. RUN_DIR=$(find "$EXTRACTED" -mindepth 1 -maxdepth 1 -type d | head -n 1) if [ -z "$RUN_DIR" ]; then echo "assert-bundle: bundle has no top-level run directory" >&2 ls -lR "$EXTRACTED" >&2 exit 1 fi echo "assert-bundle: run dir $RUN_DIR" # (1) Manifest sanity. MANIFEST="$RUN_DIR/MANIFEST.json" if [ ! -s "$MANIFEST" ]; then echo "assert-bundle: MANIFEST.json missing or empty in $RUN_DIR" >&2 exit 1 fi # Friendly node labels are written into the per-node subdirs (e.g. # `orchestrator`, `stage-0`, ...). Count them, and check that # orchestrator + every stage 0..N-1 are present. The label scheme is # the collector's `bundle::assemble` contract. node_dirs=() while IFS= read -r -d '' dir; do node_dirs+=("$(basename "$dir")") done < <(find "$RUN_DIR" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) echo "assert-bundle: node dirs: ${node_dirs[*]:-}" declare -A seen for d in "${node_dirs[@]}"; do seen["$d"]=1 done missing=() [ -n "${seen[orchestrator]:-}" ] || missing+=("orchestrator") for ((i = 0; i < EXPECTED_STAGES; i++)); do [ -n "${seen[stage-$i]:-}" ] || missing+=("stage-$i") done if [ "${#missing[@]}" -gt 0 ]; then echo "assert-bundle: bundle missing nodes: ${missing[*]}" >&2 echo "(found: ${node_dirs[*]})" >&2 exit 1 fi echo "assert-bundle: orchestrator + $EXPECTED_STAGES stages all present" # (2) Each node has at least one snapshot. The collector bundle layout # (see `collector::bundle::assemble`) groups records into per-kind # subdirs under each node label: `