Skip to content

feat(observability): deploy github-repo-health-exporter with dashboard and alerts (JDWLABS-388) - #385

Open
jdwillmsen wants to merge 1 commit into
mainfrom
feat/JDWLABS-388-repo-health-exporter
Open

feat(observability): deploy github-repo-health-exporter with dashboard and alerts (JDWLABS-388)#385
jdwillmsen wants to merge 1 commit into
mainfrom
feat/JDWLABS-388-repo-health-exporter

Conversation

@jdwillmsen

Copy link
Copy Markdown
Member

Summary

Deploys the github-repo-health-exporter (released by apps as 0.0.1, JDWLABS-423) as a platform tenant service, scraped by kube-prometheus-stack, with one Git-Synced Grafana dashboard and a PrometheusRule on the series it emits. Modelled on truenas-graphite-exporter (deployment + service + servicemonitor) with the App key delivered by ExternalSecret the way ai-sre-relay does.

Image: jdwlabs/github-repo-health-exporter:0.0.1@sha256:3f697eefb82e9cd1266efee0ba13e32f82bcc9a1956c08cb1d51e31f670b33c4 (OCI index digest, multi-arch).

Files

  • tenants/platform/services/github-repo-health-exporter/postInstall/{deployment,service,servicemonitor,externalsecret}.yaml
  • tenants/platform/tenant.yaml — registered as rawManifests in monitoring, syncWave 2
  • tenants/platform/services/kube-prometheus-stack/postInstall/rules-github-repo-health.yaml
  • tests/prometheus-rules/rules-github-repo-health_test.yaml
  • observability/dashboards/platform/repo-health.json (uid platform-repo-health; Git Sync already bound to this folder)

Metrics the exporter emits (all read by dashboard/rules — nothing invented)

  • jdwlabs_repo_ci_consecutive_failures{repo,workflow}
  • jdwlabs_repo_ci_main_failure_ratio{repo,workflow}
  • jdwlabs_repo_health_exporter_sample_size{repo,workflow}
  • jdwlabs_repo_health_exporter_refreshes_total{outcome}
  • jdwlabs_repo_health_exporter_last_refresh_timestamp_seconds

Alerts

Alert Expr Severity
RepoCiWorkflowFailingOnMain jdwlabs_repo_ci_consecutive_failures >= 3 for 1h warning
RepoCiWorkflowFailingOnMainWeek >= 7 for 1h critical
RepoHealthExporterStale no successful refresh in 1h, for 15m warning
RepoHealthExporterRefreshFailing increase(refreshes_total{outcome="failed"}[1h]) >= 3 for 15m warning
RepoHealthExporterTargetDown up == 0 or absent(up) for 15m critical

The ticket also asks for an alert on merged PRs without approving review — the exporter's current slice does not emit that metric (only the CI-health family shipped in apps#190), so there is nothing to alert on yet. Follow-up when that slice lands.

Manual pre-steps

  1. Grant the jdwlabs-agent-bot GitHub App Actions: Read. The Vault agent-bot secret is reused (no new credential), but the org installation currently holds only contents: write, metadata: read, pull_requests: write (checked via gh api /orgs/jdwlabs/installations). GET /repos/{owner}/{repo}/actions/runs needs actions: read; without it every refresh 403s and RepoHealthExporterStale fires ~1h15m after rollout. Add the permission in the App settings, then accept the updated permissions on the org installation.
  2. Nothing else: no new Vault path, no ingress, no pushgateway.

Test plan

  • tests/prometheus-rules/run.shpromtool check rules + unit tests green (19 rule sets, new tests cover healthy-silent, 3→7 escalation, short streak never fires, never-refreshed stale, one-repo-failing, target down + absent)
  • yamllint with the CI config on the touched files
  • kubeconform -strict on the new manifests (5/5 valid)
  • tools/check-image-pins.py, tools/check-gitsync-tenant-folders.py, tools/tests unittests
  • tests/alertmanager-routing/run.sh (24 cases, 0 failures)
  • platformctl tenants validate tenants/
  • dashboard JSON parses
  • After merge + pre-step 1: up{job="github-repo-health-exporter"}==1, /metrics lists all four repos, dashboard appears in the platform Git Sync folder, RepoCiWorkflowFailingOnMain fires for deployments / prd Drift if it is still red

