All checks were successful
Rust Cross-Platform Build / Detect Rust Project (push) Successful in 5s
Rust Cross-Platform Build / Set Tag Name (push) Successful in 5s
Rust Cross-Platform Build / Run Tests (push) Successful in 1m8s
Rust Cross-Platform Build / Build (x86_64-unknown-linux-gnu) (push) Successful in 3m5s
Rust Cross-Platform Build / Build (x86_64-pc-windows-gnu) (push) Successful in 3m56s
Rust Cross-Platform Build / Build and Push Docker Image (push) Successful in 2m24s
Rust Cross-Platform Build / Create Tag (push) Successful in 6s
Rust Cross-Platform Build / Workflow Summary (push) Successful in 1s
138 lines
4.7 KiB
Rust
138 lines
4.7 KiB
Rust
|
|
//! Server-client communication utilities for WatcherAgent
|
|
//!
|
|
//! Handles server commands, Docker image updates, and container management using the Bollard library.
|
|
//!
|
|
use crate::models::{DockerContainer, ServerMessage};
|
|
use crate::docker::container::{get_available_container};
|
|
|
|
use std::error::Error;
|
|
use bollard::Docker;
|
|
use bollard::query_parameters::{CreateImageOptions, RestartContainerOptions};
|
|
use futures_util::StreamExt;
|
|
|
|
/// Handles a message from the backend server and dispatches the appropriate action.
|
|
///
|
|
/// # Arguments
|
|
/// * `docker` - Reference to a Bollard Docker client.
|
|
/// * `msg` - The server message to handle.
|
|
///
|
|
/// # Returns
|
|
/// * `Result<(), Box<dyn Error + Send + Sync>>` - Ok if handled successfully, error otherwise.
|
|
pub async fn handle_server_message(docker: &Docker, msg: ServerMessage) -> Result<(), Box<dyn Error + Send + Sync>> {
|
|
let msg = msg.clone();
|
|
println!("Handling server message: {:?}", msg);
|
|
|
|
// Handle different message types
|
|
match msg.message_type.as_str() {
|
|
"update_image" => {
|
|
if let Some(image_name) = msg.data.get("image").and_then(|v| v.as_str()) {
|
|
println!("Received update command for image: {}", image_name);
|
|
// Call your update_docker_image function here
|
|
update_docker_image(docker, image_name).await?;
|
|
Ok(())
|
|
} else {
|
|
Err("Missing image name in update message".into())
|
|
}
|
|
}
|
|
"restart_container" => {
|
|
println!("Received restart container command");
|
|
// Call your restart_container function here
|
|
restart_container(docker).await?;
|
|
Ok(())
|
|
}
|
|
"stop_agent" => {
|
|
println!("Received stop agent command");
|
|
// Implement graceful shutdown
|
|
std::process::exit(0);
|
|
}
|
|
_ => {
|
|
eprintln!("Unknown message type: {}", msg.message_type);
|
|
Err(format!("Unknown message type: {}", msg.message_type).into())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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 _ = restart_container(docker).await;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Finds the Docker container running the agent by image name.
|
|
///
|
|
/// # Arguments
|
|
/// * `docker` - Reference to a Bollard Docker client.
|
|
///
|
|
/// # Returns
|
|
/// * `Result<Option<DockerContainer>, Box<dyn Error + Send + Sync>>` - The agent's container info if found.
|
|
pub async fn get_client_container(docker: &Docker) -> Result<Option<DockerContainer>, Box<dyn Error + Send + Sync>> {
|
|
let containers = get_available_container(docker).await;
|
|
let client_image = "watcher-agent";
|
|
|
|
// Find container with the specific image
|
|
if let Some(container) = containers.iter().find(|c| c.image == client_image) {
|
|
Ok(Some(container.clone()))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
/// 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) -> Result<(), Box<dyn Error + Send + Sync>> {
|
|
if let Ok(Some(container)) = get_client_container(docker).await {
|
|
let container_id = container.clone().ID;
|
|
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);
|
|
}
|
|
} else {
|
|
eprintln!("No container ID found (HOSTNAME not set?)");
|
|
}
|
|
|
|
Ok(())
|
|
} |