use koi_common::capability::CapabilityStatus;
use crate::cli::{Cli, Config};
use crate::client::KoiClient;
use crate::format;
pub fn status(cli: &Cli, config: &Config) -> anyhow::Result<()> {
if let Some(status_json) = try_daemon_status(cli) {
if cli.json {
println!("{}", serde_json::to_string_pretty(&status_json)?);
} else {
print!("{}", format::unified_status(&status_json));
}
return Ok(());
}
let capabilities = offline_capabilities(config);
let status = LocalStatus {
version: env!("CARGO_PKG_VERSION").to_string(),
platform: std::env::consts::OS.to_string(),
daemon: false,
capabilities,
};
if cli.json {
println!("{}", serde_json::to_string_pretty(&status)?);
} else {
println!("Koi v{}", status.version);
println!(" Platform: {}", status.platform);
println!(" Daemon: not running");
for cap in &status.capabilities {
let marker = if cap.healthy { "+" } else { "-" };
println!(" [{}] {}: {}", marker, cap.name, cap.summary);
}
}
Ok(())
}
#[derive(serde::Serialize)]
struct LocalStatus {
version: String,
platform: String,
daemon: bool,
capabilities: Vec<CapabilityStatus>,
}
pub fn try_daemon_status(cli: &Cli) -> Option<serde_json::Value> {
if cli.standalone {
return None;
}
let endpoint = cli
.endpoint
.clone()
.or_else(koi_config::breadcrumb::read_breadcrumb_endpoint)?;
let client = KoiClient::new(&endpoint);
if client.health().is_err() {
return None;
}
match client.unified_status() {
Ok(status_json) => Some(status_json),
Err(e) => {
tracing::debug!(error = %e, "Could not fetch unified status");
None
}
}
}
fn offline_capabilities(config: &Config) -> Vec<CapabilityStatus> {
let mut caps = Vec::new();
if config.no_mdns {
caps.push(CapabilityStatus {
name: "mdns".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
caps.push(CapabilityStatus {
name: "mdns".to_string(),
summary: "not running".to_string(),
healthy: false,
});
}
if config.no_certmesh {
caps.push(CapabilityStatus {
name: "certmesh".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
let certmesh_summary = if koi_certmesh::CertmeshPaths::default().is_ca_initialized() {
"CA initialized (daemon not running)".to_string()
} else {
"CA not initialized".to_string()
};
caps.push(CapabilityStatus {
name: "certmesh".to_string(),
summary: certmesh_summary,
healthy: false,
});
}
if config.no_dns {
caps.push(CapabilityStatus {
name: "dns".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
caps.push(CapabilityStatus {
name: "dns".to_string(),
summary: "not running".to_string(),
healthy: false,
});
}
if config.no_health {
caps.push(CapabilityStatus {
name: "health".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
caps.push(CapabilityStatus {
name: "health".to_string(),
summary: "not running".to_string(),
healthy: false,
});
}
if config.no_proxy {
caps.push(CapabilityStatus {
name: "proxy".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
caps.push(CapabilityStatus {
name: "proxy".to_string(),
summary: "not running".to_string(),
healthy: false,
});
}
if config.no_udp {
caps.push(CapabilityStatus {
name: "udp".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
caps.push(CapabilityStatus {
name: "udp".to_string(),
summary: "not running".to_string(),
healthy: false,
});
}
if config.no_runtime {
caps.push(CapabilityStatus {
name: "runtime".to_string(),
summary: "disabled".to_string(),
healthy: false,
});
} else {
caps.push(CapabilityStatus {
name: "runtime".to_string(),
summary: "not running".to_string(),
healthy: false,
});
}
caps
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn offline_all_enabled_shows_not_running() {
let config = Config::default();
let caps = offline_capabilities(&config);
assert_eq!(caps[0].name, "mdns");
assert!(!caps[0].healthy);
assert!(
caps[0].summary.contains("not running"),
"mdns summary: {}",
caps[0].summary
);
assert_eq!(caps[1].name, "certmesh");
assert!(!caps[1].healthy);
assert_eq!(caps[2].name, "dns");
assert!(!caps[2].healthy);
assert_eq!(caps[3].name, "health");
assert!(!caps[3].healthy);
assert_eq!(caps[4].name, "proxy");
assert!(!caps[4].healthy);
assert_eq!(caps[5].name, "udp");
assert!(!caps[5].healthy);
}
#[test]
fn offline_mdns_disabled() {
let config = Config {
no_mdns: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[0].name, "mdns");
assert_eq!(caps[0].summary, "disabled");
}
#[test]
fn offline_certmesh_disabled() {
let config = Config {
no_certmesh: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[1].name, "certmesh");
assert_eq!(caps[1].summary, "disabled");
}
#[test]
fn offline_dns_disabled() {
let config = Config {
no_dns: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[2].name, "dns");
assert_eq!(caps[2].summary, "disabled");
}
#[test]
fn offline_health_disabled() {
let config = Config {
no_health: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[3].name, "health");
assert_eq!(caps[3].summary, "disabled");
}
#[test]
fn offline_proxy_disabled() {
let config = Config {
no_proxy: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[4].name, "proxy");
assert_eq!(caps[4].summary, "disabled");
}
#[test]
fn offline_udp_disabled() {
let config = Config {
no_udp: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[5].name, "udp");
assert_eq!(caps[5].summary, "disabled");
}
#[test]
fn offline_both_disabled() {
let config = Config {
no_mdns: true,
no_certmesh: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[0].summary, "disabled");
assert_eq!(caps[1].summary, "disabled");
}
#[test]
fn offline_returns_seven_capabilities() {
let config = Config::default();
let caps = offline_capabilities(&config);
assert_eq!(caps.len(), 7);
}
#[test]
fn offline_runtime_disabled() {
let config = Config {
no_runtime: true,
..Config::default()
};
let caps = offline_capabilities(&config);
assert_eq!(caps[6].name, "runtime");
assert_eq!(caps[6].summary, "disabled");
}
}