Specs stay at repo root (cross-VM). Move deploy and code into logical projects with README per domain, updated manifest.yaml, and symlinks at legacy paths for VM122 backward compatibility.
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
"""DNS gate logic — Spec 019."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
from app.migration import store
|
|
|
|
GATE_MIN_RATIO = float(os.getenv("MIGRATION_GATE_MIN_RATIO", "0.99"))
|
|
|
|
|
|
def evaluate_job(conn, job_id: int) -> dict[str, Any]:
|
|
job = store.get_job(conn, job_id)
|
|
if not job:
|
|
return {"gate": "blocked", "reason": "job_not_found"}
|
|
|
|
mailboxes = job.get("mailboxes") or []
|
|
checks: list[tuple[str, str, str]] = []
|
|
|
|
if not mailboxes:
|
|
checks.append(("mailboxes_present", "fail", "Nenhuma mailbox no job"))
|
|
gate = "blocked"
|
|
else:
|
|
ratios = []
|
|
for mb in mailboxes:
|
|
src = max(mb.get("messages_source") or 0, 1)
|
|
dst = mb.get("messages_dest") or 0
|
|
ratio = dst / src if src else 0.0
|
|
ratios.append(ratio)
|
|
if ratio < GATE_MIN_RATIO:
|
|
checks.append(
|
|
(
|
|
f"sync_{mb['email']}",
|
|
"warn" if ratio >= 0.9 else "fail",
|
|
f"{mb['email']}: {ratio * 100:.1f}% sincronizado",
|
|
)
|
|
)
|
|
avg = sum(ratios) / len(ratios) if ratios else 0.0
|
|
checks.append(("count_ratio", "pass" if avg >= GATE_MIN_RATIO else "warn", f"Média {avg * 100:.1f}%"))
|
|
|
|
if job.get("approved_by"):
|
|
checks.append(("manual_approval", "pass", f"Aprovado por {job['approved_by']}"))
|
|
gate = "ready_for_dns" if avg >= GATE_MIN_RATIO else "warning"
|
|
elif avg >= GATE_MIN_RATIO:
|
|
checks.append(("manual_approval", "fail", "Aguarda aprovação ops_lead"))
|
|
gate = "warning"
|
|
elif avg >= 0.9:
|
|
gate = "warning"
|
|
else:
|
|
gate = "blocked"
|
|
|
|
for check_id, status, message in checks:
|
|
store.add_gate_check(conn, job_id, check_id, status, message)
|
|
|
|
store.update_job(conn, job_id, migration_gate=gate, phase=job.get("phase") or "cutover_ready")
|
|
return {"gate": gate, "checks": [{"check_id": c[0], "status": c[1], "message": c[2]} for c in checks]}
|
|
|
|
|
|
def approve_gate(conn, job_id: int, approved_by: str) -> dict[str, Any]:
|
|
from datetime import datetime, timezone
|
|
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
store.update_job(conn, job_id, approved_by=approved_by, approved_at=now, phase="cutover_ready")
|
|
result = evaluate_job(conn, job_id)
|
|
if result["gate"] in ("warning", "ready_for_dns"):
|
|
store.update_job(conn, job_id, migration_gate="ready_for_dns")
|
|
result["gate"] = "ready_for_dns"
|
|
return result
|