FUG-117: HITL Grafana dashboarding — /metrics endpoint + Grafana Cloud wiring + dashboards-as-code - #90
Open
issuefleet[bot] wants to merge 3 commits into
Open
Conversation
Expose reservation-queue, per-DUT occupancy, lifecycle counters, and host CPU/memory/temperature from the HITL daemon in Prometheus text-exposition format, ready for a Grafana Alloy agent to scrape and remote_write to Grafana Cloud. - internal/metrics: a stdlib-only Prometheus text-format Writer (keeps the hitl module dependency-free per MODULE.bazel) + host stat readers (/proc/loadavg, /proc/meminfo, thermal zone), each degrading gracefully when unreadable. - queue.Manager: monotonic lifecycle counters + Metrics() snapshot reporting true per-DUT occupancy and queue depth (vs the legacy Status() summary). - hitl-managerd: GET /metrics wiring + writeMetrics composition. - Unit tests for exposition format, host parsing, manager counters, and the composed output; smoke-tested against a live daemon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the observability scaffolding around the new /metrics endpoint: - observability/README.md: the scoping doc — architecture (push/remote_write model chosen because rigs are tailnet-only), metric catalog, GitHub reporting options, Grafana Cloud free-tier setup, and what's operator setup vs. shipped. - observability/alloy.alloy: Grafana Alloy pipeline — scrape the daemon's /metrics + host node metrics, remote_write to Grafana Cloud (creds from env). - observability/alloy.nix: opt-in NixOS module to run Alloy on a rig; gated on a grafana.env (ConditionPathExists) so importing it never breaks an unwired rig. - observability/dashboards/hitl-rigs.json: the dashboard as code (rig + datasource template vars; queue/occupancy/lifecycle/host-resource panels). - observability/push-ci-metrics.sh: optional per-run CI pass/fail push to Grafana Cloud Loki (curl-only, no-ops without creds); wired into hitl.yaml as a final always() step. - .github/workflows/grafana-dashboards.yaml: dashboards-as-code sync — POST dashboards/*.json to the Grafana API on change to main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
FUG-117: HITL Grafana dashboarding
Scopes out and implements Grafana dashboarding for HITL rig status — CPU/memory,
job-queue depth, per-DUT occupancy, reservation lifecycle, and CI job pass/fail —
using Grafana Cloud (free tier) with dashboards kept as code in the repo.
Full design/scoping write-up:
pi/hitl/observability/README.md.Architecture
The rigs are tailnet-only (no inbound), so instead of Grafana scraping in, a
Grafana Alloy agent runs on each Pi and
remote_writes metrics out toGrafana Cloud. CI reports job results separately.
1. Reporting from the runners (implemented, tested)
hitl-managerdnow servesGET /metricsin Prometheus text-exposition format.internal/metricspackage: a stdlib-only exposition writer (thepi/hitlGo module is deliberately dependency-free, so noclient_golang) plushost stat readers (
/proc/loadavg,/proc/meminfo, thermal zone) that degradegracefully when a source is unreadable.
queue.Managergains monotonic lifecycle counters and aMetrics()snapshotwith true per-DUT occupancy and queue depth.
hitl_queue_depth,hitl_duts_total/_busy,hitl_dut_busy{device},hitl_active_reservations,hitl_lease_seconds, andhitl_reservations_total/_activations_total/_releases_total/_lease_expirations_total/_start_failures_total, plus hosthitl_host_load1/_memory_*_bytes/_temperature_celsius. Every seriescarries a
riglabel.composed output; smoke-tested against a live daemon.
2. Reporting from GitHub
workflow/job pass-fail + duration via the API and ships a prebuilt dashboard.
observability/push-ci-metrics.shpushes each HITL run'spass/fail to Grafana Cloud Loki (a CI job is an event, not a scrape target),
wired into
hitl.yamlas a finalalways()step that no-ops without creds.3. Grafana wiring
observability/alloy.alloy— the Alloy pipeline (scrape daemon + host nodemetrics, remote_write to Grafana Cloud; creds from env).
observability/alloy.nix— opt-in NixOS module to run Alloy on a rig, gated on/var/lib/hitl/grafana.env(ConditionPathExists) so importing it never breaksa rig that isn't wired to Grafana yet.
4. Dashboards as code (bonus)
observability/dashboards/hitl-rigs.json— the dashboard model (rig +datasource template variables; queue/occupancy/lifecycle/host panels).
.github/workflows/grafana-dashboards.yaml— CI that POSTsdashboards/*.jsonto the Grafana API on change to
main. Edit a panel via the JSON in a PR; mergesyncs it.
Verification
bazel test //pi/hitl/...— all pass./metricssmoke-tested against a runninghitl-managerd(reservation countersadvance; host mem/load emitted; temperature correctly omitted where absent).
clean.
Operator one-time setup (needs the Grafana Cloud account; can't be done here)
Create the free stack, generate
metrics:write/logs:writetokens, seedgrafana.envon the rigs, populate the "HITL"/"Grafana" GitHub Actionsenvironments, and enable the GitHub integration. The Alloy NixOS module and the
dashboard-sync workflow are provided but not build-verified in-container (no
Nix/Grafana network); they follow the same deploy path as the rest of the rig.
Closes-Linear: FUG-117 (https://linear.app/fughilli/issue/FUG-117/hitl-grafana-dashboarding)