Files
duty-ai-ops/src/k8s/nodes.rs
2025-12-24 01:36:12 +00:00

182 lines
5.7 KiB
Rust

use super::client::KubeClient;
use super::types::{NodeCondition, NodeDetails, NodeResources, NodeStatus};
use k8s_openapi::api::core::v1::{Node, Pod};
use kube::{api::ListParams, Api};
impl KubeClient {
/// Gets list of all nodes and their statuses
pub async fn get_nodes(&self) -> Result<Vec<NodeStatus>, Box<dyn std::error::Error>> {
let nodes: Api<Node> = Api::all(self.inner().clone());
let node_list = nodes.list(&ListParams::default()).await?;
let mut result = Vec::new();
for node in node_list.items {
let name = node
.metadata
.name
.clone()
.unwrap_or_else(|| "unknown".to_string());
let ready = node
.status
.as_ref()
.and_then(|s| s.conditions.as_ref())
.and_then(|conditions| {
conditions
.iter()
.find(|c| c.type_ == "Ready")
.map(|c| c.status == "True")
})
.unwrap_or(false);
let version = node
.status
.as_ref()
.and_then(|s| s.node_info.as_ref())
.map(|info| info.kubelet_version.clone())
.unwrap_or_else(|| "unknown".to_string());
let internal_ip = node
.status
.as_ref()
.and_then(|s| s.addresses.as_ref())
.and_then(|addresses| {
addresses
.iter()
.find(|a| a.type_ == "InternalIP")
.map(|a| a.address.clone())
});
result.push(NodeStatus {
name,
ready,
version,
internal_ip,
});
}
Ok(result)
}
/// Gets detailed information about a specific node for diagnostics
pub async fn get_node_details(
&self,
node_name: &str,
) -> Result<NodeDetails, Box<dyn std::error::Error>> {
let nodes: Api<Node> = Api::all(self.inner().clone());
let node = nodes.get(node_name).await?;
// Conditions
let conditions = node
.status
.as_ref()
.and_then(|s| s.conditions.as_ref())
.map(|conds| {
conds
.iter()
.map(|c| NodeCondition {
type_: c.type_.clone(),
status: c.status.clone(),
reason: c.reason.clone(),
message: c.message.clone(),
})
.collect()
})
.unwrap_or_default();
// Resources - capacity
let capacity = node
.status
.as_ref()
.and_then(|s| s.capacity.as_ref())
.map(|c| NodeResources {
cpu: c.get("cpu").map(|q| q.0.clone()).unwrap_or_default(),
memory: c.get("memory").map(|q| q.0.clone()).unwrap_or_default(),
pods: c.get("pods").map(|q| q.0.clone()).unwrap_or_default(),
})
.unwrap_or(NodeResources {
cpu: "unknown".to_string(),
memory: "unknown".to_string(),
pods: "unknown".to_string(),
});
// Resources - allocatable
let allocatable = node
.status
.as_ref()
.and_then(|s| s.allocatable.as_ref())
.map(|a| NodeResources {
cpu: a.get("cpu").map(|q| q.0.clone()).unwrap_or_default(),
memory: a.get("memory").map(|q| q.0.clone()).unwrap_or_default(),
pods: a.get("pods").map(|q| q.0.clone()).unwrap_or_default(),
})
.unwrap_or(NodeResources {
cpu: "unknown".to_string(),
memory: "unknown".to_string(),
pods: "unknown".to_string(),
});
// Taints
let taints = node
.spec
.as_ref()
.and_then(|s| s.taints.as_ref())
.map(|t| {
t.iter()
.map(|taint| {
format!(
"{}={} (effect: {})",
taint.key,
taint.value.as_deref().unwrap_or(""),
taint.effect
)
})
.collect()
})
.unwrap_or_default();
// Labels
let labels = node
.metadata
.labels
.clone()
.unwrap_or_default()
.into_iter()
.collect::<Vec<_>>();
// Count pods on the node
let pods: Api<Pod> = Api::all(self.inner().clone());
let pod_list = pods.list(&ListParams::default()).await?;
let pod_count = pod_list
.items
.iter()
.filter(|pod| {
pod.spec
.as_ref()
.and_then(|s| s.node_name.as_ref())
.map(|n| n == node_name)
.unwrap_or(false)
})
.count();
let version = node
.status
.as_ref()
.and_then(|s| s.node_info.as_ref())
.map(|info| info.kubelet_version.clone())
.unwrap_or_else(|| "unknown".to_string());
Ok(NodeDetails {
name: node_name.to_string(),
conditions,
capacity,
allocatable,
taints,
labels,
pod_count,
version,
})
}
}