use crate::docker; use crate::process::{self, ProcInfo}; use std::collections::HashMap; use std::time::Duration; use tokio::sync::mpsc; use tokio::task; use tokio::time::sleep; #[derive(Clone, Copy, Default, Debug, PartialEq)] pub struct StackInfo { pub containers: u32, pub running_containers: u32, pub stopped_containers: u32, pub process_count: u32, pub cpu_percent: f64, pub memory_usage: usize, pub memory_percent: f64, } pub fn spawn_monitor(stack: docker::Stack) -> mpsc::Receiver { let (tx, rx) = mpsc::channel(64); task::spawn(monitor_proc(tx, stack)); rx } async fn monitor_proc(tx: mpsc::Sender, stack: docker::Stack) -> anyhow::Result<()> { let mut proc_monitors: HashMap> = HashMap::new(); let mut processes = HashMap::::new(); let mut last_stack_info = StackInfo::default(); loop { let mut stack_info = StackInfo::default(); let containers = docker::list_containers(&stack).await?; stack_info.containers = containers.len() as u32; stack_info.running_containers = containers.iter().filter(|c| c.is_running()).count() as u32; stack_info.stopped_containers = stack_info.containers - stack_info.running_containers; processes.clear(); for container in &containers { for process in docker::list_processes(&stack, container).await? { processes.insert(process.pid, Default::default()); if !proc_monitors.contains_key(&process.pid) { proc_monitors.insert(process.pid, process::spawn_monitor(process.pid)); } } } proc_monitors.retain(|&pid, monitor| { if !processes.contains_key(&pid) { return false; } match monitor.try_recv() { Ok(info) => { processes.insert(pid, info); true } Err(mpsc::error::TryRecvError::Empty) => true, Err(_) => false, } }); let memory_usage = processes.values().map(|p| p.memory_usage).sum(); let host_memory = (1usize << 20) * 16; // 10 GiB // TODO stack_info = StackInfo { process_count: processes.len() as u32, memory_usage, memory_percent: memory_usage as f64 / host_memory as f64, cpu_percent: processes .values() .map(|p| p.cpu_percent) .sum::() .max(0.0), ..stack_info }; if stack_info != last_stack_info { last_stack_info = stack_info; tx.send(stack_info).await?; } sleep(Duration::from_secs(1)).await; } }