174 lines
5.5 KiB
Rust
174 lines
5.5 KiB
Rust
//! # Hero Supervisor Application
|
|
//!
|
|
//! Simplified supervisor application that wraps a built Supervisor instance.
|
|
//! Use SupervisorBuilder to construct the supervisor with all configuration,
|
|
//! then pass it to SupervisorApp for runtime management.
|
|
|
|
use crate::Supervisor;
|
|
use crate::mycelium::MyceliumIntegration;
|
|
use log::{info, error, debug};
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex;
|
|
|
|
/// Main supervisor application
|
|
pub struct SupervisorApp {
|
|
pub supervisor: Supervisor,
|
|
pub mycelium_url: String,
|
|
pub topic: String,
|
|
}
|
|
|
|
impl SupervisorApp {
|
|
/// Create a new supervisor application with a built supervisor
|
|
pub fn new(supervisor: Supervisor, mycelium_url: String, topic: String) -> Self {
|
|
Self {
|
|
supervisor,
|
|
mycelium_url,
|
|
topic,
|
|
}
|
|
}
|
|
|
|
/// Start the complete supervisor application
|
|
/// This method handles the entire application lifecycle:
|
|
/// - Starts all configured runners
|
|
/// - Connects to Mycelium daemon for message transport
|
|
/// - Sets up graceful shutdown handling
|
|
/// - Keeps the application running
|
|
pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
|
info!("Starting Hero Supervisor Application");
|
|
|
|
// Start all configured runners
|
|
self.start_all().await?;
|
|
|
|
// Start Mycelium integration
|
|
self.start_mycelium_integration().await?;
|
|
|
|
// Set up graceful shutdown
|
|
self.setup_graceful_shutdown().await;
|
|
|
|
// Keep the application running
|
|
info!("Supervisor is running. Press Ctrl+C to shutdown.");
|
|
self.run_main_loop().await;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Start the Mycelium integration
|
|
async fn start_mycelium_integration(&self) -> Result<(), Box<dyn std::error::Error>> {
|
|
info!("Starting Mycelium integration...");
|
|
|
|
let supervisor_for_mycelium = Arc::new(Mutex::new(self.supervisor.clone()));
|
|
let mycelium_url = self.mycelium_url.clone();
|
|
let topic = self.topic.clone();
|
|
|
|
let mycelium_integration = MyceliumIntegration::new(
|
|
supervisor_for_mycelium,
|
|
mycelium_url,
|
|
topic,
|
|
);
|
|
|
|
// Start the Mycelium integration in a background task
|
|
let integration_handle = tokio::spawn(async move {
|
|
if let Err(e) = mycelium_integration.start().await {
|
|
error!("Mycelium integration error: {}", e);
|
|
}
|
|
});
|
|
|
|
// Give the integration a moment to start
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
|
info!("Mycelium integration started successfully");
|
|
|
|
// Store the handle for potential cleanup
|
|
std::mem::forget(integration_handle); // For now, let it run in background
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Set up graceful shutdown handling
|
|
async fn setup_graceful_shutdown(&self) {
|
|
tokio::spawn(async move {
|
|
tokio::signal::ctrl_c().await.expect("Failed to listen for ctrl+c");
|
|
info!("Received shutdown signal");
|
|
std::process::exit(0);
|
|
});
|
|
}
|
|
|
|
/// Main application loop
|
|
async fn run_main_loop(&self) {
|
|
// Keep the main thread alive
|
|
loop {
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
|
|
/// Start all configured runners
|
|
pub async fn start_all(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
|
info!("Starting all runners");
|
|
|
|
let results = self.supervisor.start_all().await;
|
|
let mut failed_count = 0;
|
|
|
|
for (runner_id, result) in results {
|
|
match result {
|
|
Ok(_) => info!("Runner {} started successfully", runner_id),
|
|
Err(e) => {
|
|
error!("Failed to start runner {}: {}", runner_id, e);
|
|
failed_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if failed_count == 0 {
|
|
info!("All runners started successfully");
|
|
} else {
|
|
error!("Failed to start {} runners", failed_count);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Stop all configured runners
|
|
pub async fn stop_all(&mut self, force: bool) -> Result<(), Box<dyn std::error::Error>> {
|
|
info!("Stopping all runners (force: {})", force);
|
|
|
|
let results = self.supervisor.stop_all(force).await;
|
|
let mut failed_count = 0;
|
|
|
|
for (runner_id, result) in results {
|
|
match result {
|
|
Ok(_) => info!("Runner {} stopped successfully", runner_id),
|
|
Err(e) => {
|
|
error!("Failed to stop runner {}: {}", runner_id, e);
|
|
failed_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if failed_count == 0 {
|
|
info!("All runners stopped successfully");
|
|
} else {
|
|
error!("Failed to stop {} runners", failed_count);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
|
|
/// Get status of all runners
|
|
pub async fn get_status(&self) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> {
|
|
debug!("Getting status of all runners");
|
|
|
|
let statuses = self.supervisor.get_all_runner_status().await
|
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
|
|
|
|
let status_strings: Vec<(String, String)> = statuses
|
|
.into_iter()
|
|
.map(|(runner_id, status)| {
|
|
let status_str = format!("{:?}", status);
|
|
(runner_id, status_str)
|
|
})
|
|
.collect();
|
|
|
|
Ok(status_strings)
|
|
}
|
|
}
|