Skip to main content

camel_api/
health.rs

1//! Health monitoring types for rust-camel.
2//!
3//! This module provides types for tracking and reporting the health status
4//! of services in a rust-camel application.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::sync::Arc;
9
10use crate::lifecycle::{HealthStatus, ServiceStatus};
11
12/// System-wide health report containing aggregated status of all services.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct HealthReport {
15    pub status: HealthStatus,
16    pub services: Vec<ServiceHealth>,
17    pub timestamp: DateTime<Utc>,
18}
19
20impl Default for HealthReport {
21    fn default() -> Self {
22        Self {
23            status: HealthStatus::Healthy,
24            services: Vec::new(),
25            timestamp: Utc::now(),
26        }
27    }
28}
29
30/// Health status of an individual service.
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct ServiceHealth {
33    pub name: String,
34    pub status: ServiceStatus,
35}
36
37pub type HealthChecker = Arc<dyn Fn() -> HealthReport + Send + Sync>;
38
39/// Programmatic health state readable by platform adapters.
40/// `camel-health` implements this; `camel-platform-kubernetes` consumes it via this trait.
41/// Neither crate depends on the other.
42pub trait HealthSource: Send + Sync {
43    fn liveness(&self) -> HealthStatus;
44    fn readiness(&self) -> HealthStatus;
45
46    /// Default: `Healthy` — non-K8s implementors need not override.
47    fn startup(&self) -> HealthStatus {
48        HealthStatus::Healthy
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_health_source_default_startup() {
58        struct MinimalSource;
59        impl HealthSource for MinimalSource {
60            fn liveness(&self) -> HealthStatus {
61                HealthStatus::Healthy
62            }
63
64            fn readiness(&self) -> HealthStatus {
65                HealthStatus::Healthy
66            }
67        }
68        let s = MinimalSource;
69        assert_eq!(s.startup(), HealthStatus::Healthy);
70    }
71
72    #[test]
73    fn test_health_source_custom_startup() {
74        struct BootingSource;
75        impl HealthSource for BootingSource {
76            fn liveness(&self) -> HealthStatus {
77                HealthStatus::Healthy
78            }
79
80            fn readiness(&self) -> HealthStatus {
81                HealthStatus::Healthy
82            }
83
84            fn startup(&self) -> HealthStatus {
85                HealthStatus::Unhealthy
86            }
87        }
88        let s = BootingSource;
89        assert_eq!(s.startup(), HealthStatus::Unhealthy);
90    }
91
92    #[test]
93    fn test_health_report_serialization() {
94        let report = HealthReport {
95            status: HealthStatus::Healthy,
96            services: vec![ServiceHealth {
97                name: "prometheus".to_string(),
98                status: ServiceStatus::Started,
99            }],
100            timestamp: chrono::Utc::now(),
101        };
102
103        let json = serde_json::to_string(&report).unwrap();
104        assert!(json.contains("Healthy"));
105        assert!(json.contains("prometheus"));
106        assert!(json.contains("Started"));
107        assert!(json.contains("timestamp"));
108    }
109
110    #[test]
111    fn test_health_report_default() {
112        let report = HealthReport::default();
113        assert_eq!(report.status, HealthStatus::Healthy);
114        assert!(report.services.is_empty());
115        assert!(report.timestamp <= chrono::Utc::now());
116    }
117}