2026-02-04 01:02:18 +00:00

158 lines
4.6 KiB
Rust

//! Provisioning Platform Daemon
//!
//! A thin wrapper around daemon-cli library with provisioning-specific
//! configuration. All core functionality (Nushell execution, config rendering,
//! i18n) is provided by daemon-cli.
mod config;
use std::path::PathBuf;
use std::sync::Arc;
use axum::Router;
use clap::Parser;
use config::ProvisioningDaemonConfigWrapper;
use daemon_cli::{
api::api_routes,
core::{DaemonConfig, HierarchicalCache, Result},
orchestration::{
AuditOperation, EncryptOperation, InitServsOperation, OperationRegistry, RuntimeOperation,
ValidaOperation,
},
AppState,
};
use tokio::net::TcpListener;
use tracing_subscriber::EnvFilter;
/// Provisioning daemon - Nushell execution and configuration rendering service
#[derive(Parser, Debug)]
#[command(name = "provisioning-daemon")]
#[command(about = "Provisioning platform daemon with Nushell execution and config rendering")]
#[command(version = env!("CARGO_PKG_VERSION"))]
struct Args {
/// Configuration file path (highest priority)
#[arg(short = 'c', long, env = "PROVISIONING_DAEMON_CONFIG")]
config: Option<PathBuf>,
/// Configuration directory (searches for provisioning-daemon.ncl|toml|json)
#[arg(long, env = "PROVISIONING_CONFIG_DIR")]
config_dir: Option<PathBuf>,
/// Deployment mode (solo, multiuser, cicd, enterprise)
#[arg(short = 'm', long, env = "PROVISIONING_DAEMON_MODE")]
mode: Option<String>,
/// Enable verbose logging
#[arg(short = 'v', long)]
verbose: bool,
/// Validate configuration and exit
#[arg(long)]
validate_config: bool,
/// Show configuration and exit
#[arg(long)]
show_config: bool,
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
// Initialize logging
let log_level = if args.verbose { "debug" } else { "info" };
let filter = EnvFilter::new(format!(
"provisioning_daemon={},tower_http=info,daemon_cli=info",
log_level
));
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_target(false)
.init();
tracing::info!(
"Starting Provisioning Daemon v{}",
env!("CARGO_PKG_VERSION")
);
// Load configuration with hierarchical support
let config_wrapper = if let Some(explicit_path) = args.config {
// Use explicit path from CLI argument
ProvisioningDaemonConfigWrapper {
config_path: explicit_path,
}
} else {
// Use hierarchical loading (env vars or defaults)
ProvisioningDaemonConfigWrapper::load_from_hierarchy()
};
tracing::debug!("Loading configuration from: {}", config_wrapper.path_str());
// Load configuration using external daemon library
let config = DaemonConfig::load(Some(config_wrapper.path().clone()))?;
// Handle special commands
if args.validate_config {
config.validate()?;
println!("✓ Configuration is valid");
return Ok(());
}
if args.show_config {
config.print();
return Ok(());
}
// Validate configuration
config.validate()?;
tracing::info!("Configuration validated: bind={}", config.server.bind);
// Create cache system
let cache = HierarchicalCache::new()?;
let cache_arc = Arc::new(cache);
// Create operation registry
let mut registry = OperationRegistry::default();
registry.register("valida", Arc::new(ValidaOperation::new(cache_arc.clone())));
registry.register(
"runtime",
Arc::new(RuntimeOperation::new(cache_arc.clone())),
);
registry.register(
"encrypt",
Arc::new(EncryptOperation::new(cache_arc.clone())),
);
registry.register(
"init-servs",
Arc::new(InitServsOperation::new(cache_arc.clone())),
);
registry.register("audit", Arc::new(AuditOperation::new(cache_arc.clone())));
tracing::info!(
"✓ Registered {} operations: {}",
registry.len(),
registry.list().join(", ")
);
// Create app state
let state = AppState::new(config.clone(), cache_arc, registry);
// Create router
let app = Router::new().nest("/api/v1", api_routes(state.clone()));
// Start server
let addr = config.bind_addr()?;
let listener = TcpListener::bind(addr).await?;
tracing::info!("✓ Provisioning daemon listening on http://{}", addr);
tracing::info!("API documentation: http://{}/api/v1/health", addr);
tracing::info!("Config rendering: http://{}/api/v1/render", addr);
tracing::info!("i18n translation: http://{}/api/v1/translate", addr);
// Run server
axum::serve(listener, app).await?;
Ok(())
}