Commit Graph
1 Commits
Author SHA1 Message Date
9a3c6ef9af ci(monitoring-rules): machine-check PromQL with promtool (REN-161)
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
Nothing in this repo checked an alert expression. ci-manifests-test.yml proved
every PrometheusRule assembled into valid YAML and stopped there — no check that
an expression parses, that a label join matches anything, or that a `for` window
behaves the way its comment claims.

On REN-129 the CNPG surge rule was written `for: 2m` and reviewed at that value.
Against Iris's seven measured nights it fires on six of them: two owner emails a
night, 04:16 and 04:22 local, for a WAL backlog that clears itself in under ten
minutes. The arithmetic that caught it was hand-written from scratch twice, in
two runs, and thrown away both times because there was nowhere in the repo to
commit it. It is now a negative control.

Adds:

* scripts/ci/promtool-rules.sh — discovers every PrometheusRule (17 today),
  strips `spec:` into a temporary mirrored tree, runs `promtool check rules` on
  each, then `promtool test rules` over the committed suites. Discovery floors
  on both, as in the sibling steps.
* a `promtool` step in ci-manifests-test.yml, `depends_on: []` like the other
  four, on prom/prometheus:v3.15.0. The 3.x pin is load-bearing: the cluster
  runs kube-prometheus-stack >= 72.0.0, and alert `for` evaluation is
  version-sensitive, so a 2.x promtool would check a different evaluator than
  production. The script asserts the major version it is handed.
* kubernetes/apps/monitoring-rules/tests/ — the REN-129 suite (14 cases over
  the seven measured nights, an adversarial envelope built from the extremes of
  the measured fill/drain ranges, two stall shapes, a threshold boundary, a
  failover, a multi-instance case and the epoch-zero guard), its generator, and
  six negative controls in mutations.txt.
* a mutation phase. Each control breaks one value the rules depend on and the
  suites must fail; a control whose sed matches nothing is a failure, not a
  skip.
* scripts/ci/test-promtool-rules.sh, run from ci-scripts-test.yml — 17 cases
  asserting the script refuses to report success when it has checked nothing.

The mutation phase is not belt-and-braces, it is the point. Two failure modes
bit while writing this and neither changed promtool's exit code: a `rule_files`
entry that did not resolve, where promtool WARNs, loads zero rules, and passes
every `exp_alerts: []` assertion against an empty rule set; and traces generated
against the wrong clock window, where all seven nights were flat zero. Both were
green. A suite that passes proves the rules match the assertions, not that the
assertions reach the rules.

Production: nothing applied to the cluster. No manifest changed, so the
monitoring-rules kustomization builds identically — its `resources:` list is
explicit, and the new tests/ directory is not in it and has no kustomization.yaml
of its own, so Flux does not see it. Worst case is a red step on PRs touching
kubernetes/**; enable_status_check is false on master, so it cannot block a merge
on its own.

Verified with real promtool 3.15.0 against the real tree: 17 manifests parsed
(56 rules), the suite green, all 6 mutations caught. Run again under BusyBox
1.35.0 sh/awk/sed/find/xargs, the toolchain prom/prometheus actually ships.
Guard tests 17/17 under both bash and BusyBox sh. shellcheck 0.11.0 clean at
every severity. woodpecker-cli 3.18.0 lint clean on all 17 pipeline files.
Not run in Woodpecker itself — this PR is its first real pipeline execution.

Revert: revert this commit. It adds files and one step; nothing else reads them.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-26 08:25:59 +00:00