hyperhive/hive-c0re/src/swarm_queue.rs

72 lines
3.1 KiB
Rust

//! One swarm-queue connection, shared by every consumer in this process.
//!
//! [`swarm_status`](crate::swarm_status) and
//! [`swarm_notices`](crate::swarm_notices) both need the swarm queue, and
//! both authenticate as the *same* identity (`hive-<name>`, minted for
//! this hive — see `swarm-authelia.nix`). Two independent `connect()`
//! calls would be two token mints and two live connections for one
//! identity, not two different credentials — the same shape that turned
//! `swarm-controller`'s own connect into the shared `swarm-queue-client`
//! crate in the first place, one layer up. This module is that same move
//! made again, this time between two consumers *inside* one process.
//!
//! Connects lazily on first use rather than at boot — nothing here
//! blocks `hive-c0re` starting up on hosts with no queue configured,
//! which is the ordinary case.
use tokio::sync::OnceCell;
/// Env var prefix for this daemon's swarm-queue credentials — see
/// [`swarm_queue_client::QueueConfig::from_env`]. All four or none.
const ENV_PREFIX: &str = "HIVE_C0RE";
static CLIENT: OnceCell<Option<async_nats::Client>> = OnceCell::const_new();
/// The shared swarm-queue client, connecting on first call and memoized
/// for the rest of the process's life.
///
/// `None` covers both "no queue configured" (the ordinary case, logged
/// once at `info`) and "config present but connecting failed" (bannered
/// once via [`crate::warnings::set_boot_warning`] the first time this is
/// called) — either way, a caller with `None` should just skip whatever
/// it was about to publish. No caller needs to distinguish the two: both
/// mean "this hive is not offering anything to the swarm right now."
pub async fn client() -> Option<async_nats::Client> {
CLIENT.get_or_init(connect_once).await.clone()
}
async fn connect_once() -> Option<async_nats::Client> {
let cfg = match swarm_queue_client::QueueConfig::from_env(ENV_PREFIX) {
Ok(Some(cfg)) => cfg,
Ok(None) => {
tracing::info!("no swarm queue configured; this hive offers nothing upward");
return None;
}
Err(e) => {
// A one-shot startup step with no later retry to clear it —
// exactly what `set_boot_warning` is for. `chain`, not
// `{:#}`: this is `swarm_queue_client::Error`, whose
// `Display` ignores the alternate flag (see `chain`'s own
// doc comment), so `{:#}` would drop which env vars are
// actually missing.
crate::warnings::set_boot_warning(
"swarm_queue_config",
"warn",
format!("swarm queue is off: {}", swarm_queue_client::chain(&e)),
);
return None;
}
};
match swarm_queue_client::connect(cfg).await {
Ok(client) => Some(client),
Err(e) => {
crate::warnings::set_boot_warning(
"swarm_queue_config",
"warn",
format!("swarm queue is off: {}", swarm_queue_client::chain(&e)),
);
None
}
}
}