//! Docker container utilities for WatcherAgent //! //! Provides functions to list and process Docker containers using the Bollard library. //! use crate::docker::stats; use crate::docker::stats::{ContainerCpuInfo, ContainerNetworkInfo}; use crate::models::DockerContainer; use bollard::query_parameters::{ CreateImageOptions, ListContainersOptions, RestartContainerOptions, }; use bollard::Docker; use futures_util::StreamExt; use std::error::Error; /// Returns a list of available Docker containers. /// /// # Arguments /// * `docker` - Reference to a Bollard Docker client. /// /// # Returns /// * `Vec` - Vector of Docker container info. pub async fn get_available_containers(docker: &Docker) -> Vec { println!("=== DOCKER CONTAINER LIST ==="); let options = Some(ListContainersOptions { all: true, ..Default::default() }); let containers_list = match docker.list_containers(options).await { Ok(containers) => { println!("Available containers ({}):", containers.len()); containers .into_iter() .filter_map(|container| { container.id.as_ref()?; // Skip if no ID let id = container.id?; let short_id = if id.len() > 12 { &id[..12] } else { &id }; let name = container .names .and_then(|names| names.into_iter().next()) .map(|name| name.trim_start_matches('/').to_string()) .unwrap_or_else(|| "unknown".to_string()); let image = container .image .as_ref() .map(|img| img.to_string()) .unwrap_or_else(|| "unknown".to_string()); Some(DockerContainer { id: short_id.to_string(), image: Some(image), name: Some(name), }) }) .collect() } Err(e) => { eprintln!("Failed to list containers: {}", e); Vec::new() } }; containers_list } /// Pulls a new Docker image and restarts the current container. /// /// # Arguments /// * `docker` - Reference to a Bollard Docker client. /// * `image` - The name of the Docker image to pull. /// /// # Returns /// * `Result<(), Box>` - Ok if updated successfully, error otherwise. pub async fn update_docker_image( docker: &Docker, image: &str, ) -> Result<(), Box> { println!("Updating to {}", image); // 1. Pull new image let mut stream = docker.create_image( Some(CreateImageOptions { from_image: Some(image.to_string()), ..Default::default() }), None, None, ); // Use the stream with proper trait bounds while let Some(result) = StreamExt::next(&mut stream).await { match result { Ok(progress) => { if let Some(status) = progress.status { println!("Pull status: {}", status); } } Err(e) => { eprintln!("Error pulling image: {}", e); break; } } } // 2. Restart the current container let options = Some(ListContainersOptions { all: true, ..Default::default() }); let container_id = docker .list_containers(options) .await? .into_iter() .find_map(|c| { c.image .as_ref() .and_then(|img| if img == image { c.id } else { None }) }); let _ = restart_container(docker, &container_id.unwrap()).await; Ok(()) } /// Restarts the agent's own Docker container. /// /// # Arguments /// * `docker` - Reference to a Bollard Docker client. /// /// # Returns /// * `Result<(), Box>` - Ok if restarted successfully, error otherwise. pub async fn restart_container( docker: &Docker, container_id: &str, ) -> Result<(), Box> { println!("Restarting container {}", container_id); if let Err(e) = docker .restart_container( &container_id.to_string(), Some(RestartContainerOptions { signal: None, t: Some(0), }), ) .await { eprintln!("Failed to restart container: {}", e); } Ok(()) } /* /// Extracts a Docker container ID from a string line. /// /// # Arguments /// * `line` - The input string containing a container ID or related info. /// /// # Returns /// * `Option` - The extracted container ID if found. pub fn extract_client_container_id(line: &str) -> Option { // ...existing code... } */ /// Gets network statistics for a specific container pub async fn get_network_stats( docker: &Docker, container_id: &str, ) -> Result> { let (_, net_info, _) = stats::get_single_container_stats(docker, container_id).await?; if let Some(net_info) = net_info { Ok(net_info) } else { // Return default network info if not found println!("No network info found for container {}", container_id); Ok(ContainerNetworkInfo { container_id: Some(container_id.to_string()), rx_bytes: None, tx_bytes: None, rx_packets: None, tx_packets: None, rx_errors: None, tx_errors: None, }) } } /// Gets CPU statistics for a specific container pub async fn get_cpu_stats( docker: &Docker, container_id: &str, ) -> Result> { let (cpu_info, _, _) = stats::get_single_container_stats(docker, container_id).await?; if let Some(cpu_info) = cpu_info { Ok(cpu_info) } else { // Return default CPU info if not found println!("No CPU info found for container {}", container_id); Ok(ContainerCpuInfo { container_id: Some(container_id.to_string()), cpu_usage_percent: None, system_cpu_usage: None, container_cpu_usage: None, online_cpus: None, }) } }