Root causes found by prebuilding every challenge image in parallel:
- fjb: ghcr.io base is not anonymously pullable here -> official httpd:2.4.
pnpm 12 (via corepack on node:20) fails the install with
ERR_PNPM_IGNORED_BUILDS unless build scripts are approved; neither
onlyBuiltDependencies in pnpm-workspace.yaml nor --no-ignore-scripts
suppresses it. The working sequence is:
pnpm install --ignore-scripts && pnpm approve-builds --all && pnpm rebuild
- xl + kode-viewer: node:20-slim-bookworm is not a real tag -> node:20-bookworm-slim.
- burvesigner: python-dev no longer exists in bookworm -> dropped (python3-dev
was already there and the source has no py2 syntax).
- burvesigner/hirnfick/s3: apt update and install were separate RUN layers;
with the bundled apt-insecure.conf the second invocation re-resolved against
the EOL bullseye-security mirror and 404'd every package. Merged into one
'update && install' layer (fix_apt_layers.py, idempotent).
- consolidate_images.sh: teams used to build a private image per team
(team1-x ... team4-x) because no shared image existed. Since the password is
applied at runtime via chpasswd, one shared services-<name> build is enough;
this reclaims ~1.5 GB, which matters on a 79 GB disk.
- reconcile_team_state(): a challenge enabled while a team was down left
state.json without ports/flag/password, so the next compose render died with
KeyError. Now both the API and the CLI tools reconcile first.
55 lines
1.9 KiB
Bash
Executable File
55 lines
1.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Remove per-team duplicate challenge images and repoint every team compose at
|
|
# the shared `services-<name>` image.
|
|
#
|
|
# Why: before services-<name> images existed, enabling a challenge made each of
|
|
# the 4 teams build its own copy (team1-x, team2-x, ...). A challenge image is
|
|
# identical for all teams — the SSH password is applied at container start via
|
|
# chpasswd, never baked in — so one shared build is enough. Those duplicates
|
|
# were consuming ~1.5 GB on a disk that only has ~3 GB free.
|
|
set -euo pipefail
|
|
|
|
ROOT=/opt/gemastik18-final
|
|
cd "$ROOT/panel"
|
|
python3 - <<'PY'
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
sys.path.insert(0, '.')
|
|
import teams as orch, compose_gen
|
|
|
|
for note in orch.reconcile_team_state():
|
|
print(note)
|
|
|
|
for d in sorted(orch.TEAMS_DIR.glob("team*")):
|
|
sf = d / "state.json"
|
|
if not sf.exists():
|
|
continue
|
|
st = json.loads(sf.read_text())
|
|
(d / "services" / "docker-compose.yml").write_text(
|
|
compose_gen.render_team_compose(st["index"], st))
|
|
print(f"team{st['index']}: compose re-rendered against shared images")
|
|
PY
|
|
|
|
echo "--- removing per-team images ---"
|
|
for img in $(docker images --format '{{.Repository}}' | grep -E '^team[0-9]+-' || true); do
|
|
# only safe to remove if no container is using it
|
|
if docker ps -a --format '{{.Image}}' | grep -q "^${img}$"; then
|
|
# containers reference the image by name; remove containers first
|
|
docker ps -a --filter "ancestor=${img}" --format '{{.Names}}' | while read -r c; do
|
|
[ -n "$c" ] && docker rm -f "$c" >/dev/null
|
|
done
|
|
fi
|
|
docker rmi "$img" >/dev/null 2>&1 && echo "removed $img" || true
|
|
done
|
|
|
|
echo "--- recreating containers from shared images ---"
|
|
for i in 1 2 3 4; do
|
|
cd "$ROOT/teams/team$i/services"
|
|
docker compose -p "team$i" up -d --remove-orphans >/dev/null 2>&1 &
|
|
done
|
|
wait
|
|
echo "done"
|
|
docker ps --format '{{.Names}}' | grep -c '_container_team' || true
|
|
df -h / | tail -1
|