Files
watcheragent/WatcherAgent/src/docker/container.rs

215 lines
6.3 KiB
Rust

//! 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<DockerContainer>` - Vector of Docker container info.
pub async fn get_available_containers(docker: &Docker) -> Vec<DockerContainer> {
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<dyn Error + Send + Sync>>` - Ok if updated successfully, error otherwise.
pub async fn update_docker_image(
docker: &Docker,
image: &str,
) -> Result<(), Box<dyn Error + Send + Sync>> {
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<dyn Error + Send + Sync>>` - Ok if restarted successfully, error otherwise.
pub async fn restart_container(
docker: &Docker,
container_id: &str,
) -> Result<(), Box<dyn Error + Send + Sync>> {
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<String>` - The extracted container ID if found.
pub fn extract_client_container_id(line: &str) -> Option<String> {
// ...existing code...
}
*/
/// Gets network statistics for a specific container
pub async fn get_network_stats(
docker: &Docker,
container_id: &str,
) -> Result<ContainerNetworkInfo, Box<dyn Error + Send + Sync>> {
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<ContainerCpuInfo, Box<dyn Error + Send + Sync>> {
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,
})
}
}