use chrono::{DateTime, Utc}; use futures::lock::Mutex; use log::info; use serde::{Deserialize, Serialize}; use std::collections::HashMap; pub type ServiceId = String; #[derive(Debug)] pub enum HealthStatus { /// The service is up and running Up, /// The service gave an error when performing the health check Errored, /// The service did not respond to the health check Down, } #[derive(Serialize, Deserialize)] pub struct ServiceConfig { pub url: String, pub mode: HttpHealthCheckMode, } #[derive(Serialize, Deserialize)] pub enum HttpHealthCheckMode { /// Expect the server to return a 200 status code. The body is ignored. Check200, } pub struct HealthState { pub last_update: Mutex>>, pub health: HashMap>>, pub config: HealthConfig, } #[derive(Serialize, Deserialize)] pub struct HealthConfig { /// The time between updates (in seconds) pub update_period: u64, /// The list of services pub services: HashMap, } impl HealthState { pub fn new(config: HealthConfig) -> HealthState { HealthState { last_update: Mutex::new(None), health: config .services .iter() .map(|(id, _config)| (id.clone(), Mutex::new(None))) .collect(), config, } } pub async fn update(&self) { *self.last_update.lock().await = Some(Utc::now()); for (id, status) in &self.health { let url = &self.config.services[id].url; info!("service [{}] querying {}", id, url); let new_status = match reqwest::get(url).await { Err(_) => HealthStatus::Down, Ok(response) if response.status().is_success() => HealthStatus::Up, Ok(_response) => HealthStatus::Errored, }; info!("service [{}] new status {:?}", id, new_status); *status.lock().await = Some(new_status); } } }