Files

59 lines
2.2 KiB
Python
Raw Permalink Normal View History

"""Check every alert rule against the alerting contract (see the header of
k8s/prometheus/rules/ledgerline.rules.yml).
python observability/alerts/check_contract.py
Needs PyYAML. Skips scratch.* and host.* rule files.
"""
import sys
from pathlib import Path
import yaml
ROOT = Path(__file__).resolve().parents[2]
RULES = ROOT / "k8s" / "prometheus" / "rules"
SEVERITIES = {"critical", "warning", "info"}
TEAMS = {"platform", "payments", "risk", "data"} # placeholders: edit to taste
ANNOTATIONS = ("summary", "description", "runbook_url", "dashboard")
RUNBOOK_MARKER = "/docs/runbooks/"
def check(path: Path) -> list[str]:
problems = []
for group in yaml.safe_load(path.read_text())["groups"]:
for rule in group["rules"]:
name = rule.get("alert")
if name is None: # recording rule
continue
where = f"{path.name}:{name}"
labels, ann = rule.get("labels", {}), rule.get("annotations", {})
if labels.get("severity") not in SEVERITIES:
problems.append(f"{where}: severity must be one of {sorted(SEVERITIES)}")
if labels.get("team") not in TEAMS:
problems.append(f"{where}: team must be one of {sorted(TEAMS)}")
for key in ANNOTATIONS:
if not ann.get(key):
problems.append(f"{where}: missing annotation {key}")
url = ann.get("runbook_url", "")
if RUNBOOK_MARKER in url:
local = ROOT / "docs" / "runbooks" / url.rsplit("/", 1)[-1]
if not local.exists():
problems.append(f"{where}: runbook {local.relative_to(ROOT)} does not exist")
elif url:
problems.append(f"{where}: runbook_url should point under {RUNBOOK_MARKER}")
return problems
def main() -> int:
files = [p for p in sorted(RULES.glob("*.yml")) if p.name.split(".")[0] not in ("scratch", "host")]
problems = [msg for p in files for msg in check(p)]
for msg in problems:
print(msg)
print(f"{len(files)} file(s) checked, {len(problems)} problem(s)")
return 1 if problems else 0
if __name__ == "__main__":
sys.exit(main())