diff --git a/deploy/release.sh b/deploy/release.sh index 77d4751..e2c6989 100644 --- a/deploy/release.sh +++ b/deploy/release.sh @@ -85,33 +85,48 @@ refresh_trending_scout_pin() { echo "Pinned trending scout image: $TRENDING_SCOUT_IMAGE_ID" } -verify_trending_scout_pin() { - if ! docker image inspect "$TRENDING_SCOUT_IMAGE" >/dev/null 2>&1; then - echo "ERROR: Trending scout image was removed during cleanup: $TRENDING_SCOUT_IMAGE" - exit 1 - fi - - CURRENT_SCOUT_IMAGE_ID=$(docker image inspect --format '{{.Id}}' "$TRENDING_SCOUT_IMAGE") +assert_trending_scout_pin() { if ! docker inspect "$TRENDING_SCOUT_PIN_NAME" >/dev/null 2>&1; then echo "ERROR: Trending scout pin container is missing: $TRENDING_SCOUT_PIN_NAME" exit 1 fi PINNED_SCOUT_IMAGE_ID=$(docker inspect --format '{{.Image}}' "$TRENDING_SCOUT_PIN_NAME") PINNED_SCOUT_RUNNING=$(docker inspect --format '{{.State.Running}}' "$TRENDING_SCOUT_PIN_NAME") - if [ "$CURRENT_SCOUT_IMAGE_ID" != "$TRENDING_SCOUT_IMAGE_ID" ] || \ - [ "$PINNED_SCOUT_IMAGE_ID" != "$TRENDING_SCOUT_IMAGE_ID" ] || \ + if [ "$PINNED_SCOUT_IMAGE_ID" != "$TRENDING_SCOUT_IMAGE_ID" ] || \ [ "$PINNED_SCOUT_RUNNING" != "true" ]; then echo "ERROR: Trending scout pin no longer protects the scheduled image" exit 1 fi } +restore_trending_scout_tag() { + # Docker prune can remove a mutable tag even while the pin retains its image + # ID. Recreate the cron's tag from that protected immutable reference. + assert_trending_scout_pin + docker tag "$TRENDING_SCOUT_IMAGE_ID" "$TRENDING_SCOUT_IMAGE" +} + +verify_trending_scout_pin() { + assert_trending_scout_pin + if ! docker image inspect "$TRENDING_SCOUT_IMAGE" >/dev/null 2>&1; then + echo "ERROR: Trending scout image was not restored after cleanup: $TRENDING_SCOUT_IMAGE" + exit 1 + fi + + CURRENT_SCOUT_IMAGE_ID=$(docker image inspect --format '{{.Id}}' "$TRENDING_SCOUT_IMAGE") + if [ "$CURRENT_SCOUT_IMAGE_ID" != "$TRENDING_SCOUT_IMAGE_ID" ]; then + echo "ERROR: Trending scout tag does not resolve to the protected image" + exit 1 + fi +} + # ========== DISK SPACE CHECK & CLEANUP ========== echo "Pre-cleanup disk usage:" df -h / refresh_trending_scout_pin docker system prune -af || true docker builder prune -af || true +restore_trending_scout_tag verify_trending_scout_pin echo "Post-cleanup disk usage:" @@ -555,6 +570,7 @@ fi # Stopped rollback containers still protect their known-good images here. echo "Cleaning up unused images..." docker image prune -af +restore_trending_scout_tag verify_trending_scout_pin df -h / diff --git a/tests/test_deploy_workflow.py b/tests/test_deploy_workflow.py index b5ea31b..99bd30c 100644 --- a/tests/test_deploy_workflow.py +++ b/tests/test_deploy_workflow.py @@ -44,18 +44,26 @@ def test_release_refreshes_and_verifies_the_trending_scout_pin() -> None: assert "--memory 8m --cpus 0.01" in release_script assert "--label panicradar.role=trending-scout-image-pin" not in release_script assert 'docker inspect --format \'{{.Image}}\' "$TRENDING_SCOUT_PIN_NAME"' in release_script + assert 'docker tag "$TRENDING_SCOUT_IMAGE_ID" "$TRENDING_SCOUT_IMAGE"' in release_script assert "Trending scout pin container is missing" in release_script refresh_call = release_script.index("\nrefresh_trending_scout_pin\n") system_prune = release_script.index("docker system prune -af") post_deploy_prune = release_script.index("docker image prune -af") + restore_after_system_prune = release_script.index( + "\nrestore_trending_scout_tag\n", system_prune + ) verify_after_system_prune = release_script.index( "\nverify_trending_scout_pin\n", system_prune ) + restore_after_post_deploy_prune = release_script.index( + "\nrestore_trending_scout_tag\n", post_deploy_prune + ) verify_after_post_deploy_prune = release_script.index( "\nverify_trending_scout_pin\n", post_deploy_prune ) assert refresh_call < system_prune + assert system_prune < restore_after_system_prune < verify_after_system_prune assert verify_after_system_prune < post_deploy_prune - assert verify_after_post_deploy_prune > post_deploy_prune + assert post_deploy_prune < restore_after_post_deploy_prune < verify_after_post_deploy_prune