Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-secret-client/src/grafana.rs
atlas 7d09776742 swarm UI: an alerts page, read by the controller with a Viewer Grafana token from bao
swarm-bao-grafana-controller-token, on the host that runs Grafana, logs in
to Grafana's API over its socket as the built-in admin (the password
swarm-bao-grafana-admin minted, #5138) and keeps a `swarm-controller`
service account that is an enabled Viewer. It mints a token for that
account and writes it to bao at
swarm/controller/swarm-controller/grafana/token under Grafana's leaf. A
ten-minute timer runs it again. The unit keeps a stored token Grafana
accepts. If Grafana refuses the stored token with 401, or bao holds
none, the unit deletes every token of the account and mints a new one.
Grafana's grant gains create/read/update on that leaf, and the
controller's gains read.

swarm-controller serves GET /api/alerts from Grafana's
/api/prometheus/grafana/api/v1/alerts. It reads the token from bao,
keeps it in memory, and re-reads bao once on a 401. The HTTP client has
5s connect and 15s request timeouts. Any failure is a 503 that names its
cause. swarm-ui gets an /alerts page listing them, built the same way as
the hives page.

This is option (c) on #5129, the operator's choice.
2026-10-15 15:16:21 +02:00

54 lines
1.9 KiB
Rust

//! The Grafana agreement: where `swarm-controller`'s token for Grafana's API
//! lives, and what the object at that path holds.
//!
//! `swarm-bao-grafana-controller-token`, on the host that runs Grafana, mints
//! the token for a Viewer service account and writes it here under Grafana's
//! own certificate; `swarm-controller` reads it under its own. Those two
//! principals' stanzas are the only ones in the store that cover this path.
use serde::{Deserialize, Serialize};
use crate::{
Error,
path::{Kind, principal_prefix},
};
/// The path holding `swarm-controller`'s Grafana token.
///
/// Under [`Kind::Controller`], which no hive's policy reads. The nix half is
/// `swarm-bao.nix`'s `controllerGrafanaTokenLeaf`.
///
/// # Errors
/// Never in practice: the name segment is a constant. The `Result` is
/// [`principal_prefix`]'s.
pub fn controller_token_path() -> Result<String, Error> {
let prefix = principal_prefix(Kind::Controller, crate::matrix::CONTROLLER)?;
Ok(format!("{prefix}/grafana/token"))
}
/// What [`controller_token_path`] holds.
///
/// No `Debug` derive: `value` reads every alert and dashboard in the swarm's
/// Grafana, and a derived `Debug` is one `{:?}` away from a log line.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ControllerToken {
/// The token itself. The minting unit writes it with
/// `bao kv put … value=-`, so this name is load-bearing for a writer this
/// crate does not control.
pub value: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_controllers_grafana_token_is_under_the_controllers_prefix() {
// The literal is what `swarm-bao.nix` grants Grafana's role and the
// controller's, and what the minting unit writes.
assert_eq!(
controller_token_path().expect("a constant segment is legal"),
"swarm/controller/swarm-controller/grafana/token"
);
}
}