FEAT: Complete OCI integration with container image generation capabilities - Add comprehensive OCI module (src/oci.rs) with full specification compliance - Implement OciImageBuilder for OSTree commit to container image conversion - Add OciRegistry for push/pull operations with authentication support - Create OciUtils for image validation, inspection, and format conversion - Support both OCI and Docker image formats with proper content addressing - Add SHA256 digest calculation for all image components - Implement gzip compression for filesystem layers CLI: Add complete OCI command suite - apt-ostree oci build - Build OCI images from OSTree commits - apt-ostree oci push - Push images to container registries - apt-ostree oci pull - Pull images from registries - apt-ostree oci inspect - Inspect image information - apt-ostree oci validate - Validate image integrity - apt-ostree oci convert - Convert between image formats COMPOSE: Enhance compose workflow with OCI integration - apt-ostree compose build-image - Convert deployments to OCI images - apt-ostree compose container-encapsulate - Generate container images from commits - apt-ostree compose image - Generate container images from treefiles ARCH: Add OCI layer to project architecture - Integrate OCI manager into lib.rs and main.rs - Add proper error handling and recovery mechanisms - Include comprehensive testing and validation - Create test script for OCI functionality validation DEPS: Add sha256 crate for content addressing - Update Cargo.toml with sha256 dependency - Ensure proper async/await handling with tokio::process::Command - Fix borrow checker issues and lifetime management DOCS: Update project documentation - Add OCI integration summary documentation - Update todo.md with milestone 9 completion - Include usage examples and workflow documentation
341 lines
No EOL
12 KiB
Rust
341 lines
No EOL
12 KiB
Rust
//! APT-OSTree Monitoring Service
|
|
//!
|
|
//! This service runs in the background to collect metrics, perform health checks,
|
|
//! and provide monitoring capabilities for the APT-OSTree system.
|
|
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::time::interval;
|
|
use tracing::{info, warn, error, debug};
|
|
use serde_json;
|
|
|
|
use apt_ostree::monitoring::{MonitoringManager, MonitoringConfig};
|
|
use apt_ostree::error::AptOstreeResult;
|
|
|
|
/// Monitoring service configuration
|
|
#[derive(Debug, Clone)]
|
|
struct MonitoringServiceConfig {
|
|
/// Metrics collection interval in seconds
|
|
pub metrics_interval: u64,
|
|
/// Health check interval in seconds
|
|
pub health_check_interval: u64,
|
|
/// Export metrics to file
|
|
pub export_metrics: bool,
|
|
/// Metrics export file path
|
|
pub metrics_file: String,
|
|
/// Enable system resource monitoring
|
|
pub enable_system_monitoring: bool,
|
|
/// Enable performance monitoring
|
|
pub enable_performance_monitoring: bool,
|
|
/// Enable transaction monitoring
|
|
pub enable_transaction_monitoring: bool,
|
|
}
|
|
|
|
impl Default for MonitoringServiceConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
metrics_interval: 60,
|
|
health_check_interval: 300,
|
|
export_metrics: true,
|
|
metrics_file: "/var/log/apt-ostree/metrics.json".to_string(),
|
|
enable_system_monitoring: true,
|
|
enable_performance_monitoring: true,
|
|
enable_transaction_monitoring: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Monitoring service
|
|
struct MonitoringService {
|
|
config: MonitoringServiceConfig,
|
|
monitoring_manager: Arc<MonitoringManager>,
|
|
running: bool,
|
|
}
|
|
|
|
impl MonitoringService {
|
|
/// Create a new monitoring service
|
|
fn new(config: MonitoringServiceConfig) -> AptOstreeResult<Self> {
|
|
info!("Creating monitoring service with config: {:?}", config);
|
|
|
|
let monitoring_config = MonitoringConfig {
|
|
log_level: "info".to_string(),
|
|
log_file: None,
|
|
structured_logging: true,
|
|
enable_metrics: true,
|
|
metrics_interval: config.metrics_interval,
|
|
enable_health_checks: true,
|
|
health_check_interval: config.health_check_interval,
|
|
enable_performance_monitoring: config.enable_performance_monitoring,
|
|
enable_transaction_monitoring: config.enable_transaction_monitoring,
|
|
enable_system_monitoring: config.enable_system_monitoring,
|
|
};
|
|
|
|
let monitoring_manager = Arc::new(MonitoringManager::new(monitoring_config)?);
|
|
monitoring_manager.init_logging()?;
|
|
|
|
Ok(Self {
|
|
config,
|
|
monitoring_manager,
|
|
running: false,
|
|
})
|
|
}
|
|
|
|
/// Start the monitoring service
|
|
async fn start(&mut self) -> AptOstreeResult<()> {
|
|
info!("Starting monitoring service");
|
|
|
|
self.running = true;
|
|
|
|
// Start metrics collection task
|
|
let metrics_manager = self.monitoring_manager.clone();
|
|
let metrics_interval = self.config.metrics_interval;
|
|
let export_metrics = self.config.export_metrics;
|
|
let metrics_file = self.config.metrics_file.clone();
|
|
|
|
tokio::spawn(async move {
|
|
let mut interval = interval(Duration::from_secs(metrics_interval));
|
|
|
|
while let Some(_) = interval.tick().await {
|
|
debug!("Collecting system metrics");
|
|
|
|
if let Err(e) = metrics_manager.record_system_metrics().await {
|
|
error!("Failed to record system metrics: {}", e);
|
|
}
|
|
|
|
if export_metrics {
|
|
if let Err(e) = Self::export_metrics_to_file(&metrics_manager, &metrics_file).await {
|
|
error!("Failed to export metrics to file: {}", e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Start health check task
|
|
let health_manager = self.monitoring_manager.clone();
|
|
let health_interval = self.config.health_check_interval;
|
|
|
|
tokio::spawn(async move {
|
|
let mut interval = interval(Duration::from_secs(health_interval));
|
|
|
|
while let Some(_) = interval.tick().await {
|
|
debug!("Running health checks");
|
|
|
|
match health_manager.run_health_checks().await {
|
|
Ok(results) => {
|
|
for result in results {
|
|
match result.status {
|
|
apt_ostree::monitoring::HealthStatus::Healthy => {
|
|
debug!("Health check passed: {}", result.check_name);
|
|
}
|
|
apt_ostree::monitoring::HealthStatus::Warning => {
|
|
warn!("Health check warning: {} - {}", result.check_name, result.message);
|
|
}
|
|
apt_ostree::monitoring::HealthStatus::Critical => {
|
|
error!("Health check critical: {} - {}", result.check_name, result.message);
|
|
}
|
|
apt_ostree::monitoring::HealthStatus::Unknown => {
|
|
warn!("Health check unknown: {} - {}", result.check_name, result.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Err(e) => {
|
|
error!("Failed to run health checks: {}", e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
info!("Monitoring service started successfully");
|
|
Ok(())
|
|
}
|
|
|
|
/// Stop the monitoring service
|
|
async fn stop(&mut self) -> AptOstreeResult<()> {
|
|
info!("Stopping monitoring service");
|
|
|
|
self.running = false;
|
|
|
|
// Export final metrics
|
|
if self.config.export_metrics {
|
|
if let Err(e) = Self::export_metrics_to_file(&self.monitoring_manager, &self.config.metrics_file).await {
|
|
error!("Failed to export final metrics: {}", e);
|
|
}
|
|
}
|
|
|
|
info!("Monitoring service stopped");
|
|
Ok(())
|
|
}
|
|
|
|
/// Export metrics to file
|
|
async fn export_metrics_to_file(
|
|
monitoring_manager: &Arc<MonitoringManager>,
|
|
file_path: &str,
|
|
) -> AptOstreeResult<()> {
|
|
let metrics_json = monitoring_manager.export_metrics().await?;
|
|
|
|
// Ensure directory exists
|
|
if let Some(parent) = std::path::Path::new(file_path).parent() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
|
|
// Write metrics to file
|
|
std::fs::write(file_path, metrics_json)?;
|
|
|
|
debug!("Metrics exported to: {}", file_path);
|
|
Ok(())
|
|
}
|
|
|
|
/// Get service statistics
|
|
async fn get_statistics(&self) -> AptOstreeResult<String> {
|
|
let stats = self.monitoring_manager.get_statistics().await?;
|
|
|
|
let output = format!(
|
|
"Monitoring Service Statistics:\n\
|
|
Uptime: {} seconds\n\
|
|
Metrics collected: {}\n\
|
|
Performance metrics: {}\n\
|
|
Active transactions: {}\n\
|
|
Health checks performed: {}\n\
|
|
Service running: {}\n",
|
|
stats.uptime_seconds,
|
|
stats.metrics_collected,
|
|
stats.performance_metrics_collected,
|
|
stats.active_transactions,
|
|
stats.health_checks_performed,
|
|
self.running
|
|
);
|
|
|
|
Ok(output)
|
|
}
|
|
|
|
/// Run a single health check cycle
|
|
async fn run_health_check_cycle(&self) -> AptOstreeResult<()> {
|
|
info!("Running health check cycle");
|
|
|
|
let results = self.monitoring_manager.run_health_checks().await?;
|
|
|
|
let mut healthy_count = 0;
|
|
let mut warning_count = 0;
|
|
let mut critical_count = 0;
|
|
let mut unknown_count = 0;
|
|
|
|
for result in results {
|
|
match result.status {
|
|
apt_ostree::monitoring::HealthStatus::Healthy => {
|
|
healthy_count += 1;
|
|
debug!("✅ {}: {}", result.check_name, result.message);
|
|
}
|
|
apt_ostree::monitoring::HealthStatus::Warning => {
|
|
warning_count += 1;
|
|
warn!("⚠️ {}: {}", result.check_name, result.message);
|
|
}
|
|
apt_ostree::monitoring::HealthStatus::Critical => {
|
|
critical_count += 1;
|
|
error!("❌ {}: {}", result.check_name, result.message);
|
|
}
|
|
apt_ostree::monitoring::HealthStatus::Unknown => {
|
|
unknown_count += 1;
|
|
warn!("❓ {}: {}", result.check_name, result.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
info!(
|
|
"Health check cycle completed: {} healthy, {} warnings, {} critical, {} unknown",
|
|
healthy_count, warning_count, critical_count, unknown_count
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Initialize logging
|
|
tracing_subscriber::fmt::init();
|
|
|
|
info!("Starting APT-OSTree monitoring service");
|
|
|
|
// Parse command line arguments
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
if args.len() > 1 {
|
|
match args[1].as_str() {
|
|
"start" => {
|
|
let config = MonitoringServiceConfig::default();
|
|
let mut service = MonitoringService::new(config)?;
|
|
service.start().await?;
|
|
|
|
// Keep the service running
|
|
loop {
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
"stop" => {
|
|
info!("Stop command received (not implemented in this version)");
|
|
}
|
|
"status" => {
|
|
let config = MonitoringServiceConfig::default();
|
|
let service = MonitoringService::new(config)?;
|
|
let stats = service.get_statistics().await?;
|
|
println!("{}", stats);
|
|
}
|
|
"health-check" => {
|
|
let config = MonitoringServiceConfig::default();
|
|
let service = MonitoringService::new(config)?;
|
|
service.run_health_check_cycle().await?;
|
|
}
|
|
"export-metrics" => {
|
|
let config = MonitoringServiceConfig::default();
|
|
let service = MonitoringService::new(config)?;
|
|
let metrics_json = service.monitoring_manager.export_metrics().await?;
|
|
println!("{}", metrics_json);
|
|
}
|
|
_ => {
|
|
eprintln!("Usage: {} [start|stop|status|health-check|export-metrics]", args[0]);
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
} else {
|
|
// Default: start the service
|
|
let config = MonitoringServiceConfig::default();
|
|
let mut service = MonitoringService::new(config)?;
|
|
service.start().await?;
|
|
|
|
// Keep the service running
|
|
loop {
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_monitoring_service_creation() {
|
|
let config = MonitoringServiceConfig::default();
|
|
let service = MonitoringService::new(config).unwrap();
|
|
assert!(service.running == false);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_health_check_cycle() {
|
|
let config = MonitoringServiceConfig::default();
|
|
let service = MonitoringService::new(config).unwrap();
|
|
assert!(service.run_health_check_cycle().await.is_ok());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_statistics() {
|
|
let config = MonitoringServiceConfig::default();
|
|
let service = MonitoringService::new(config).unwrap();
|
|
let stats = service.get_statistics().await.unwrap();
|
|
assert!(!stats.is_empty());
|
|
assert!(stats.contains("Monitoring Service Statistics"));
|
|
}
|
|
}
|