Public Access
87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
"""Validate Prometheus alert rules with promtool check rules.
|
|
|
|
Renders an alert-rules Jinja2 template with test values and validates
|
|
the output with ``promtool check rules``. Exits 0 if valid, non-zero
|
|
otherwise. Skips (exits 0) if promtool is not on PATH.
|
|
|
|
Usage::
|
|
|
|
python -m devx.tools.check_alert_rules \\
|
|
--template-path ansible/roles/observability/templates \\
|
|
--template-name alert-rules.yml.j2
|
|
|
|
# With extra template variables:
|
|
python -m devx.tools.check_alert_rules \\
|
|
--template-path ansible/roles/observability/templates \\
|
|
--template-name alert-rules.yml.j2 \\
|
|
--var grafana_base_url=https://grafana.test.example.com
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess # nosec B404 — used to run promtool, a trusted binary
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import click
|
|
|
|
from devx.utils.jinja import make_env, render_template
|
|
|
|
|
|
@click.command()
|
|
@click.option(
|
|
"--template-path",
|
|
type=click.Path(exists=True, path_type=Path),
|
|
required=True,
|
|
help="Path to the directory containing the Jinja2 template.",
|
|
)
|
|
@click.option(
|
|
"--template-name",
|
|
default="alert-rules.yml.j2",
|
|
help="Name of the Jinja2 template file to render.",
|
|
)
|
|
@click.option(
|
|
"--var",
|
|
"template_vars",
|
|
multiple=True,
|
|
help="Template variables in key=value format (can be repeated). "
|
|
"Example: --var grafana_base_url=https://grafana.example.com",
|
|
)
|
|
def main(template_path: Path, template_name: str, template_vars: tuple[str, ...]) -> None:
|
|
"""Validate rendered alert rules with promtool."""
|
|
if not shutil.which("promtool"):
|
|
click.echo("promtool not found in PATH — skipping alert rules validation")
|
|
return
|
|
|
|
# Parse template variables
|
|
kwargs: dict[str, str] = {}
|
|
for v in template_vars:
|
|
if "=" in v:
|
|
key, value = v.split("=", 1)
|
|
kwargs[key] = value
|
|
|
|
env = make_env(str(template_path))
|
|
output = render_template(env, template_name, **kwargs)
|
|
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".yml", delete=False) as f:
|
|
f.write(output)
|
|
tmp_path = f.name
|
|
|
|
click.echo("[check-alert-rules] Validating rendered rules with promtool...")
|
|
result = subprocess.run( # nosec
|
|
["promtool", "check", "rules", tmp_path],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
click.echo(result.stdout, nl=False)
|
|
if result.returncode != 0:
|
|
click.echo(result.stderr, nl=False, err=True)
|
|
sys.exit(result.returncode)
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
main()
|