//! Attune Executor Service //! //! The Executor is the core orchestration engine that: //! - Processes enforcements from triggered rules //! - Schedules executions to workers //! - Manages execution lifecycle //! - Enforces execution policies //! - Orchestrates workflows //! - Handles human-in-the-loop inquiries mod completion_listener; mod dead_letter_handler; mod enforcement_processor; mod event_processor; mod execution_manager; mod inquiry_handler; mod policy_enforcer; mod queue_manager; mod retry_manager; mod scheduler; mod service; mod timeout_monitor; mod worker_health; mod workflow; use anyhow::Result; use attune_common::config::Config; use clap::Parser; use service::ExecutorService; use tracing::{error, info}; #[derive(Parser, Debug)] #[command(name = "attune-executor")] #[command(about = "Attune Executor Service - Execution orchestration and scheduling", long_about = None)] struct Args { /// Path to configuration file #[arg(short, long)] config: Option, /// Log level (trace, debug, info, warn, error) #[arg(short, long, default_value = "info")] log_level: String, } #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); // Initialize tracing with specified log level let log_level = args.log_level.parse().unwrap_or(tracing::Level::INFO); tracing_subscriber::fmt() .with_max_level(log_level) .with_target(false) .with_thread_ids(true) .with_file(true) .with_line_number(true) .init(); info!("Starting Attune Executor Service"); info!("Version: {}", env!("CARGO_PKG_VERSION")); // Load configuration if let Some(config_path) = args.config { info!("Loading configuration from: {}", config_path); std::env::set_var("ATTUNE_CONFIG", config_path); } let config = Config::load()?; config.validate()?; info!("Configuration loaded successfully"); info!("Environment: {}", config.environment); info!("Database: {}", mask_connection_string(&config.database.url)); if let Some(ref mq_config) = config.message_queue { info!("Message Queue: {}", mask_connection_string(&mq_config.url)); } // Create executor service let service = ExecutorService::new(config).await?; info!("Executor Service initialized successfully"); // Set up graceful shutdown handler let service_clone = service.clone(); tokio::spawn(async move { if let Err(e) = tokio::signal::ctrl_c().await { error!("Failed to listen for shutdown signal: {}", e); } else { info!("Shutdown signal received"); if let Err(e) = service_clone.stop().await { error!("Error during shutdown: {}", e); } } }); // Start the service info!("Starting Executor Service components..."); if let Err(e) = service.start().await { error!("Executor Service error: {}", e); return Err(e); } info!("Executor Service has shut down gracefully"); Ok(()) } /// Mask sensitive parts of connection strings for logging fn mask_connection_string(url: &str) -> String { if let Some(at_pos) = url.find('@') { if let Some(proto_end) = url.find("://") { let protocol = &url[..proto_end + 3]; let host_and_path = &url[at_pos..]; return format!("{}***:***{}", protocol, host_and_path); } } "***:***@***".to_string() } #[cfg(test)] mod tests { use super::*; #[test] fn test_mask_connection_string() { let url = "postgresql://user:password@localhost:5432/attune"; let masked = mask_connection_string(url); assert!(!masked.contains("user")); assert!(!masked.contains("password")); assert!(masked.contains("@localhost")); } #[test] fn test_mask_connection_string_no_credentials() { let url = "postgresql://localhost:5432/attune"; let masked = mask_connection_string(url); assert_eq!(masked, "***:***@***"); } }