1use std::ops::BitAnd;
2use std::collections::HashMap;
3
4use ::iron::prelude::*;
5use ::iron::status;
6
7use ::serde::{Serialize, Serializer};
8use ::json;
9
10#[derive(Debug, Clone)]
11pub enum HealthCheckStatus {
12 Healthy,
13 Unhealthy,
14}
15
16impl PartialEq for HealthCheckStatus {
17 fn eq(&self, other: &HealthCheckStatus) -> bool {
18 match (self, other) {
19 (&HealthCheckStatus::Healthy, &HealthCheckStatus::Healthy) => true,
20 (&HealthCheckStatus::Unhealthy, &HealthCheckStatus::Unhealthy) => true,
21 _ => false,
22 }
23 }
24}
25
26impl Into<status::Status> for HealthCheckStatus {
27 fn into(self) -> status::Status {
28 match self {
29 HealthCheckStatus::Healthy => status::Ok,
30 HealthCheckStatus::Unhealthy => status::InternalServerError,
31 }
32 }
33}
34
35impl Serialize for HealthCheckStatus {
36 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
37 where S: Serializer
38 {
39 match *self {
40 HealthCheckStatus::Healthy => serializer.serialize_str("Ok"),
41 HealthCheckStatus::Unhealthy => serializer.serialize_str("Failed"),
42 }
43 }
44}
45
46impl BitAnd for HealthCheckStatus {
47 type Output = HealthCheckStatus;
48
49 fn bitand(self, other: HealthCheckStatus) -> HealthCheckStatus {
50 match (self, other) {
51 (HealthCheckStatus::Healthy, HealthCheckStatus::Healthy) => HealthCheckStatus::Healthy,
52 _ => HealthCheckStatus::Unhealthy,
53 }
54 }
55}
56
57pub trait HealthCheck: Send {
58 fn name(&self) -> String;
59
60 fn check_health(&mut self) -> HealthCheckStatus;
61}
62
63#[derive(Default)]
64pub struct HealthCheckService {
65 checks: Vec<Box<HealthCheck + 'static>>,
66}
67
68impl HealthCheckService {
69
70 pub fn register_check<H: HealthCheck + 'static>(&mut self, check: H) {
71 self.checks.push(Box::new(check));
72 }
73
74 pub fn check_service_health(&mut self, _: &mut Request) -> IronResult<Response> {
75 let (global, health) = self.execute();
76
77 let payload = json::to_string(&health).unwrap();
78 let status: status::Status = global.into();
79
80 Ok(Response::with((status, payload)))
81 }
82
83 pub fn execute(&mut self) -> (HealthCheckStatus, HashMap<String, HealthCheckStatus>) {
84 let mut map = HashMap::new();
85
86 for check in &mut self.checks {
87 let res = check.check_health();
88 map.insert(check.name(), res);
89 }
90
91 let global_health = map.values()
92 .fold(HealthCheckStatus::Healthy, |check, val| check & val.clone());
93
94 (global_health, map)
95 }
96}