Rollback: delete the tenant directory, the tenant.yaml entry, the rules file + its test, and the dashboard JSON.

Refs JDWLABS-388.

🤖 Generated with Claude Code

https://claude.ai/code/session_016XRddumw4ZNvFqHSd9KSf6

…d and alerts

Cross-repo CI health had no durable sink: a scheduled workflow on main
failed 13 of 15 daily runs and the only place that showed was a step
summary nobody opened. The exporter image now exists; this deploys it
as a tenant service scraped by kube-prometheus-stack, gives it one
Git-Synced dashboard, and alerts on the series it actually emits.

- Deployment/Service/ServiceMonitor in monitoring, digest-pinned image,
  same hardening as the sibling exporters.
- ExternalSecret reuses the org agent-bot App key already in Vault; the
  exporter only lists Actions runs, so no new credential.
- PrometheusRule: consecutive-failure streak on main (warning at 3,
  critical at 7), exporter stale / refresh failing / target down. Unit
  tests under tests/prometheus-rules.
- Dashboard observability/dashboards/platform/repo-health.json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016XRddumw4ZNvFqHSd9KSf6
Comment on lines +31 to +74
- name: exporter
image: jdwlabs/github-repo-health-exporter:0.0.1@sha256:3f697eefb82e9cd1266efee0ba13e32f82bcc9a1956c08cb1d51e31f670b33c4
ports:
- name: metrics
containerPort: 9090
protocol: TCP
envFrom:
- secretRef:
name: github-repo-health-exporter
env:
- name: PORT
value: "9090"
- name: GITHUB_ORG
value: "jdwlabs"
# 15m is the cadence the originating investigation asked for; at
# one request per repo per cycle this is far below the App's
# rate limit and Prometheus scrapes it at 60s regardless.
- name: REFRESH_INTERVAL
value: "15m"
- name: GOMEMLIMIT
value: "48MiB"
readinessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 10
periodSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 64Mi
Comment on lines +31 to +74
- name: exporter
image: jdwlabs/github-repo-health-exporter:0.0.1@sha256:3f697eefb82e9cd1266efee0ba13e32f82bcc9a1956c08cb1d51e31f670b33c4
ports:
- name: metrics
containerPort: 9090
protocol: TCP
envFrom:
- secretRef:
name: github-repo-health-exporter
env:
- name: PORT
value: "9090"
- name: GITHUB_ORG
value: "jdwlabs"
# 15m is the cadence the originating investigation asked for; at
# one request per repo per cycle this is far below the App's
# rate limit and Prometheus scrapes it at 60s regardless.
- name: REFRESH_INTERVAL
value: "15m"
- name: GOMEMLIMIT
value: "48MiB"
readinessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 10
periodSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 64Mi
Comment on lines +31 to +74
- name: exporter
image: jdwlabs/github-repo-health-exporter:0.0.1@sha256:3f697eefb82e9cd1266efee0ba13e32f82bcc9a1956c08cb1d51e31f670b33c4
ports:
- name: metrics
containerPort: 9090
protocol: TCP
envFrom:
- secretRef:
name: github-repo-health-exporter
env:
- name: PORT
value: "9090"
- name: GITHUB_ORG
value: "jdwlabs"
# 15m is the cadence the originating investigation asked for; at
# one request per repo per cycle this is far below the App's
# rate limit and Prometheus scrapes it at 60s regardless.
- name: REFRESH_INTERVAL
value: "15m"
- name: GOMEMLIMIT
value: "48MiB"
readinessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 10
periodSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 64Mi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants