baobab/cmd/main.rs
2025-08-05 15:44:33 +02:00

187 lines
6.4 KiB
Rust

use std::env;
use std::sync::Arc;
use std::time::Duration;
use hero_supervisor::{SupervisorBuilder, SupervisorError};
use hero_websocket_server::ServerBuilder;
use tokio::signal;
use log::{info, error};
use env_logger::Builder;
/// The main entry point of the Hero Supervisor.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging
env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Info)
.init();
info!("Hero Supervisor starting up...");
// Get config path from command line arguments or use default
let args: Vec<String> = env::args().collect();
let config_path = if let Some(config_index) = args.iter().position(|arg| arg == "--config") {
if config_index + 1 < args.len() {
&args[config_index + 1]
} else {
"cmd/config.toml"
}
} else {
"cmd/config.toml"
};
println!("Loading configuration from: {}", config_path);
let supervisor = SupervisorBuilder::from_toml(config_path)?
.build().await?;
// Wrap supervisor in Arc for sharing across tasks
let supervisor = Arc::new(supervisor);
// Extract actor configurations from TOML config
let actor_configs = supervisor.get_actor_configs()?;
info!("Loaded {} actor configurations from TOML", actor_configs.len());
// Spawn the background lifecycle manager with 5-minute health check interval
let health_check_interval = Duration::from_secs(5 * 60); // 5 minutes
let mut lifecycle_handle = supervisor.clone().spawn_lifecycle_manager(actor_configs, health_check_interval);
info!("Hero Supervisor started successfully!");
info!("Background lifecycle manager is running with 5-minute health checks.");
info!("Actors are being monitored and will be automatically restarted if they fail.");
// Start WebSocket server for job dispatching
info!("Starting WebSocket server for job dispatching...");
let ws_supervisor = supervisor.clone();
// Get WebSocket server config from TOML or use defaults
let ws_config = supervisor.get_websocket_config().unwrap_or_else(|_| {
info!("Using default WebSocket server configuration");
hero_supervisor::WebSocketServerConfig {
host: "127.0.0.1".to_string(),
port: 8443,
redis_url: "redis://127.0.0.1/".to_string(),
auth: false,
tls: false,
cert: None,
key: None,
tls_port: None,
circles: std::collections::HashMap::new(),
}
});
let mut websocket_handle = tokio::spawn(async move {
info!("WebSocket server starting on {}:{}", ws_config.host, ws_config.port);
// Create the WebSocket server with our supervisor
let mut server_builder = ServerBuilder::new()
.host(&ws_config.host)
.port(ws_config.port)
.redis_url(&ws_config.redis_url)
.with_supervisor(ws_supervisor);
// Configure auth if enabled
if ws_config.auth {
server_builder = server_builder.with_auth();
}
// Configure TLS if enabled
if ws_config.tls {
if let (Some(cert), Some(key)) = (&ws_config.cert, &ws_config.key) {
server_builder = server_builder.with_tls(cert.clone(), key.clone());
if let Some(tls_port) = ws_config.tls_port {
server_builder = server_builder.with_tls_port(tls_port);
}
}
}
// Configure circles
if !ws_config.circles.is_empty() {
server_builder = server_builder.circles(ws_config.circles.clone());
}
let server = match server_builder.build() {
Ok(server) => server,
Err(e) => {
error!("Failed to build WebSocket server: {}", e);
return;
}
};
// Start the WebSocket server
match server.spawn_circle_server() {
Ok((server_handle, _)) => {
info!("WebSocket server successfully started and ready to dispatch jobs");
if let Err(e) = server_handle.await {
error!("WebSocket server error: {:?}", e);
}
}
Err(e) => {
error!("Failed to start WebSocket server: {}", e);
}
}
});
info!("WebSocket server started - ready to accept connections and dispatch jobs");
// Set up graceful shutdown signal handlers
let shutdown_signal = async {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
info!("Shutdown signal received, initiating graceful shutdown...");
};
// Wait for shutdown signal or task completion
tokio::select! {
_ = shutdown_signal => {
info!("Graceful shutdown initiated");
// Cancel background tasks
lifecycle_handle.abort();
websocket_handle.abort();
info!("Background tasks stopped");
}
result = &mut lifecycle_handle => {
match result {
Ok(Ok(())) => info!("Lifecycle manager completed successfully"),
Ok(Err(e)) => error!("Lifecycle manager error: {}", e),
Err(e) => error!("Lifecycle manager task panicked: {}", e),
}
// Also stop the websocket handle
websocket_handle.abort();
}
result = &mut websocket_handle => {
match result {
Ok(()) => info!("WebSocket server completed successfully"),
Err(e) => error!("WebSocket server task panicked: {}", e),
}
// Also stop the lifecycle handle
lifecycle_handle.abort();
}
}
info!("Hero Supervisor shutdown complete");
Ok(())
}