Add more detailed Http mode config
This commit is contained in:
@ -3,7 +3,10 @@
|
||||
services: {
|
||||
"example": (
|
||||
url: "https://example.org",
|
||||
mode: Check200,
|
||||
mode: (
|
||||
up_status_codes: [Status2XX],
|
||||
down_status_codes: [Status(404)],
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use futures::lock::Mutex;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use reqwest::{Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type ServiceId = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum HealthStatus {
|
||||
/// The service is up and running
|
||||
Up,
|
||||
|
||||
/// The service gave an error when performing the health check
|
||||
/// The service gave a valid error when performing the health check
|
||||
Errored,
|
||||
|
||||
/// The service did not respond to the health check
|
||||
/// The service seems to be completely offline
|
||||
Down,
|
||||
}
|
||||
|
||||
@ -25,9 +26,26 @@ pub struct ServiceConfig {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum HttpHealthCheckMode {
|
||||
/// Expect the server to return a 200 status code. The body is ignored.
|
||||
Check200,
|
||||
pub enum PartialStatusCode {
|
||||
Any,
|
||||
Status1XX,
|
||||
Status2XX,
|
||||
Status3XX,
|
||||
Status4XX,
|
||||
Status5XX,
|
||||
Status(u16),
|
||||
}
|
||||
|
||||
/// If the response status code matches matches any in `ok_status_codes`, status will be Up
|
||||
/// else if it matches any in `down_status_codes` status will be Down, else status will be Errored
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HttpHealthCheckMode {
|
||||
/// If the response status code matches one in this list, the status will be Up
|
||||
pub up_status_codes: Vec<PartialStatusCode>,
|
||||
|
||||
/// If the response status code matches one in this list, the status will be Down
|
||||
#[serde(default)]
|
||||
pub down_status_codes: Vec<PartialStatusCode>,
|
||||
}
|
||||
|
||||
pub struct HealthState {
|
||||
@ -62,15 +80,61 @@ impl HealthState {
|
||||
*self.last_update.lock().await = Some(Utc::now());
|
||||
|
||||
for (id, status) in &self.health {
|
||||
let url = &self.config.services[id].url;
|
||||
let service_config = &self.config.services[id];
|
||||
let url = &service_config.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,
|
||||
Ok(response) => service_config.mode.status_of(&response),
|
||||
};
|
||||
info!("service [{}] new status {:?}", id, new_status);
|
||||
*status.lock().await = Some(new_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpHealthCheckMode {
|
||||
fn status_of(&self, response: &Response) -> HealthStatus {
|
||||
let response_status = response.status();
|
||||
|
||||
fn validate_status(
|
||||
expected: &PartialStatusCode,
|
||||
actual: StatusCode,
|
||||
if_valid: HealthStatus,
|
||||
) -> Option<HealthStatus> {
|
||||
let if_valid = Some(if_valid);
|
||||
match expected {
|
||||
PartialStatusCode::Any => if_valid,
|
||||
&PartialStatusCode::Status(code) => StatusCode::from_u16(code)
|
||||
.map_err(|e| error!("invalid status code: {}", e))
|
||||
.ok()
|
||||
.filter(|status| status == &actual)
|
||||
.and_then(|_| if_valid),
|
||||
PartialStatusCode::Status5XX if actual.is_server_error() => if_valid,
|
||||
PartialStatusCode::Status4XX if actual.is_server_error() => if_valid,
|
||||
PartialStatusCode::Status3XX if actual.is_redirection() => if_valid,
|
||||
PartialStatusCode::Status2XX if actual.is_success() => if_valid,
|
||||
PartialStatusCode::Status1XX if actual.is_informational() => if_valid,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Check if response status matches expected status codes for Up
|
||||
let check_up = self
|
||||
.up_status_codes
|
||||
.iter()
|
||||
.flat_map(|up_code| validate_status(&up_code, response_status, HealthStatus::Up));
|
||||
|
||||
// Check if response status matches expected status codes for Down
|
||||
let check_down = self
|
||||
.down_status_codes
|
||||
.iter()
|
||||
.flat_map(|down_code| validate_status(&down_code, response_status, HealthStatus::Down));
|
||||
|
||||
// Compute status, defaulting to Errored if neigher Up nor Down matched
|
||||
check_up
|
||||
.chain(check_down)
|
||||
.next()
|
||||
.unwrap_or(HealthStatus::Errored)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user