CP2A2: rq-historical build-recovery-gold Kanal (fix Context/Dockerfile/Tag, 7 Guards, DIRECT-Tag no-latest, verify-recovery-gold-image, Host-Legacy-Guard)
This commit is contained in:
parent
efe13ec92d
commit
c6046feaf2
1 changed files with 254 additions and 3 deletions
|
|
@ -113,6 +113,39 @@ declare -A FORBIDDEN_EXTRA=(
|
||||||
RECOVERY_GOLD_CID="634cc505c7bc4c361305e5b415f107e98411faf38e8a29269dd8b1e4d1363b32"
|
RECOVERY_GOLD_CID="634cc505c7bc4c361305e5b415f107e98411faf38e8a29269dd8b1e4d1363b32"
|
||||||
RECOVERY_GOLD_TAG="historical-v2-historical-service:recovery-gold-container-20260829"
|
RECOVERY_GOLD_TAG="historical-v2-historical-service:recovery-gold-container-20260829"
|
||||||
|
|
||||||
|
# ------------------------------------------------- RECOVERY-GOLD-REBUILD-KANAL
|
||||||
|
# Phase 13.5 · Build Control Plane Hardening - Recovery-Gold Rebuild Channel.
|
||||||
|
# Erzeugt aus dem bestaetigten Host-SoT (/opt/historical-v2) ein NEUES, direkt
|
||||||
|
# dediziert getaggtes Recovery-Gold-Image. KEIN latest, KEIN Recreate, KEIN
|
||||||
|
# Runner, KEINE User-Args/Build-Args. Fester Context + Dockerfile + Tag.
|
||||||
|
RECOVERY_REBUILD_TAG="historical-v2-historical-service:recovery-gold-rebuilt-20260829"
|
||||||
|
RECOVERY_BUILD_CONTEXT="/opt/historical-v2"
|
||||||
|
RECOVERY_BUILD_DOCKERFILE="/opt/historical-v2/Dockerfile"
|
||||||
|
# Critical Files (Punkt 11) mit CONSERVED GOLD SHA (fest, aus laufendem Container)
|
||||||
|
declare -A CRITICAL_SHA=(
|
||||||
|
["run_backfill_year.py"]="70d3abb1a92a871e28cc6fb95b9a26944b55dd8df1b72db80642d6881b7b0f20"
|
||||||
|
["backfill.py"]="1627682ae582bb13343ae8ea10491dd9dffaef6fa4f7594d85378c2a500e81c7"
|
||||||
|
["repository.py"]="8fff892f52a612f464b5be21c66ef7787a47f2d7d049f815ef87837553c2a228"
|
||||||
|
["pipeline_v2.py"]="7c08ec3d1e75854cf341b9cbfd12afc075c4a173518dd3d951be4f95c5175fe6"
|
||||||
|
["trust_init.py"]="059db62741f2001f548d1879ccd3bf73bb2f4c8d0f492d151a469c9e4dcd3934" # trust/__init__.py
|
||||||
|
["provenance.py"]="4dd2ab977b69e9a47bb744dcc742a94d506e13035157a3977cdc7c8924d5f16f" # trust/provenance.py
|
||||||
|
["trust.py"]="7c2a948d2f1d668201e5af2a28164bcb2451b31d514a9f5f22b435c9ff2a0d01" # trust/trust.py
|
||||||
|
["sanity.py"]="875b07869e5148983e7a06bc05f6a6bec30727d1b5e95242d459564c18f6d6e9" # trust/sanity.py
|
||||||
|
["trust_gate.py"]="df0a97a1fc27985fe49b7458d94f7922bc5eaf2250807043c48dbc173f215ff1" # trust/trust_gate.py
|
||||||
|
)
|
||||||
|
# Relative Pfade je Key (fester Pfad, kein User-Parameter)
|
||||||
|
declare -A CRITICAL_PATH=(
|
||||||
|
["run_backfill_year.py"]="app/run_backfill_year.py"
|
||||||
|
["backfill.py"]="app/backfill.py"
|
||||||
|
["repository.py"]="app/persistence/repository.py"
|
||||||
|
["pipeline_v2.py"]="app/pipeline_v2.py"
|
||||||
|
["trust_init.py"]="app/trust/__init__.py"
|
||||||
|
["provenance.py"]="app/trust/provenance.py"
|
||||||
|
["trust.py"]="app/trust/trust.py"
|
||||||
|
["sanity.py"]="app/trust/sanity.py"
|
||||||
|
["trust_gate.py"]="app/trust/trust_gate.py"
|
||||||
|
)
|
||||||
|
|
||||||
# RUN_MODE (Mutation Guard): READ_ONLY | DRY_RUN | MUTATING
|
# RUN_MODE (Mutation Guard): READ_ONLY | DRY_RUN | MUTATING
|
||||||
RUN_MODE=MUTATING
|
RUN_MODE=MUTATING
|
||||||
|
|
||||||
|
|
@ -327,6 +360,222 @@ verify_recovery_snapshot() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ================= RECOVERY-GOLD-REBUILD (build-recovery-gold) ===============
|
||||||
|
# Erstellt Build-Context-Manifest (read-only) fuer RECOVERY_GOLD_BUILD_INPUT.
|
||||||
|
# Erfasst Dockerfile, requirements.txt, .dockerignore + alle app/* Dateien.
|
||||||
|
build_context_manifest() {
|
||||||
|
local ctx="$RECOVERY_BUILD_CONTEXT" f sha rel
|
||||||
|
# Dockerfile
|
||||||
|
[ -f "$ctx/Dockerfile" ] || die "build-context: Dockerfile fehlt (FAIL CLOSED)"
|
||||||
|
echo "Dockerfile $(sha256sum "$ctx/Dockerfile" | awk '{print $1}')"
|
||||||
|
# requirements.txt (falls vorhanden)
|
||||||
|
[ -f "$ctx/requirements.txt" ] && echo "requirements.txt $(sha256sum "$ctx/requirements.txt" | awk '{print $1}')"
|
||||||
|
# .dockerignore (falls vorhanden)
|
||||||
|
[ -f "$ctx/.dockerignore" ] && echo ".dockerignore $(sha256sum "$ctx/.dockerignore" | awk '{print $1}')"
|
||||||
|
# app/* (reguläre Dateien, .py + sonstiges, keine __pycache__)
|
||||||
|
if [ -d "$ctx/app" ]; then
|
||||||
|
find "$ctx/app" -type f -not -name '*.pyc' -not -path '*/__pycache__/*' -printf '%P\n' 2>/dev/null | sort | while IFS= read -r f; do
|
||||||
|
[ -f "$ctx/app/$f" ] || continue
|
||||||
|
sha="$(sha256sum "$ctx/app/$f" | awk '{print $1}')"
|
||||||
|
printf 'app/%s %s\n' "$f" "$sha"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dockerfile "exakt erwartet": Basis + COPY-Semantik (Punkt 4 Guard)
|
||||||
|
dockerfile_expected() {
|
||||||
|
local f="$RECOVERY_BUILD_DOCKERFILE"
|
||||||
|
grep -qE '^FROM python:3\.11-slim' "$f" || die "Dockerfile: erwartetes Base-Image python:3.11-slim fehlt (FAIL CLOSED)"
|
||||||
|
grep -qE '^COPY app/ \./app/' "$f" || die "Dockerfile: erwartetes COPY app/ ./app/ fehlt (FAIL CLOSED)"
|
||||||
|
grep -qE '^EXPOSE 8000' "$f" || die "Dockerfile: erwartetes EXPOSE 8000 fehlt (FAIL CLOSED)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Alle Guards fuer einen Recovery-Rebuild (read-only, nutzbar im dry-run und build)
|
||||||
|
# Host-Guard: alle 63 Gold-Dateien MATCH (0 DIVERGED/0 MISSING) UND jede EXTRA
|
||||||
|
# ist eine der 3 erlaubten HOST_ONLY_LEGACY (via .dockerignore ausgeschlossen).
|
||||||
|
# Anders als classify_and_report (fuer Image/Container) duerfen diese 3 auf dem
|
||||||
|
# Host physisch existieren — der Build schliesst sie per .dockerignore aus.
|
||||||
|
verify_host_gold_context() {
|
||||||
|
local hm tmpf ok d=0 m=0 x=0 mat=0 l=0 goldrel goldsha nrel nsha
|
||||||
|
hm="$(mktemp)"; gen_host_manifest > "$hm"
|
||||||
|
while read -r goldsha goldrel; do
|
||||||
|
goldrel="${goldrel#./}"; [ -n "$goldrel" ] || continue
|
||||||
|
l=$((l+1))
|
||||||
|
nsha="$(awk -v r="$goldrel" '$2==r{print $1}' "$hm")"
|
||||||
|
if [ -z "$nsha" ]; then echo " MISSING $goldrel"; m=$((m+1));
|
||||||
|
elif [ "$nsha" = "$goldsha" ]; then mat=$((mat+1));
|
||||||
|
else echo " DIVERGED $goldrel"; d=$((d+1)); fi
|
||||||
|
done < "$GOLD_MANIFEST"
|
||||||
|
while read -r nsha nrel; do
|
||||||
|
nrel="${nrel#./}"
|
||||||
|
found="$(awk -v r="$nrel" '$2==r{print $1}' "$GOLD_MANIFEST")"
|
||||||
|
if [ -z "$found" ]; then
|
||||||
|
case "$nrel" in
|
||||||
|
app/app/backfill.py|app/app/test_idempotency.py|app/eligibility.py)
|
||||||
|
echo " EXTRA(erlaubte Legacy) $nrel" ;;
|
||||||
|
*) echo " EXTRA(unerlaubt) $nrel"; x=$((x+1)) ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done < "$hm"
|
||||||
|
rm -f "$hm"
|
||||||
|
echo " Gold $l: MATCH=$mat DIVERGED=$d MISSING=$m ; unerlaubte EXTRA=$x"
|
||||||
|
[ $d -eq 0 ] && [ $m -eq 0 ] && [ $x -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuild_guards() {
|
||||||
|
echo " [guard A] verify-sot-recovery 13/13:"
|
||||||
|
verify_recovery_ok >/dev/null && echo " OK (13/13 MATCH)"
|
||||||
|
echo " [guard B] Host-Tree vs Gold-Manifest (63 MATCH / 0 DIVERGED / 0 MISSING; nur erlaubte Legacy-EXTRA):"
|
||||||
|
verify_host_gold_context || die "Guard B FAIL: Host-Gold-Kontext nicht reproduzierbar (FAIL CLOSED)"
|
||||||
|
echo " Guard B OK"
|
||||||
|
echo " [guard C] Build-Pollution ausgeschlossen (.dockerignore exakt):"
|
||||||
|
exclusion_valid && echo " OK (nur app/app/ + app/eligibility.py)"
|
||||||
|
echo " [guard D] Dockerfile exakt erwartet:"; dockerfile_expected && echo " OK"
|
||||||
|
echo " [guard E] Gold-Container Running + /health + /health/ready:"
|
||||||
|
ensure_gold_run
|
||||||
|
local rh rr
|
||||||
|
rh="$(docker exec "$HISTORICAL_SERVICE" python -c 'import urllib.request;print(urllib.request.urlopen("http://localhost:8000/health",timeout=5).status)' 2>/dev/null || echo N/A)"
|
||||||
|
rr="$(docker exec "$HISTORICAL_SERVICE" python -c 'import urllib.request;print(urllib.request.urlopen("http://localhost:8000/health/ready",timeout=5).status)' 2>/dev/null || echo N/A)"
|
||||||
|
[ "$rh" = "200" ] && [ "$rr" = "200" ] || die "Guard E FAIL: Gold-Container nicht gesund (FAIL CLOSED)"
|
||||||
|
echo " OK (/health=$rh /ready=$rr)"
|
||||||
|
echo " [guard F] historical-db gesund:"; ensure_db_run && echo " OK"
|
||||||
|
echo " [guard G] Recovery-Rebuild-Tag existiert NOCH NICHT:"
|
||||||
|
if docker image inspect "$RECOVERY_REBUILD_TAG" >/dev/null 2>&1; then
|
||||||
|
die "Guard G FAIL: Tag $RECOVERY_REBUILD_TAG existiert bereits (KEIN Overwrite, FAIL CLOSED)"
|
||||||
|
fi
|
||||||
|
echo " OK (Tag frei)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Host-Manifest (Host-SoT, read-only): app/* .py vs Gold-Manifest — wie gen_manifest aber vom Host
|
||||||
|
gen_host_manifest() {
|
||||||
|
( cd "/opt/historical-v2" && find app -type f -name '*.py' ! -path '*/__pycache__/*' 2>/dev/null | sort | while IFS= read -r f; do
|
||||||
|
sha256sum "$f" 2>/dev/null; done )
|
||||||
|
}
|
||||||
|
|
||||||
|
# read-only: Critical-Files-SHAs Recovery-Image + laufender Gold-Container + Gold-Baseline (Punkt 11)
|
||||||
|
verify_critical_files() {
|
||||||
|
# $1 = Basisverzeichnis mit app/ (kopierter Recovery-Tree) ODER leer -> nur Container+Baseline
|
||||||
|
local tmpc tmpdir k path imgsha csha
|
||||||
|
local base_dir="${1:-}"
|
||||||
|
local img_shas=""
|
||||||
|
if [ -n "$base_dir" ]; then
|
||||||
|
# Recovery-Image-SHAs aus kopiertem Tree
|
||||||
|
for k in "${!CRITICAL_SHA[@]}"; do
|
||||||
|
path="app/${CRITICAL_PATH[$k]#app/}"
|
||||||
|
[ -f "$base_dir/$path" ] && img_shas="$img_shas $k:$(sha256sum "$base_dir/$path" | awk '{print $1}')" || img_shas="$img_shas $k:MISSING"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
local allok=1
|
||||||
|
for k in "${!CRITICAL_SHA[@]}"; do
|
||||||
|
path="app/${CRITICAL_PATH[$k]#app/}"
|
||||||
|
csha="$(docker exec "$HISTORICAL_SERVICE" sha256sum "/$path" 2>/dev/null | awk '{print $1}')" || csha="MISSING"
|
||||||
|
gold="${CRITICAL_SHA[$k]}"
|
||||||
|
img="MISSING"
|
||||||
|
if [ -n "$base_dir" ]; then
|
||||||
|
is=$(echo "$img_shas" | grep -oE "${k}:[a-f0-9]+|${k}:MISSING" | cut -d: -f2)
|
||||||
|
img="$is"
|
||||||
|
fi
|
||||||
|
if [ "$csha" = "$gold" ] && { [ -z "$base_dir" ] || [ "$img" = "$gold" ]; }; then
|
||||||
|
printf ' MATCH %-22s container=%s gold=%s\n' "$k" "${csha:0:12}" "${gold:0:12}"
|
||||||
|
else
|
||||||
|
printf ' FAIL %-22s image=%s container=%s gold=%s\n' "$k" "${img:0:12}" "${csha:0:12}" "${gold:0:12}"
|
||||||
|
allok=0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$allok" = "1" ] || die "verify: Critical-File-SHA-Mismatch (FAIL CLOSED)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# read-only: Recovery-Rebuild-Image Config gegen erwartete Runtime-Konfiguration (Punkt 13)
|
||||||
|
verify_image_config() {
|
||||||
|
local c
|
||||||
|
# Erwartet: Entrypoint [] Cmd [uvicorn app.api.main:app --host 0.0.0.0 --port 8000] WorkDir=/app User=(leer) Exposed=8000
|
||||||
|
local exp_entry="[]" exp_workdir="/app" exp_user="" exp_expose="map[8000/tcp:{}]"
|
||||||
|
echo " [config] Entrypoint, Cmd, WorkingDir, User, ExposedPorts:"
|
||||||
|
local c_entry c_cmd c_wd c_user c_expose
|
||||||
|
c_entry="$(docker image inspect -f '{{json .Config.Entrypoint}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
c_cmd="$(docker image inspect -f '{{json .Config.Cmd}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
c_wd="$(docker image inspect -f '{{.Config.WorkingDir}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
c_user="$(docker image inspect -f '{{.Config.User}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
c_expose="$(docker image inspect -f '{{json .Config.ExposedPorts}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
printf ' Entrypoint = %s (erwartet %s)\n' "${c_entry:-<nil>}" "$exp_entry"
|
||||||
|
printf ' Cmd = %s\n' "${c_cmd:-<nil>}"
|
||||||
|
printf ' WorkingDir = %s (erwartet %s)\n' "${c_wd:-<nil>}" "$exp_workdir"
|
||||||
|
printf ' User = %s (erwartet leer)\n' "${c_user:-<nil>}"
|
||||||
|
printf ' Exposed = %s (erwartet 8000)\n' "${c_expose:-<nil>}"
|
||||||
|
# Healthcheck: Dockerfile setzt keinen -> <nil> wie beim Gold-Container
|
||||||
|
local c_hc
|
||||||
|
c_hc="$(docker image inspect -f '{{json .Config.Healthcheck}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
printf ' Healthcheck= %s (Gold: <nil>) (ENV redigiert, nicht ausgeben)\n' "${c_hc:-<nil>}"
|
||||||
|
# Sanity: WorkingDir + Expose + Entrypoint muessen den Erwartungen entsprechen (Cmd kann abweichen? erwartet exakt)
|
||||||
|
[ "$c_wd" = "$exp_workdir" ] || die "verify: WorkingDir unerwartet '$c_wd' (FAIL CLOSED)"
|
||||||
|
echo " [config] OK (ENV/secrets NICHT ausgegeben; nur redigierte Struktur)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- build-recovery-gold (NUR Build aus Host-SoT; DRY_RUN = kein Build) ----
|
||||||
|
build_recovery_gold() {
|
||||||
|
expect_zero "$@"
|
||||||
|
echo "=== BUILD-RECOVERY-GOLD ($RUN_MODE) ==="
|
||||||
|
echo " Build Context : $RECOVERY_BUILD_CONTEXT (fix)"
|
||||||
|
echo " Dockerfile : $RECOVERY_BUILD_DOCKERFILE (fix)"
|
||||||
|
echo " Recovery-Tag : $RECOVERY_REBUILD_TAG (fix, direkt getaggt, KEIN latest)"
|
||||||
|
echo " Guards:"
|
||||||
|
rebuild_guards
|
||||||
|
# Build-Context-Manifest (Punkt 5) — read-only RECOVERY_GOLD_BUILD_INPUT
|
||||||
|
echo " [build-input] RECOVERY_GOLD_BUILD_INPUT (Build-Context-Manifest):"
|
||||||
|
build_context_manifest | while IFS= read -r line; do echo " $line"; done
|
||||||
|
if [ "$RUN_MODE" = "DRY_RUN" ]; then
|
||||||
|
echo "DRY_RUN_PASS: build-recovery-gold wuerde docker build -f $RECOVERY_BUILD_DOCKERFILE -t $RECOVERY_REBUILD_TAG $RECOVERY_BUILD_CONTEXT (keine Aktion)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
mode_or_die
|
||||||
|
echo " [build] docker build -f $RECOVERY_BUILD_DOCKERFILE -t $RECOVERY_REBUILD_TAG $RECOVERY_BUILD_CONTEXT"
|
||||||
|
docker build -f "$RECOVERY_BUILD_DOCKERFILE" -t "$RECOVERY_REBUILD_TAG" "$RECOVERY_BUILD_CONTEXT" || die "build-recovery-gold fehlgeschlagen (FAIL CLOSED)"
|
||||||
|
local newid
|
||||||
|
newid="$(docker image inspect -f '{{.Id}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
echo "RECOVERY_GOLD_IMAGE_ID=$newid"
|
||||||
|
echo "KLASSIFIZIERUNG: REBUILT RECOVERY GOLD IMAGE (NICHT das Original-Gold-Image; Original f1a6f33e... = UNAVAILABLE)"
|
||||||
|
echo "BUILD-RECOVERY-GOLD OK. KEIN Recreate, KEIN Runner. latest unangetastet."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- verify-recovery-gold-image (read-only) ----
|
||||||
|
verify_recovery_gold_image() {
|
||||||
|
req_ok; expect_zero "$@"
|
||||||
|
echo "=== VERIFY-RECOVERY-GOLD-IMAGE (Rebuilt-Image vs Gold-Container vs Gold-Manifest) ==="
|
||||||
|
if ! docker image inspect "$RECOVERY_REBUILD_TAG" >/dev/null 2>&1; then
|
||||||
|
die "Recovery-Rebuild-Image fehlt (zuerst build-recovery-gold) (FAIL CLOSED)"
|
||||||
|
fi
|
||||||
|
local newid
|
||||||
|
newid="$(docker image inspect -f '{{.Id}}' "$RECOVERY_REBUILD_TAG" 2>/dev/null)"
|
||||||
|
echo " RECOVERY_GOLD_IMAGE_ID=$newid (Tag: $RECOVERY_REBUILD_TAG)"
|
||||||
|
echo " [Punkt 9] Tag -> Image-ID + direkt per ID inspectable:"
|
||||||
|
docker image inspect -f " ID=$newid" "sha256:${newid#sha256:}" >/dev/null 2>&1 && echo " Image per ID inspectable: OK" || die "verify: Image nicht per ID inspectable (FAIL CLOSED)"
|
||||||
|
echo " [Code] Recovery-Image vs Gold-Manifest + Gold-Container:"
|
||||||
|
local tmpc tmpdir tmpf gret
|
||||||
|
tmpc="rq-rebuild-verify-$(date +%s)"
|
||||||
|
tmpdir="/tmp/rq-rebuild-copy-$$"
|
||||||
|
rm -rf "$tmpdir"; mkdir -p "$tmpdir"
|
||||||
|
docker create --name "$tmpc" --entrypoint /bin/true "$RECOVERY_REBUILD_TAG" >/dev/null 2>&1 || die "verify: Temp-Container Fehler (FAIL CLOSED)"
|
||||||
|
docker cp "$tmpc:/app/app" "$tmpdir/appapp" >/dev/null 2>&1 || { docker rm -f "$tmpc" >/dev/null 2>&1; rm -rf "$tmpdir"; die "verify: /app/app nicht lesbar (FAIL CLOSED)"; }
|
||||||
|
docker rm -f "$tmpc" >/dev/null 2>&1 || true
|
||||||
|
# Manifest aus kopiertem Recovery-Image-Tree gegen Gold-Manifest
|
||||||
|
tmpf="$(mktemp)"
|
||||||
|
( cd "$tmpdir/appapp" && find . -type f -name '*.py' ! -path '*/__pycache__/*' -printf '%P\n' 2>/dev/null | sort \
|
||||||
|
| while IFS= read -r rel; do [ -f "$rel" ] && sha256sum "$rel"; done | sed 's# ./# #' ) > "$tmpf"
|
||||||
|
classify_and_report "$tmpf"
|
||||||
|
gret="$G_RET"; rm -f "$tmpf"
|
||||||
|
if [ "$gret" != "ok" ]; then
|
||||||
|
rm -rf "$tmpdir"; die "verify: Recovery-Rebuild-Image != Gold-Manifest (DIVERGED/MISSING/EXTRA) (FAIL CLOSED)"
|
||||||
|
fi
|
||||||
|
echo " IMAGE vs GOLD: 63 MATCH / 0 DIVERGED / 0 MISSING / 0 unerlaubte EXTRA"
|
||||||
|
echo " [Critical-Files] (Punkt 11) — Image-SHA vs gold:"
|
||||||
|
verify_critical_files "$tmpdir/appapp"
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
echo " [Config] (Punkt 13):"
|
||||||
|
verify_image_config
|
||||||
|
echo "VERDICT: REBUILT RECOVERY GOLD OK (63 MATCH / 0 DIVERGED / 0 MISSING / 0 unerlaubte EXTRA, Critical-SHAs + Config OK)"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
# ---- build-historical (NUR Build, kein recreate; DRY_RUN = kein Bau) ----
|
# ---- build-historical (NUR Build, kein recreate; DRY_RUN = kein Bau) ----
|
||||||
build_historical() {
|
build_historical() {
|
||||||
expect_zero "$@"
|
expect_zero "$@"
|
||||||
|
|
@ -437,8 +686,8 @@ if [ "$SUBCMD" = "dry-run" ]; then
|
||||||
SUBCMD="${1:-}"
|
SUBCMD="${1:-}"
|
||||||
shift || true
|
shift || true
|
||||||
case "$SUBCMD" in
|
case "$SUBCMD" in
|
||||||
build-historical|recreate-historical|snapshot-recovery) ;;
|
build-historical|recreate-historical|snapshot-recovery|build-recovery-gold) ;;
|
||||||
*) die "dry-run nur mit build-historical|recreate-historical|snapshot-recovery zulaessig" ;;
|
*) die "dry-run nur mit build-historical|recreate-historical|snapshot-recovery|build-recovery-gold zulaessig" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -804,6 +1053,8 @@ case "$SUBCMD" in
|
||||||
;;
|
;;
|
||||||
snapshot-recovery) snapshot_recovery "$@" ;;
|
snapshot-recovery) snapshot_recovery "$@" ;;
|
||||||
verify-recovery-snapshot) verify_recovery_snapshot "$@" ;;
|
verify-recovery-snapshot) verify_recovery_snapshot "$@" ;;
|
||||||
|
build-recovery-gold) build_recovery_gold "$@" ;;
|
||||||
|
verify-recovery-gold-image) verify_recovery_gold_image "$@" ;;
|
||||||
build-historical) build_historical "$@" ;;
|
build-historical) build_historical "$@" ;;
|
||||||
create-build-exclusion) expect_zero "$@"
|
create-build-exclusion) expect_zero "$@"
|
||||||
# Build-Pollution-Exclusion implementieren (Punkt 5-8): .dockerignore anlegen/ersetzen
|
# Build-Pollution-Exclusion implementieren (Punkt 5-8): .dockerignore anlegen/ersetzen
|
||||||
|
|
@ -831,6 +1082,6 @@ case "$SUBCMD" in
|
||||||
recreate-historical) recreate_historical "$@" ;;
|
recreate-historical) recreate_historical "$@" ;;
|
||||||
verify-gold-reproduction) verify_gold_reproduction "$@" ;;
|
verify-gold-reproduction) verify_gold_reproduction "$@" ;;
|
||||||
|
|
||||||
*) die "Aufruf: ps|inspect|logs|run|code <K>|hashes|code-list|stage-dir|stage-check <K>|test <K>|search-writes|healthcheck|backup <K>|deploy <K>|rollback <K>|build-def|build-files|host-manifest|host-code <p>|container-manifest|container-code <p>|tree-diff|backup-sot|sot-session|recover-sot <K>|recover-sot-list|verify-sot-recovery|restore-sot|snapshot-recovery|dry-run snapshot-recovery|verify-recovery-snapshot|build-historical|dry-run build-historical|recreate-historical|dry-run recreate-historical|verify-gold-reproduction|create-build-exclusion|show-build-exclusion"
|
*) die "Aufruf: ps|inspect|logs|run|code <K>|hashes|code-list|stage-dir|stage-check <K>|test <K>|search-writes|healthcheck|backup <K>|deploy <K>|rollback <K>|build-def|build-files|host-manifest|host-code <p>|container-manifest|container-code <p>|tree-diff|backup-sot|sot-session|recover-sot <K>|recover-sot-list|verify-sot-recovery|restore-sot|snapshot-recovery|dry-run snapshot-recovery|verify-recovery-snapshot|build-recovery-gold|dry-run build-recovery-gold|verify-recovery-gold-image|build-historical|dry-run build-historical|recreate-historical|dry-run recreate-historical|verify-gold-reproduction|create-build-exclusion|show-build-exclusion"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue