fleet-manager: checkpoint 4 — InfluxDB metrics gateway (write + Flux read)
The manager is the ONLY thing that talks to InfluxDB, both directions; hosts hold
no token and never reach it.
- influx.go: influxSink implements MetricsSink — each agent HealthSnapshot is
written as a fleet_health point, identity (serial/hostnqn/starship) as TAGS
from the AUTHENTICATED stream (a host can only write its own series), telemetry
as fields. QueryField answers the SPA's read: a Flux query built with a strict
FIELD WHITELIST + hex-serial guard + parsed-duration range, so no caller input
is interpolated raw (injection-safe). Server-side influxdb-client-go/v2.
- admin.go: GET /api/hosts/{serial}/metrics?field=&range= -> time-series JSON;
503 when InfluxDB isn't wired, 400 on a bad range, 404 for unknown host.
- main.go: sink created when FLEET_MGR_INFLUX_URL+_TOKEN set, fed to the agent
hub; nil -> no-op sink so the manager runs without metrics.
Tests: snapshotPoint tags/fields/time, fluxQuery contents + %q quoting, and
QueryField rejects a non-hex serial + non-whitelisted field. build/test/vet/gofmt
clean. Live InfluxDB path validated in the compose bench (ckpt 6).
This commit is contained in:
@@ -66,7 +66,7 @@ inspectable, reproducible; a DB swap is a drop-in behind the `Store` interface.
|
||||
| `GET /api/enrol/pending` | ✅ ckpt 3 — proxies the broker queue |
|
||||
| `POST /api/enrol/{id}/approve` | ✅ ckpt 3 — signs via broker + **births inventory** (identity/binding/enrolled) |
|
||||
| `POST /api/enrol/{id}/deny` | ✅ ckpt 3 |
|
||||
| `GET /api/hosts/{serial}/metrics` | ⏳ ckpt 4 (InfluxDB/Flux) |
|
||||
| `GET /api/hosts/{serial}/metrics?field=&range=` | ✅ ckpt 4 — Flux time-series (field whitelist) |
|
||||
|
||||
The authorise pillar drives the enrol-broker's admin plane over mTLS (the manager
|
||||
is a service client, gated by the dedicated admin CA) asking for JSON, so an
|
||||
@@ -95,6 +95,10 @@ inventory for offline delivery. Metrics writing is behind a `MetricsSink`
|
||||
| `FLEET_MGR_BROKER_URL` | — | enrol-broker admin base URL (e.g. `https://127.0.0.1:9443`). Authorise pillar enabled only when set. |
|
||||
| `FLEET_MGR_BROKER_CLIENT_CERT` / `_KEY` | — | manager's client cert for the broker admin mTLS (admin CA) |
|
||||
| `FLEET_MGR_BROKER_CA` | — | CA that signs the broker's admin server cert |
|
||||
| `FLEET_MGR_INFLUX_URL` | — | InfluxDB URL (private mgmt net). Metrics enabled only when URL + token set. |
|
||||
| `FLEET_MGR_INFLUX_TOKEN` | — | InfluxDB token — **server-side only**, never on hosts |
|
||||
| `FLEET_MGR_INFLUX_ORG` | `fleet` | InfluxDB org |
|
||||
| `FLEET_MGR_INFLUX_BUCKET` | `fleet_health` | InfluxDB bucket |
|
||||
| `FLEET_MGR_INVENTORY` | `/var/lib/fleet-manager/inventory.json` | inventory file (mount a volume) |
|
||||
| `FLEET_MGR_SEED` | `false` | inject demo hosts so the SPA has data — dev only |
|
||||
| `HOSTNQN_BASE` | `nqn.2026-06.org.example.fleet:host-` | hostnqn prefix (match the broker) |
|
||||
@@ -121,5 +125,9 @@ curl -s localhost:9500/api/hosts | jq '.hosts[] | {name:.identity.starship_name,
|
||||
`Accept`-gated JSON approve response (browsers/`verify-serving.sh` unchanged);
|
||||
the manager drives it over mTLS and births the inventory record on approve.
|
||||
httptest-mock-broker test.
|
||||
- Next: InfluxDB read/write (ckpt 4) → themed SPA (ckpt 5) → Docker/compose + PKI
|
||||
+ docs (ckpt 6).
|
||||
- **Checkpoint 4 (done):** InfluxDB metrics gateway — snapshots written as points
|
||||
tagged with the authenticated identity (`MetricsSink`→`influxSink`), and a
|
||||
read API (`/api/hosts/{serial}/metrics`) that runs a Flux query with a **field
|
||||
whitelist** + hex-serial guard (no caller input reaches the query raw). Unit
|
||||
tests for point construction, query building, and unsafe-input rejection.
|
||||
- Next: themed SPA (ckpt 5) → Docker/compose + PKI + docs (ckpt 6).
|
||||
|
||||
Reference in New Issue
Block a user