#!/usr/bin/env bash # FULL reset of the runtime — keeps ONLY the shared challenge images. # # Removes: every team container, every team docker network, every anonymous/ # named volume, every per-team receiver systemd unit, and all team directories # (state, flags, credentials, compose). KEEPS: services-* images (the # challenges themselves), the panel, the global receiver, the challenge sources # in services/, and the registry. # # This is the "purge everything except the challenges" path the organiser asked # for after passwords drifted: fresh containers get fresh /etc/shadow state, so # no stale credential can survive. set -uo pipefail BASE=/opt/gemastik18-final TEAMS=$BASE/teams echo "=== [1/6] stop per-team receivers + remove units ===" for u in $(systemctl list-unit-files 'gemastik-receiver-team*.service' 2>/dev/null \ | awk '/gemastik-receiver-team/{print $1}'); do systemctl disable --now "$u" >/dev/null 2>&1 rm -f "/etc/systemd/system/$u" echo " removed $u" done systemctl daemon-reload echo "=== [2/6] compose down for every team (before deleting dirs) ===" for d in "$TEAMS"/team*/services; do [ -d "$d" ] || continue idx=$(basename "$(dirname "$d")") echo " compose down $idx" (cd "$d" && docker compose -p "$idx" -f docker-compose.yml down -v --remove-orphans 2>&1 | tail -1) done echo "=== [3/6] force-remove any surviving team containers ===" left=$(docker ps -aq --filter 'name=_container_team' | wc -l) echo " found $left" [ "$left" -gt 0 ] && docker rm -f $(docker ps -aq --filter 'name=_container_team') >/dev/null 2>&1 echo "=== [4/6] remove team networks + stray volumes ===" for n in $(docker network ls --format '{{.Name}}' | grep -E '^team[0-9]+_default$' || true); do docker network rm "$n" >/dev/null 2>&1 && echo " network $n" done # anonymous + team-scoped volumes (challenge DB state lives here) anon=$(docker volume ls -q --filter dangling=true | wc -l) echo " dangling volumes: $anon" [ "$anon" -gt 0 ] && docker volume prune -f >/dev/null 2>&1 && echo " pruned" for v in $(docker volume ls --format '{{.Name}}' | grep -E '^team[0-9]+' || true); do docker volume rm "$v" >/dev/null 2>&1 && echo " volume $v" done echo "=== [5/6] delete team dirs + ledgers ===" rm -rf "$TEAMS"/team* rm -f "$TEAMS"/leaderboard.json "$TEAMS"/points.json "$TEAMS"/attacks.json echo " team dirs now: $(ls -d "$TEAMS"/team* 2>/dev/null | wc -l)" echo "=== [6/6] drop team domains from Traefik ===" cd "$BASE/panel" && python3 -c " import sys; sys.path.insert(0,'.') import teams print(' ', teams.ensure_team_domains()) " # the platform-level receiver is separate and must keep running systemctl restart gemastik-panel 2>/dev/null echo " panel: $(systemctl is-active gemastik-panel)" echo "=== done ===" docker ps --format '{{.Names}}' | grep -c '_container_team' || echo " team containers: 0" docker images --format '{{.Repository}}' | grep -c '^services-' || true df -h / | tail -1