export hyperhive.agent.needs_login and alert per agent
This commit is contained in:
parent
5af927bfaa
commit
9043be4a61
3 changed files with 117 additions and 12 deletions
|
|
@ -496,6 +496,7 @@ impl Coordinator {
|
|||
}
|
||||
};
|
||||
crate::host_stats::refresh_pending_logins(&fresh);
|
||||
crate::host_stats::record_login_states(&fresh);
|
||||
let mut last = self.last_containers.lock().await;
|
||||
let mut changed_or_new = Vec::new();
|
||||
let mut removed = Vec::new();
|
||||
|
|
|
|||
|
|
@ -113,6 +113,40 @@ pub fn refresh_pending_logins(containers: &[ContainerView]) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Per-agent login state of the last container snapshot; see
|
||||
/// [`record_login_states`].
|
||||
static LOGIN_STATES: std::sync::Mutex<Vec<(String, u64)>> = std::sync::Mutex::new(Vec::new());
|
||||
|
||||
/// `(agent, 1 if it needs a login else 0)` for every running agent. A stopped
|
||||
/// agent has no entry: `needs_login` is already running-gated in
|
||||
/// `container_view::build_all`, so its `false` would read as "logged in".
|
||||
#[must_use]
|
||||
pub fn login_readings(containers: &[ContainerView]) -> Vec<(String, u64)> {
|
||||
containers
|
||||
.iter()
|
||||
.filter(|c| c.running)
|
||||
.map(|c| (c.name.clone(), u64::from(c.needs_login)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Hold the login state of the current container snapshot for the
|
||||
/// `hyperhive.agent.needs_login` gauge. Driven by the container rescan, next
|
||||
/// to [`refresh_pending_logins`].
|
||||
pub fn record_login_states(containers: &[ContainerView]) {
|
||||
*LOGIN_STATES
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = login_readings(containers);
|
||||
}
|
||||
|
||||
/// The held readings of [`record_login_states`]; empty before the first rescan.
|
||||
#[must_use]
|
||||
pub fn held_login_states() -> Vec<(String, u64)> {
|
||||
LOGIN_STATES
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Agent-state warnings derived from the live container snapshot: agents
|
||||
/// that need a claude login. Takes the container list from its caller
|
||||
/// because that is where the snapshot lives.
|
||||
|
|
@ -307,6 +341,20 @@ mod tests {
|
|||
assert!(current().is_none(), "no agent needing a login clears it");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn login_readings_are_per_running_agent_by_name() {
|
||||
let mut stopped = cv("zed", false);
|
||||
stopped.running = false;
|
||||
let mut machine = cv("amy", true);
|
||||
machine.container = "h-amy".to_owned();
|
||||
let readings = login_readings(&[machine, cv("bob", false), stopped]);
|
||||
assert_eq!(
|
||||
readings,
|
||||
vec![("amy".to_owned(), 1), ("bob".to_owned(), 0)],
|
||||
"agent names, 1 for needs-login, 0 for logged in, nothing for stopped"
|
||||
);
|
||||
}
|
||||
|
||||
/// The gauge value is a fraction, so a percent leaking through would put
|
||||
/// every alert threshold 100x off.
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ fn register_instruments(provider: &SdkMeterProvider, snapshot: Arc<Mutex<Vec<Con
|
|||
.build();
|
||||
|
||||
register_closure_pullable(&meter);
|
||||
register_needs_login(&meter);
|
||||
register_hive_instruments(&meter);
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +282,20 @@ fn register_closure_pullable(meter: &opentelemetry::metrics::Meter) {
|
|||
.build();
|
||||
}
|
||||
|
||||
/// 1 when a running agent needs a claude login, else 0, from the container
|
||||
/// snapshot the dashboard shows. A stopped agent reports nothing.
|
||||
fn register_needs_login(meter: &opentelemetry::metrics::Meter) {
|
||||
meter
|
||||
.u64_observable_gauge("hyperhive.agent.needs_login")
|
||||
.with_unit("1")
|
||||
.with_callback(|obs| {
|
||||
for (agent, value) in crate::host_stats::held_login_states() {
|
||||
obs.observe(value, &[KeyValue::new("agent", agent)]);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
/// The gauge value for a sampled container: `None` for one that belongs to no
|
||||
/// agent (the CI runner), else [`unrestricted_value`] for its agent.
|
||||
fn unrestricted_reading(stored: &permissions::Stored, c: &ContainerResource) -> Option<u64> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue