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.
54 lines
1.9 KiB
Rust
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"
|
|
);
|
|
}
|
|
}
|