"""Controleer of elk image in de catalogus echt bestaat bij zijn registry. Aanleiding: `ghcr.io/plexguide/huntarr` stond in de ARR-stack terwijl dat project offline was gehaald en het image niet meer bestond. Daarna bleken ook Forgejo, Planka en Baby Buddy naar tags te wijzen die er niet zijn. Zulke fouten merk je anders pas als iemand op installeren drukt. python3 tools/controleer_images.py [--json] **Waarom dit langzaam gaat.** Registries knijpen af bij te veel verzoeken achter elkaar, en een afgeknepen verzoek ziet er precies zo uit als een image dat niet bestaat. Eén ronde met zestien tegelijk leverde negen valse meldingen op — waarop je bijna een werkend sjabloon gaat "repareren". Daarom weinig tegelijk, en wat niet meteen lukt gaat opnieuw met oplopende pauzes. """ from __future__ import annotations import concurrent.futures as cf import json import re import sys import time from pathlib import Path WORTEL = Path(__file__).resolve().parent.parent sys.path.insert(0, str(WORTEL / "server-up" / "core")) import registry # noqa: E402 TEGELIJK = 4 POGINGEN = 3 IMAGE_RE = re.compile(r"^\s*image:\s*(\S+)\s*$", re.M) def images() -> dict[str, list[str]]: """Elk image in de catalogus, met de apps die het gebruiken.""" uit: dict[str, list[str]] = {} for pad in sorted((WORTEL / "apps").glob("*/files/*.yaml")): for m in IMAGE_RE.finditer(pad.read_text(encoding="utf-8")): naam = m.group(1) if "<<" in naam: # nog niet gerenderd continue uit.setdefault(naam, []).append(pad.parent.parent.name) return uit def bestaat(image: str) -> bool: for poging in range(POGINGEN): try: if registry.manifest_digest(image): return True except Exception: pass if poging < POGINGEN - 1: time.sleep(2 ** poging) # 1s, 2s return False def main() -> int: alles = images() with cf.ThreadPoolExecutor(TEGELIJK) as ex: uitslag = dict(zip(alles, ex.map(bestaat, alles))) fout = {i: alles[i] for i, ok in uitslag.items() if not ok} if "--json" in sys.argv: print(json.dumps({"gecontroleerd": len(alles), "fout": fout}, indent=2)) else: print(f"{len(alles)} unieke images gecontroleerd") for image, apps in sorted(fout.items()): print(f" ✖ {image} ({', '.join(sorted(set(apps)))})") if not fout: print(" alle images bestaan") return 1 if fout else 0 if __name__ == "__main__": raise SystemExit(main())