57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
CHECKER = ROOT / "scripts" / "ops" / "backup-alert-label-contract-check.py"
|
|
RULES = ROOT / "ops" / "monitoring" / "alerts-unified.yml"
|
|
BASELINE = ROOT / "ops" / "reboot-recovery" / "full-stack-backup-baseline.yml"
|
|
|
|
|
|
def _run(rules: Path) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
[
|
|
sys.executable,
|
|
str(CHECKER),
|
|
"--rules",
|
|
str(rules),
|
|
"--baseline",
|
|
str(BASELINE),
|
|
],
|
|
cwd=ROOT,
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
|
|
|
|
def test_checker_accepts_firing_only_receipt_contract() -> None:
|
|
result = _run(RULES)
|
|
|
|
assert result.returncode == 0, result.stderr
|
|
assert "requires receipts only for actively firing backup/Gitea alerts" in result.stdout
|
|
assert "BACKUP_ALERT_LABEL_CONTRACT_OK" in result.stdout
|
|
|
|
|
|
def test_checker_rejects_non_firing_receipt_selector(tmp_path: Path) -> None:
|
|
payload = yaml.safe_load(RULES.read_text(encoding="utf-8"))
|
|
for group in payload["groups"]:
|
|
for rule in group.get("rules", []):
|
|
if rule.get("alert") == "BackupAlertReceiptStageMissing":
|
|
rule["expr"] = str(rule["expr"]).replace(
|
|
'alertstate="firing"',
|
|
'alertstate="pending"',
|
|
)
|
|
candidate = tmp_path / "alerts.yml"
|
|
candidate.write_text(yaml.safe_dump(payload, allow_unicode=True, sort_keys=False), encoding="utf-8")
|
|
|
|
result = _run(candidate)
|
|
|
|
assert result.returncode != 0
|
|
assert 'alertstate="firing"' in result.stderr
|