All checks were successful
Rust Cross-Platform Build / Detect Rust Project (push) Successful in 4s
Rust Cross-Platform Build / Set Tag Name (push) Successful in 4s
Rust Cross-Platform Build / Run Tests (push) Successful in 1m2s
Rust Cross-Platform Build / Build (x86_64-unknown-linux-gnu) (push) Successful in 2m18s
Rust Cross-Platform Build / Build (x86_64-pc-windows-gnu) (push) Successful in 3m25s
Rust Cross-Platform Build / Build and Push Docker Image (push) Successful in 2m0s
Rust Cross-Platform Build / Workflow Summary (push) Successful in 2s
Rust Cross-Platform Build / Create Tag (push) Successful in 5s
232 lines
6.9 KiB
Rust
232 lines
6.9 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::{DockerContainerDto, DockerContainerRegistrationDto};
|
|
|
|
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<DockerContainerDto> {
|
|
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());
|
|
|
|
/*let status = container
|
|
.status
|
|
.as_ref()
|
|
.map(|s| match s.to_lowercase().as_str() {
|
|
s if s.contains("up") || s.contains("running") => "running".to_string(),
|
|
s if s.contains("exited") || s.contains("stopped") => {
|
|
"stopped".to_string()
|
|
}
|
|
_ => s.to_string(),
|
|
})
|
|
.unwrap_or_else(|| "unknown".to_string());
|
|
|
|
println!(
|
|
" - ID: {}, Image: {}, Name: {}",
|
|
short_id,
|
|
container.image.unwrap(),
|
|
name
|
|
);*/
|
|
|
|
Some(DockerContainerDto {
|
|
id: short_id.to_string(),
|
|
image,
|
|
name: 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
|
|
Ok(ContainerNetworkInfo {
|
|
container_id: container_id.to_string(),
|
|
rx_bytes: 0,
|
|
tx_bytes: 0,
|
|
rx_packets: 0,
|
|
tx_packets: 0,
|
|
rx_errors: 0,
|
|
tx_errors: 0,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// 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
|
|
Ok(ContainerCpuInfo {
|
|
container_id: container_id.to_string(),
|
|
cpu_usage_percent: 0.0,
|
|
system_cpu_usage: 0,
|
|
container_cpu_usage: 0,
|
|
online_cpus: 1,
|
|
})
|
|
}
|
|
}
|