Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
1f23c303c1 | |||
1cc85bfa14 | |||
8bac357dc6 | |||
7154c01f7a | |||
813bf4e407 | |||
bf6f89c954 | |||
dbe87fedb6 | |||
67b24b33aa | |||
67ebbdaa19 |
@@ -19,6 +19,8 @@ nvml-wrapper = "0.11"
|
||||
nvml-wrapper-sys = "0.9.0"
|
||||
anyhow = "1.0.98"
|
||||
|
||||
regex = "1.11.3"
|
||||
|
||||
# Docker .env loading
|
||||
# config = "0.13"
|
||||
|
||||
|
@@ -1,16 +1,15 @@
|
||||
use crate::serverclientcomm::handle_server_message;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::hardware::HardwareInfo;
|
||||
use crate::models::{HeartbeatDto, IdResponse, MetricDto, RegistrationDto};
|
||||
use crate::models::{HeartbeatDto, IdResponse, MetricDto, RegistrationDto, ServerMessage, Acknowledgment};
|
||||
use crate::serverclientcomm::handle_server_message;
|
||||
|
||||
use anyhow::Result;
|
||||
use reqwest::{Client, StatusCode};
|
||||
use std::error::Error;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use bollard::Docker;
|
||||
use crate::models::ServerMessage;
|
||||
|
||||
pub async fn register_with_server(
|
||||
base_url: &str,
|
||||
@@ -159,24 +158,84 @@ pub async fn send_metrics(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn listening_to_server(docker: &Docker, base_url: &str) -> Result<(), Box<dyn Error + Send + Sync>>{
|
||||
pub async fn listening_to_server(docker: &Docker, base_url: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let url = format!("{}/api/message", base_url);
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
loop {
|
||||
// Replace with your server endpoint
|
||||
let resp = reqwest::get(&url)
|
||||
.await;
|
||||
// Get message from server
|
||||
let resp = client.get(&url).send().await;
|
||||
|
||||
if let Ok(resp) = resp {
|
||||
if let Ok(msg) = resp.json::<ServerMessage>().await {
|
||||
handle_server_message(docker, msg).await;
|
||||
} else {
|
||||
eprintln!("Failed to parse message");
|
||||
match resp {
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
match response.json::<ServerMessage>().await {
|
||||
Ok(msg) => {
|
||||
// Acknowledge receipt immediately
|
||||
if let Err(e) = send_acknowledgment(&client, base_url, &msg.message_id, "received", "Message received successfully").await {
|
||||
eprintln!("Failed to send receipt acknowledgment: {}", e);
|
||||
}
|
||||
|
||||
// Handle the message
|
||||
let result = handle_server_message(docker, msg.clone()).await;
|
||||
|
||||
// Send execution result acknowledgment
|
||||
let (status, details) = match result {
|
||||
Ok(_) => ("success", "Message executed successfully".to_string()),
|
||||
Err(e) => ("error", format!("Execution failed: {}", e)),
|
||||
};
|
||||
|
||||
if let Err(e) = send_acknowledgment(&client, base_url, &msg.message_id, status, &details).await {
|
||||
eprintln!("Failed to send execution acknowledgment: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to parse message: {}", e);
|
||||
}
|
||||
}
|
||||
} else if response.status() == reqwest::StatusCode::NO_CONTENT {
|
||||
// No new messages, continue polling
|
||||
println!("No new messages from server");
|
||||
} else {
|
||||
eprintln!("Server returned error status: {}", response.status());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to reach server: {}", e);
|
||||
}
|
||||
} else {
|
||||
eprintln!("Failed to reach server");
|
||||
}
|
||||
|
||||
// Poll every 5 seconds (or use WebSocket for real-time)
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_acknowledgment(
|
||||
client: &reqwest::Client,
|
||||
base_url: &str,
|
||||
message_id: &str,
|
||||
status: &str,
|
||||
details: &str,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let ack_url = format!("{}/api/acknowledge", base_url);
|
||||
|
||||
let acknowledgment = Acknowledgment {
|
||||
message_id: message_id.to_string(),
|
||||
status: status.to_string(),
|
||||
details: details.to_string(),
|
||||
};
|
||||
|
||||
let response = client
|
||||
.post(&ack_url)
|
||||
.json(&acknowledgment)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
println!("Acknowledgment sent successfully for message {}", message_id);
|
||||
} else {
|
||||
eprintln!("Server returned error for acknowledgment: {}", response.status());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@@ -1,130 +1,138 @@
|
||||
use std::error::Error;
|
||||
use crate::models::DiskInfoDetailed;
|
||||
|
||||
use std::error::Error;
|
||||
use anyhow::Result;
|
||||
use sysinfo::DiskUsage;
|
||||
use sysinfo::{Component, Components, Disk, Disks, System};
|
||||
use sysinfo::{Component, Components, Disk, Disks};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct DiskInfo {
|
||||
pub total: Option<f64>,
|
||||
pub used: Option<f64>,
|
||||
pub free: Option<f64>,
|
||||
pub total_size: Option<f64>,
|
||||
pub total_used: Option<f64>,
|
||||
pub total_available: Option<f64>,
|
||||
pub total_usage: Option<f64>,
|
||||
pub detailed_info: Vec<DiskInfoDetailed>,
|
||||
}
|
||||
|
||||
pub async fn get_disk_info() -> Result<DiskInfo> {
|
||||
pub async fn get_disk_info() -> Result<DiskInfo, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
let _disk_types = [
|
||||
sysinfo::DiskKind::HDD,
|
||||
sysinfo::DiskKind::SSD,
|
||||
sysinfo::DiskKind::Unknown(0),
|
||||
];
|
||||
|
||||
let (_, _, _, _) = get_disk_utitlization().unwrap();
|
||||
|
||||
let mut total = 0;
|
||||
let mut used = 0;
|
||||
|
||||
let mut detailed_info = Vec::new();
|
||||
|
||||
// Collect detailed disk information
|
||||
for disk in disks.list() {
|
||||
if disk.total_space() > 100 * 1024 * 1024 {
|
||||
// > 100MB
|
||||
total += disk.total_space();
|
||||
used += disk.total_space() - disk.available_space();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DiskInfo {
|
||||
total: Some(total as f64),
|
||||
used: Some(used as f64),
|
||||
free: Some((total - used) as f64),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_disk_utitlization() -> Result<(f64, f64, f64, f64), Box<dyn Error>> {
|
||||
let mut sys = System::new();
|
||||
sys.refresh_all();
|
||||
let mut count = 0;
|
||||
|
||||
let mut total_size = 0u64;
|
||||
let mut total_used = 0u64;
|
||||
let mut total_available = 0u64;
|
||||
|
||||
let disks = Disks::new_with_refreshed_list();
|
||||
for disk in disks.list() {
|
||||
// Only print disks with known kind
|
||||
if disk.kind() == sysinfo::DiskKind::Unknown(0) {
|
||||
continue;
|
||||
}
|
||||
println!(
|
||||
"Disk_Name: {:?}:\n---- Disk_Kind: {},\n---- Total: {},\n---- Available: {},\n---- Used: {}, \n---- Mount_Point: {:?}",
|
||||
disk.name(),
|
||||
disk.kind(),
|
||||
disk.total_space(),
|
||||
disk.available_space(),
|
||||
disk.total_space() - disk.available_space(),
|
||||
disk.mount_point()
|
||||
);
|
||||
|
||||
let disk_used = disk.total_space() - disk.available_space();
|
||||
detailed_info.push(DiskInfoDetailed {
|
||||
disk_name: disk.name().to_string_lossy().into_owned(),
|
||||
disk_kind: format!("{:?}", disk.kind()),
|
||||
disk_total_space: disk.total_space() as f64,
|
||||
disk_available_space: disk.available_space() as f64,
|
||||
disk_used_space: disk_used as f64,
|
||||
disk_mount_point: disk.mount_point().to_string_lossy().into_owned(),
|
||||
component_disk_label: String::new(),
|
||||
component_disk_temperature: 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
// Get component temperatures
|
||||
let components = Components::new_with_refreshed_list();
|
||||
for component in &components {
|
||||
if let Some(temperature) = component.temperature() {
|
||||
println!(
|
||||
"Component_Label: {}, Temperature: {}°C",
|
||||
component.label(),
|
||||
temperature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Berechnungen
|
||||
let total_size = if count > 0 {
|
||||
total_size as f64 // in Bytes
|
||||
} else {
|
||||
// Fallback: Versuche df unter Linux
|
||||
println!("Fallback: Using 'df' command to get disk info.");
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use std::process::Command;
|
||||
if let Ok(output) = Command::new("df")
|
||||
.arg("-B1")
|
||||
.arg("--output=size,used")
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines().skip(1) {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() == 2 {
|
||||
if let (Ok(size), Ok(used)) =
|
||||
(parts[0].parse::<u64>(), parts[1].parse::<u64>())
|
||||
{
|
||||
total_size += size;
|
||||
total_used += used;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
// Update detailed info with temperature data if it matches a disk component
|
||||
for disk_info in &mut detailed_info {
|
||||
if component.label().contains(&disk_info.disk_name) {
|
||||
disk_info.component_disk_label = component.label().to_string();
|
||||
disk_info.component_disk_temperature = temperature;
|
||||
}
|
||||
total_size as f64 // in Bytes
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
0.0
|
||||
}
|
||||
|
||||
// Calculate totals (only disks > 100MB)
|
||||
let (total_size, total_used, total_available) = calculate_disk_totals(&disks);
|
||||
|
||||
let (total_size, total_used, total_available, total_usage) = if total_size > 0.0 {
|
||||
(total_size, total_used, total_available, (total_used / total_size) * 100.0)
|
||||
} else {
|
||||
match get_disk_info_fallback() {
|
||||
Ok(fallback_data) => fallback_data,
|
||||
Err(_) => (0.0, 0.0, 0.0, 0.0), // Default values if fallback fails
|
||||
}
|
||||
};
|
||||
|
||||
Ok(DiskInfo {
|
||||
total_size: if total_size > 0.0 { Some(total_size) } else { None },
|
||||
total_used: if total_used > 0.0 { Some(total_used) } else { None },
|
||||
total_available: if total_available > 0.0 { Some(total_available) } else { None },
|
||||
total_usage: if total_usage > 0.0 { Some(total_usage) } else { None },
|
||||
detailed_info,
|
||||
})
|
||||
}
|
||||
|
||||
let usage = if total_size > 0.0 {
|
||||
fn calculate_disk_totals(disks: &Disks) -> (f64, f64, f64) {
|
||||
let mut total_size = 0u64;
|
||||
let mut total_used = 0u64;
|
||||
let mut total_available = 0u64;
|
||||
|
||||
for disk in disks.list() {
|
||||
if disk.total_space() > 100 * 1024 * 1024 { // > 100MB
|
||||
total_size += disk.total_space();
|
||||
total_available += disk.available_space();
|
||||
total_used += disk.total_space() - disk.available_space();
|
||||
}
|
||||
}
|
||||
|
||||
(total_size as f64, total_used as f64, total_available as f64)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn get_disk_info_fallback() -> Result<(f64, f64, f64, f64), Box<dyn Error + Send + Sync>> {
|
||||
use std::process::Command;
|
||||
|
||||
let output = Command::new("df")
|
||||
.arg("-B1")
|
||||
.arg("--output=size,used,avail")
|
||||
.output()?;
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let mut total_size = 0u64;
|
||||
let mut total_used = 0u64;
|
||||
let mut total_available = 0u64;
|
||||
let mut count = 0;
|
||||
|
||||
for line in stdout.lines().skip(1) {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 3 {
|
||||
if let (Ok(size), Ok(used), Ok(avail)) = (
|
||||
parts[0].parse::<u64>(),
|
||||
parts[1].parse::<u64>(),
|
||||
parts[2].parse::<u64>(),
|
||||
) {
|
||||
total_size += size;
|
||||
total_used += used;
|
||||
total_available += avail;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let usage = if total_size > 0 {
|
||||
(total_used as f64 / total_size as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
Ok((total_size as f64, total_used as f64, total_available as f64, usage))
|
||||
}
|
||||
|
||||
Ok((
|
||||
total_size,
|
||||
total_used as f64,
|
||||
total_available as f64,
|
||||
usage as f64,
|
||||
)) // Disk-Temp bleibt 0.0 ohne spezielle Hardware
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn get_disk_info_fallback() -> Result<(f64, f64, f64, f64), Box<dyn Error + Send + Sync>> {
|
||||
Ok((0.0, 0.0, 0.0, 0.0))
|
||||
}
|
||||
|
||||
pub fn _get_disk_temp_for_component(component: &Component) -> Option<f64> {
|
||||
|
@@ -13,6 +13,9 @@ use std::marker::Send;
|
||||
use std::marker::Sync;
|
||||
use std::result::Result;
|
||||
use tokio::task::JoinHandle;
|
||||
use bollard::Docker;
|
||||
|
||||
use crate::serverclientcomm::{get_current_image};
|
||||
|
||||
async fn flatten<T>(
|
||||
handle: JoinHandle<Result<T, Box<dyn Error + Send + Sync>>>,
|
||||
@@ -26,14 +29,30 @@ async fn flatten<T>(
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
// Initialize Docker client
|
||||
let docker = Docker::connect_with_local_defaults()
|
||||
.map_err(|e| format!("Failed to connect to Docker: {}", e))?;
|
||||
|
||||
// Get current image version
|
||||
let client_version = match get_current_image(&docker).await {
|
||||
Ok(Some(version)) => version,
|
||||
Ok(None) => {
|
||||
eprintln!("Warning: No image version found");
|
||||
"unknown".to_string()
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Warning: Could not get current image version: {}", e);
|
||||
"unknown".to_string()
|
||||
}
|
||||
};
|
||||
println!("Client Version: {}", client_version);
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
// args[0] is the binary name, args[1] is the first actual argument
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: {} <server-url>", args[0]);
|
||||
return Err("Missing server URL argument".into());
|
||||
}
|
||||
|
||||
let server_url = &args[1];
|
||||
println!("Server URL: {:?}", server_url);
|
||||
|
||||
@@ -47,6 +66,13 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
};
|
||||
|
||||
// Start background tasks
|
||||
// Start server listening for commands
|
||||
let listening_handle = tokio::spawn({
|
||||
let docker = docker.clone();
|
||||
let server_url = server_url.to_string();
|
||||
async move { api::listening_to_server(&docker, &server_url).await }
|
||||
});
|
||||
|
||||
// Start heartbeat in background
|
||||
let heartbeat_handle = tokio::spawn({
|
||||
let ip = ip.clone();
|
||||
@@ -65,14 +91,16 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
}
|
||||
});
|
||||
|
||||
// Warte auf beide Tasks und prüfe explizit auf Fehler
|
||||
let (heartbeat_handle, metrics_handle) =
|
||||
tokio::try_join!(flatten(heartbeat_handle), flatten(metrics_handle))?;
|
||||
// Wait for all tasks and check for errors
|
||||
let (listening_result, heartbeat_result, metrics_result) = tokio::try_join!(
|
||||
flatten(listening_handle),
|
||||
flatten(heartbeat_handle),
|
||||
flatten(metrics_handle)
|
||||
)?;
|
||||
|
||||
let (heartbeat, metrics) = (heartbeat_handle, metrics_handle);
|
||||
println!(
|
||||
"All tasks completed successfully: {:?}, {:?}.",
|
||||
heartbeat, metrics
|
||||
"All tasks completed: listening={:?}, heartbeat={:?}, metrics={:?}",
|
||||
listening_result, heartbeat_result, metrics_result
|
||||
);
|
||||
|
||||
println!("All tasks completed successfully.");
|
||||
|
@@ -62,8 +62,8 @@ impl Collector {
|
||||
gpu_vram_usage: hardware.gpu.vram_used.unwrap_or_default(),
|
||||
ram_load: hardware.memory.used.unwrap_or_default(),
|
||||
ram_size: hardware.memory.total.unwrap_or_default(),
|
||||
disk_size: hardware.disk.total.unwrap_or_default(),
|
||||
disk_usage: hardware.disk.used.unwrap_or_default(),
|
||||
disk_size: hardware.disk.total_size.unwrap_or_default(),
|
||||
disk_usage: hardware.disk.total_used.unwrap_or_default(),
|
||||
disk_temp: 0.0, // not supported
|
||||
net_rx: hardware.network.rx_rate.unwrap_or_default(),
|
||||
net_tx: hardware.network.tx_rate.unwrap_or_default(),
|
||||
|
@@ -51,6 +51,18 @@ pub struct MetricDto {
|
||||
pub net_tx: f64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct DiskInfoDetailed {
|
||||
pub disk_name: String,
|
||||
pub disk_kind: String,
|
||||
pub disk_total_space: f64,
|
||||
pub disk_available_space: f64,
|
||||
pub disk_used_space: f64,
|
||||
pub disk_mount_point: String,
|
||||
pub component_disk_label: String,
|
||||
pub component_disk_temperature: f32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IdResponse {
|
||||
pub id: i32,
|
||||
@@ -73,11 +85,17 @@ pub struct HardwareDto {
|
||||
pub ip_address: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(tag = "command", content = "data")]
|
||||
pub enum ServerMessage {
|
||||
Update(String),
|
||||
Restart,
|
||||
#[serde(other)]
|
||||
Unknown,
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct ServerMessage {
|
||||
// Define your message structure here
|
||||
pub message_type: String,
|
||||
pub data: serde_json::Value,
|
||||
pub message_id: String, // Add an ID for acknowledgment
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
pub struct Acknowledgment {
|
||||
pub message_id: String,
|
||||
pub status: String, // "success" or "error"
|
||||
pub details: String,
|
||||
}
|
@@ -1,27 +1,45 @@
|
||||
use crate::models::ServerMessage;
|
||||
use crate::models::{ServerMessage};
|
||||
|
||||
use std::error::Error;
|
||||
use bollard::Docker;
|
||||
use bollard::query_parameters::CreateImageOptions;
|
||||
use bollard::query_parameters::RestartContainerOptions;
|
||||
use bollard::query_parameters::{CreateImageOptions, RestartContainerOptions, InspectContainerOptions, ListContainersOptions};
|
||||
use futures_util::StreamExt;
|
||||
|
||||
pub fn parse_message(raw: &str) -> ServerMessage {
|
||||
match raw {
|
||||
"restart" => ServerMessage::Restart,
|
||||
msg if msg.starts_with("update:") => ServerMessage::Update(msg[7..].to_string()),
|
||||
_ => ServerMessage::Unknown,
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_server_message(docker: &Docker, msg: ServerMessage) {
|
||||
match msg {
|
||||
ServerMessage::Update(version) => update_docker_image(docker, &version).await,
|
||||
ServerMessage::Restart => restart_container(docker).await,
|
||||
ServerMessage::Unknown => eprintln!("Unknown message"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_docker_image(docker: &Docker, image: &str) {
|
||||
pub async fn update_docker_image(docker: &Docker, image: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
println!("Updating to {}", image);
|
||||
|
||||
// 1. Pull new image
|
||||
@@ -50,10 +68,148 @@ pub async fn update_docker_image(docker: &Docker, image: &str) {
|
||||
}
|
||||
|
||||
// 2. Restart the current container
|
||||
restart_container(docker).await;
|
||||
let _ = restart_container(docker).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn restart_container(docker: &Docker) {
|
||||
pub async fn get_current_image(docker: &Docker) -> Result<Option<String>, Box<dyn Error + Send + Sync>> {
|
||||
// First, let's debug the environment
|
||||
debug_docker_environment(docker).await;
|
||||
|
||||
// Get the current container ID from /proc/self/cgroup
|
||||
let container_id = match std::fs::read_to_string("/proc/self/cgroup") {
|
||||
Ok(content) => {
|
||||
let mut found_id = None;
|
||||
println!("Searching cgroup for container ID...");
|
||||
|
||||
for line in content.lines() {
|
||||
println!("Checking line: {}", line);
|
||||
|
||||
// Look for container runtime indicators
|
||||
if line.contains("docker") || line.contains("crio") || line.contains("containerd") {
|
||||
if let Some(id) = extract_container_id(line) {
|
||||
println!("Found potential container ID: {}", id);
|
||||
found_id = Some(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
found_id
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error reading cgroup file: {}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let container_id = match container_id {
|
||||
Some(id) if !id.is_empty() => {
|
||||
println!("Using container ID: '{}'", id);
|
||||
id
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Could not find valid container ID in cgroup");
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
// Try to inspect the container
|
||||
println!("Attempting to inspect container with ID: '{}'", container_id);
|
||||
|
||||
match docker.inspect_container(&container_id, None::<InspectContainerOptions>).await {
|
||||
Ok(container_info) => {
|
||||
if let Some(config) = container_info.config {
|
||||
if let Some(image) = config.image {
|
||||
println!("Successfully found image: {}", image);
|
||||
return Ok(Some(image));
|
||||
}
|
||||
}
|
||||
eprintln!("Container inspected but no image found in config");
|
||||
Ok(Some("unknown".to_string()))
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error inspecting container: {}", e);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_container_id(line: &str) -> Option<String> {
|
||||
// Split by slashes and take the last part
|
||||
if let Some(last_part) = line.split('/').last() {
|
||||
let last_part = last_part.trim();
|
||||
|
||||
// Remove common suffixes
|
||||
let clean_id = last_part
|
||||
.trim_end_matches(".scope")
|
||||
.trim_start_matches("docker-")
|
||||
.trim_start_matches("crio-")
|
||||
.trim_start_matches("containerd-");
|
||||
|
||||
// Check if it looks like a container ID (hex characters)
|
||||
if clean_id.chars().all(|c| c.is_ascii_hexdigit()) && clean_id.len() >= 12 {
|
||||
return Some(clean_id.to_string());
|
||||
}
|
||||
|
||||
// If it's not pure hex, try to extract hex sequence
|
||||
let hex_part: String = clean_id.chars()
|
||||
.take_while(|c| c.is_ascii_hexdigit())
|
||||
.collect();
|
||||
|
||||
if hex_part.len() >= 12 {
|
||||
return Some(hex_part);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Add this function to debug the Docker connection and environment
|
||||
async fn debug_docker_environment(docker: &Docker) {
|
||||
println!("=== DOCKER ENVIRONMENT DEBUG ===");
|
||||
|
||||
// List containers to see what's available - CORRECTED
|
||||
let options = Some(ListContainersOptions {
|
||||
all: true, // include stopped containers
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
match docker.list_containers(options).await {
|
||||
Ok(containers) => {
|
||||
println!("Available containers ({}):", containers.len());
|
||||
for container in containers {
|
||||
if let Some(id) = container.id {
|
||||
let short_id = if id.len() > 12 { &id[..12] } else { &id };
|
||||
println!(" - ID: {}, Image: {:?}", short_id, container.image);
|
||||
|
||||
// Also print the names for easier identification
|
||||
if let Some(names) = container.names {
|
||||
println!(" Names: {:?}", names);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to list containers: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're actually running in a container
|
||||
if let Ok(content) = std::fs::read_to_string("/proc/self/cgroup") {
|
||||
println!("Cgroup contents:");
|
||||
println!("{}", content);
|
||||
}
|
||||
|
||||
// Check other container indicators
|
||||
if std::path::Path::new("/.dockerenv").exists() {
|
||||
println!("/.dockerenv exists - running in Docker container");
|
||||
} else {
|
||||
println!("/.dockerenv does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn restart_container(docker: &Docker) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
if let Ok(container_id) = std::env::var("HOSTNAME") {
|
||||
println!("Restarting container {}", container_id);
|
||||
if let Err(e) = docker.restart_container(&container_id, Some(RestartContainerOptions { signal: None, t: Some(0) }))
|
||||
@@ -64,4 +220,6 @@ pub async fn restart_container(docker: &Docker) {
|
||||
} else {
|
||||
eprintln!("No container ID found (HOSTNAME not set?)");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user