Add agent hash display to system panel

Implement agent version tracking to diagnose deployment issues:
- Add get_agent_hash() method to extract Nix store hash from executable path
- Collect system_agent_hash metric in NixOS collector
- Display "Agent Hash" in system panel under NixOS section
- Update metric filtering to include agent hash

This helps identify which version of the agent is actually running
when troubleshooting deployment or metric collection issues.
This commit is contained in:
Christoffer Martinsson 2025-10-23 17:33:45 +02:00
parent 4193a97737
commit c5ec529210
3 changed files with 55 additions and 1 deletions

View File

@ -46,6 +46,23 @@ impl NixOSCollector {
Ok(clean_version)
}
/// Get agent hash from binary path
fn get_agent_hash(&self) -> Result<String, Box<dyn std::error::Error>> {
// Get the path of the current executable
let exe_path = std::env::current_exe()?;
let exe_str = exe_path.to_string_lossy();
// Extract Nix store hash from path like /nix/store/fn804fh332mp8gz06qawminpj20xl25h-cm-dashboard-0.1.0/bin/cm-dashboard-agent
if let Some(store_path) = exe_str.strip_prefix("/nix/store/") {
if let Some(dash_pos) = store_path.find('-') {
return Ok(store_path[..dash_pos].to_string());
}
}
// Fallback to "unknown" if not in Nix store
Ok("unknown".to_string())
}
/// Get currently active users
fn get_active_users(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let output = Command::new("who").output()?;
@ -131,6 +148,31 @@ impl Collector for NixOSCollector {
}
}
// Collect agent hash
match self.get_agent_hash() {
Ok(hash) => {
metrics.push(Metric {
name: "system_agent_hash".to_string(),
value: MetricValue::String(hash),
unit: None,
description: Some("Agent Nix store hash".to_string()),
status: Status::Ok,
timestamp,
});
}
Err(e) => {
debug!("Failed to get agent hash: {}", e);
metrics.push(Metric {
name: "system_agent_hash".to_string(),
value: MetricValue::String("unknown".to_string()),
unit: None,
description: Some("Agent hash (failed to detect)".to_string()),
status: Status::Unknown,
timestamp,
});
}
}
debug!("Collected {} NixOS metrics", metrics.len());
Ok(metrics)
}

View File

@ -119,7 +119,7 @@ impl TuiApp {
// Add NixOS metrics - using exact matching for build display fix
let nixos_metrics: Vec<&Metric> = all_metrics
.iter()
.filter(|m| m.name == "system_nixos_build" || m.name == "system_active_users")
.filter(|m| m.name == "system_nixos_build" || m.name == "system_active_users" || m.name == "system_agent_hash")
.copied()
.collect();
system_metrics.extend(nixos_metrics);

View File

@ -16,6 +16,7 @@ pub struct SystemWidget {
// NixOS information
nixos_build: Option<String>,
active_users: Option<String>,
agent_hash: Option<String>,
// CPU metrics
cpu_load_1min: Option<f32>,
@ -64,6 +65,7 @@ impl SystemWidget {
Self {
nixos_build: None,
active_users: None,
agent_hash: None,
cpu_load_1min: None,
cpu_load_5min: None,
cpu_load_15min: None,
@ -313,6 +315,11 @@ impl Widget for SystemWidget {
self.active_users = Some(users.clone());
}
}
"system_agent_hash" => {
if let MetricValue::String(hash) = &metric.value {
self.agent_hash = Some(hash.clone());
}
}
// CPU metrics
"cpu_load_1min" => {
@ -397,6 +404,11 @@ impl Widget for SystemWidget {
Span::styled(format!("Active users: {}", users_text), Typography::secondary())
]));
let agent_hash_text = self.agent_hash.as_deref().unwrap_or("unknown");
lines.push(Line::from(vec![
Span::styled(format!("Agent Hash: {}", agent_hash_text), Typography::secondary())
]));
// CPU section
lines.push(Line::from(vec![
Span::styled("CPU:", Typography::widget_title())