Skip to main content

witchcraft_server/health/
mod.rs

1// Copyright 2022 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! Health checks.
15pub use api::objects::HealthState;
16use conjure_object::Any;
17pub use registry::HealthCheckRegistry;
18use serde::Serialize;
19use staged_builder::staged_builder;
20use std::any::TypeId;
21use std::collections::BTreeMap;
22use std::sync::Arc;
23
24#[allow(warnings)]
25#[rustfmt::skip]
26pub(crate) mod api;
27pub(crate) mod config_reload;
28pub(crate) mod endpoint_500s;
29pub(crate) mod minidump;
30pub(crate) mod panics;
31mod registry;
32pub(crate) mod service_dependency;
33
34mod private {
35    pub struct PrivacyToken;
36}
37
38/// A health check.
39pub trait HealthCheck: 'static + Sync + Send {
40    /// Returns the check's type.
41    ///
42    /// The type must be `SCREAMING_SNAKE_CASE`.
43    fn type_(&self) -> &str;
44
45    /// Performs the check, returning its result.
46    fn result(&self) -> HealthCheckResult;
47
48    // PrivacyToken can't be named outside of this crate, so it prevents anyone from overriding this
49    // default implementation in another crate. That allows us to trust it to be correct in the
50    // downcast methods below.
51    #[doc(hidden)]
52    fn __private_api_type_id(&self, _: private::PrivacyToken) -> TypeId
53    where
54        Self: 'static,
55    {
56        TypeId::of::<Self>()
57    }
58}
59
60impl dyn HealthCheck {
61    /// Returns `true` if the health check's type is `T`.
62    pub fn is<T>(&self) -> bool
63    where
64        T: HealthCheck,
65    {
66        self.__private_api_type_id(private::PrivacyToken) == TypeId::of::<T>()
67    }
68
69    /// Attempts to downcast the health check to the type `T` if it is that type.
70    pub fn downcast_ref<T>(&self) -> Option<&T>
71    where
72        T: HealthCheck,
73    {
74        if self.is::<T>() {
75            unsafe { Some(&*(self as *const dyn HealthCheck as *const T)) }
76        } else {
77            None
78        }
79    }
80
81    /// Attempts to downcast the health check to the type `T` if it is that type.
82    pub fn downcast_arc<T>(self: Arc<Self>) -> Result<Arc<T>, Arc<Self>>
83    where
84        T: HealthCheck,
85    {
86        if self.is::<T>() {
87            unsafe { Ok(Arc::from_raw(Arc::into_raw(self).cast::<T>())) }
88        } else {
89            Err(self)
90        }
91    }
92}
93
94/// The result of a health check.
95#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
96#[staged_builder]
97pub struct HealthCheckResult {
98    state: HealthState,
99    #[builder(default, into)]
100    message: Option<String>,
101    #[builder(map(key(type = String, into), value(custom(type = impl Serialize, convert = serialize))))]
102    params: BTreeMap<String, Any>,
103}
104
105fn serialize(arg: impl Serialize) -> Any {
106    Any::new(arg).expect("value failed to serialize")
107}
108
109impl HealthCheckResult {
110    /// Health state of the check.
111    #[inline]
112    pub fn state(&self) -> &HealthState {
113        &self.state
114    }
115
116    /// Text describing the state of the check which should provide enough information for the check to be actionable
117    /// when included in an alert.
118    #[inline]
119    pub fn message(&self) -> Option<&str> {
120        self.message.as_deref()
121    }
122
123    /// Additional redacted information on the nature of the health check.
124    #[inline]
125    pub fn params(&self) -> &BTreeMap<String, Any> {
126        &self.params
127    }
128}