Compare commits
2 Commits
c0877548ec
...
eeb5879a46
| Author | SHA1 | Date | |
|---|---|---|---|
| eeb5879a46 | |||
| 4dea06b0c6 |
75
app.py
75
app.py
@ -8,6 +8,7 @@ import re
|
||||
import secrets
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import unicodedata
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
@ -46,10 +47,19 @@ MINT_ISO_PATH = str(Path(os.environ.get("TUXFLOTTE_MINT_ISO_PATH", "data/upstrea
|
||||
# zum eigenen Arbeitsverzeichnis.
|
||||
ISO_BUILD_OUTPUT_DIR = Path(os.environ.get("TUXFLOTTE_ISO_BUILD_OUTPUT_DIR", "data/iso-builds")).resolve()
|
||||
ISO_BUILD_TIMEOUT_SECONDS = 1800
|
||||
# ISO-Ablauf nach 7 Tagen (Migration 0021): ergaenzt die bestehende
|
||||
# "neuer Build ersetzt alten"-Aufraeumung um eine reine Zeitdimension.
|
||||
ISO_BUILD_EXPIRY_DAYS = 7
|
||||
ISO_BUILD_EXPIRY_SWEEP_INTERVAL_SECONDS = 60 * 60
|
||||
|
||||
app = FastAPI(title="Provisioning Activation Server", version="0.1.0")
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
def start_iso_build_expiry_sweep() -> None:
|
||||
threading.Thread(target=run_iso_build_expiry_sweep, daemon=True).start()
|
||||
|
||||
|
||||
class ActivationRequest(BaseModel):
|
||||
activation_code: str
|
||||
device_fingerprint: str
|
||||
@ -2076,7 +2086,7 @@ def recharge_enrollment_session(session_id: str, additional_devices: int, expire
|
||||
|
||||
def iso_build_row_to_dict(row) -> dict:
|
||||
(build_id, organization_id, status, output_filename, error_message,
|
||||
created_at, started_at, finished_at) = row
|
||||
created_at, started_at, finished_at, expires_at) = row
|
||||
return {
|
||||
"id": str(build_id),
|
||||
"organization_id": str(organization_id),
|
||||
@ -2086,6 +2096,7 @@ def iso_build_row_to_dict(row) -> dict:
|
||||
"created_at": created_at.isoformat(),
|
||||
"started_at": started_at.isoformat() if started_at else None,
|
||||
"finished_at": finished_at.isoformat() if finished_at else None,
|
||||
"expires_at": expires_at.isoformat() if expires_at else None,
|
||||
}
|
||||
|
||||
def fetch_iso_build(build_id: str):
|
||||
@ -2094,7 +2105,7 @@ def fetch_iso_build(build_id: str):
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, organization_id, status, output_filename, error_message,
|
||||
created_at, started_at, finished_at
|
||||
created_at, started_at, finished_at, expires_at
|
||||
FROM iso_builds
|
||||
WHERE id = %s
|
||||
""",
|
||||
@ -2109,7 +2120,7 @@ def fetch_iso_builds(organization_id: str):
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, organization_id, status, output_filename, error_message,
|
||||
created_at, started_at, finished_at
|
||||
created_at, started_at, finished_at, expires_at
|
||||
FROM iso_builds
|
||||
WHERE organization_id = %s
|
||||
ORDER BY created_at DESC
|
||||
@ -2138,6 +2149,12 @@ def update_iso_build(
|
||||
mark_started: bool = False,
|
||||
mark_finished: bool = False,
|
||||
):
|
||||
# expires_at nur bei einem tatsaechlich erfolgreichen Abschluss setzen -
|
||||
# ein "failed"-Build hat nichts zum Downloaden, das ablaufen koennte
|
||||
# (dessen Zeile raeumt weiterhin ausschliesslich cleanup_old_iso_builds()
|
||||
# beim naechsten Bau derselben Organisation auf).
|
||||
setzt_expiry = mark_finished and status == "completed"
|
||||
|
||||
with get_database_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
@ -2148,6 +2165,7 @@ def update_iso_build(
|
||||
error_message = %s
|
||||
{", started_at = CURRENT_TIMESTAMP" if mark_started else ""}
|
||||
{", finished_at = CURRENT_TIMESTAMP" if mark_finished else ""}
|
||||
{f", expires_at = CURRENT_TIMESTAMP + INTERVAL '{ISO_BUILD_EXPIRY_DAYS} days'" if setzt_expiry else ""}
|
||||
WHERE id = %s
|
||||
""",
|
||||
(status, output_filename, error_message, build_id),
|
||||
@ -2186,6 +2204,57 @@ def cleanup_old_iso_builds(organization_id: str, keep_build_id) -> None:
|
||||
(organization_id, keep_build_id),
|
||||
)
|
||||
|
||||
def cleanup_expired_iso_builds() -> int:
|
||||
"""
|
||||
Zeitbasierte Ergaenzung zu cleanup_old_iso_builds(): loescht Datei +
|
||||
Zeile jedes abgeschlossenen Builds, dessen expires_at in der
|
||||
Vergangenheit liegt - unabhaengig davon, ob die Organisation je erneut
|
||||
baut (die bisherige Logik greift nur beim naechsten Bau derselben Org).
|
||||
Laeuft periodisch ueber run_iso_build_expiry_sweep(). Gibt die Anzahl
|
||||
geloeschter Builds zurueck (fuer Logging/Tests).
|
||||
"""
|
||||
|
||||
with get_database_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, output_filename FROM iso_builds
|
||||
WHERE status = 'completed' AND expires_at IS NOT NULL AND expires_at < CURRENT_TIMESTAMP
|
||||
"""
|
||||
)
|
||||
abgelaufene_builds = cur.fetchall()
|
||||
|
||||
for build_id, output_filename in abgelaufene_builds:
|
||||
if output_filename:
|
||||
(ISO_BUILD_OUTPUT_DIR / output_filename).unlink(missing_ok=True)
|
||||
|
||||
cur.execute(
|
||||
"""
|
||||
DELETE FROM iso_builds
|
||||
WHERE status = 'completed' AND expires_at IS NOT NULL AND expires_at < CURRENT_TIMESTAMP
|
||||
"""
|
||||
)
|
||||
|
||||
return len(abgelaufene_builds)
|
||||
|
||||
def run_iso_build_expiry_sweep() -> None:
|
||||
"""
|
||||
Endlosschleife in einem Daemon-Thread (siehe on_startup unten) - stuendlich
|
||||
abgelaufene ISO-Builds raeumen. Ein Fehler in einem Durchlauf (z.B.
|
||||
kurzzeitig nicht erreichbare DB) darf die Schleife nicht beenden, sonst
|
||||
liefe der Sweep fuer den Rest der Prozesslaufzeit gar nicht mehr.
|
||||
"""
|
||||
|
||||
while True:
|
||||
time.sleep(ISO_BUILD_EXPIRY_SWEEP_INTERVAL_SECONDS)
|
||||
|
||||
try:
|
||||
anzahl = cleanup_expired_iso_builds()
|
||||
if anzahl:
|
||||
print(f"[iso-build-expiry] {anzahl} abgelaufene(n) ISO-Build(s) geloescht.")
|
||||
except Exception as exc:
|
||||
print(f"[iso-build-expiry] Sweep-Fehler (naechster Versuch in {ISO_BUILD_EXPIRY_SWEEP_INTERVAL_SECONDS}s): {exc}")
|
||||
|
||||
def compute_iso_volid(organization_name: str) -> str:
|
||||
"""
|
||||
ISO9660-Volume-ID fuer eine personalisierte Kunden-ISO: Organisationsname
|
||||
|
||||
5
migrations/0021_iso_build_expiry.sql
Normal file
5
migrations/0021_iso_build_expiry.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- ISO-Ablauf nach 7 Tagen (siehe ADR-Nachtrag zu ADR-0014). Ergaenzt die
|
||||
-- bestehende "neuer Build ersetzt alten Build derselben Org"-Aufraeumung
|
||||
-- (cleanup_old_iso_builds()) um eine reine Zeitdimension, die unabhaengig
|
||||
-- davon greift, ob die Organisation je erneut baut.
|
||||
ALTER TABLE iso_builds ADD COLUMN expires_at TIMESTAMPTZ;
|
||||
Loading…
x
Reference in New Issue
Block a user