re-uploading work
This commit is contained in:
145
crates/core-timer-sensor/src/main.rs
Normal file
145
crates/core-timer-sensor/src/main.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
//! Attune Timer Sensor
|
||||
//!
|
||||
//! A standalone sensor daemon that monitors timer-based triggers and emits events
|
||||
//! to the Attune platform. Each timer sensor instance manages multiple timer schedules
|
||||
//! based on active rules.
|
||||
//!
|
||||
//! Configuration is provided via environment variables or stdin JSON:
|
||||
//! - ATTUNE_API_URL: Base URL of the Attune API
|
||||
//! - ATTUNE_API_TOKEN: Service account token for authentication
|
||||
//! - ATTUNE_SENSOR_REF: Reference name for this sensor (e.g., "core.timer")
|
||||
//! - ATTUNE_MQ_URL: RabbitMQ connection URL
|
||||
//! - ATTUNE_MQ_EXCHANGE: RabbitMQ exchange name (default: "attune")
|
||||
//! - ATTUNE_LOG_LEVEL: Logging verbosity (default: "info")
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use tracing::{error, info};
|
||||
|
||||
mod api_client;
|
||||
mod config;
|
||||
mod rule_listener;
|
||||
mod timer_manager;
|
||||
mod token_refresh;
|
||||
mod types;
|
||||
|
||||
use config::SensorConfig;
|
||||
use rule_listener::RuleLifecycleListener;
|
||||
use timer_manager::TimerManager;
|
||||
use token_refresh::TokenRefreshManager;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "attune-core-timer-sensor")]
|
||||
#[command(about = "Standalone timer sensor for Attune automation platform", long_about = None)]
|
||||
struct Args {
|
||||
/// Log level (trace, debug, info, warn, error)
|
||||
#[arg(short, long, default_value = "info")]
|
||||
log_level: String,
|
||||
|
||||
/// Read configuration from stdin as JSON instead of environment variables
|
||||
#[arg(long)]
|
||||
stdin_config: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
// Initialize tracing
|
||||
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)
|
||||
.json()
|
||||
.init();
|
||||
|
||||
info!("Starting Attune Timer Sensor");
|
||||
info!("Version: {}", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
// Load configuration
|
||||
let config = if args.stdin_config {
|
||||
info!("Reading configuration from stdin");
|
||||
SensorConfig::from_stdin().await?
|
||||
} else {
|
||||
info!("Reading configuration from environment variables");
|
||||
SensorConfig::from_env()?
|
||||
};
|
||||
|
||||
config.validate()?;
|
||||
info!(
|
||||
"Configuration loaded successfully: sensor_ref={}, api_url={}",
|
||||
config.sensor_ref, config.api_url
|
||||
);
|
||||
|
||||
// Create API client
|
||||
let api_client = api_client::ApiClient::new(config.api_url.clone(), config.api_token.clone());
|
||||
|
||||
// Verify API connectivity
|
||||
info!("Verifying API connectivity...");
|
||||
api_client
|
||||
.health_check()
|
||||
.await
|
||||
.context("Failed to connect to Attune API")?;
|
||||
info!("API connectivity verified");
|
||||
|
||||
// Create timer manager
|
||||
let timer_manager = TimerManager::new(api_client.clone())
|
||||
.await
|
||||
.context("Failed to initialize timer manager")?;
|
||||
info!("Timer manager initialized");
|
||||
|
||||
// Create rule lifecycle listener
|
||||
let listener = RuleLifecycleListener::new(
|
||||
config.mq_url.clone(),
|
||||
config.mq_exchange.clone(),
|
||||
config.sensor_ref.clone(),
|
||||
api_client.clone(),
|
||||
timer_manager.clone(),
|
||||
);
|
||||
|
||||
info!("Rule lifecycle listener initialized");
|
||||
|
||||
// Start token refresh manager (auto-refresh when 80% of TTL elapsed)
|
||||
let refresh_manager = TokenRefreshManager::new(api_client.clone(), 0.8);
|
||||
let _refresh_handle = refresh_manager.start();
|
||||
info!("Token refresh manager started (will refresh at 80% of TTL)");
|
||||
|
||||
// Set up graceful shutdown handler
|
||||
let timer_manager_clone = timer_manager.clone();
|
||||
let shutdown_signal = tokio::spawn(async move {
|
||||
match tokio::signal::ctrl_c().await {
|
||||
Ok(()) => {
|
||||
info!("Shutdown signal received");
|
||||
if let Err(e) = timer_manager_clone.shutdown().await {
|
||||
error!("Error during timer manager shutdown: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to listen for shutdown signal: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start the listener (this will block until stopped)
|
||||
info!("Starting rule lifecycle listener...");
|
||||
match listener.start().await {
|
||||
Ok(()) => {
|
||||
info!("Rule lifecycle listener stopped gracefully");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Rule lifecycle listener error: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for shutdown to complete
|
||||
let _ = shutdown_signal.await;
|
||||
|
||||
// Ensure timer manager is fully shutdown
|
||||
timer_manager.shutdown().await?;
|
||||
|
||||
info!("Timer sensor has shut down gracefully");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user