1use serde::Deserialize;
3
4#[derive(Debug, Clone, Deserialize)]
5#[serde(deny_unknown_fields)]
6pub struct HaConfig {
7 #[serde(default)]
8 pub enabled: bool,
9 #[serde(default = "strategy")]
10 pub strategy: crate::ha::FailoverStrategy,
11 #[serde(default = "interval")]
12 pub probe_interval_secs: u64,
13 #[serde(default = "timeout")]
14 pub probe_timeout_secs: u64,
15 #[serde(default = "threshold")]
16 pub failure_threshold: u32,
17 #[serde(default = "cooldown")]
18 pub cooldown_secs: u64,
19 #[serde(default)]
21 pub endpoints: std::collections::BTreeMap<String, String>,
22}
23fn strategy() -> crate::ha::FailoverStrategy {
24 crate::ha::FailoverStrategy::ActivePassive
25}
26fn interval() -> u64 {
27 30
28}
29fn timeout() -> u64 {
30 5
31}
32fn threshold() -> u32 {
33 3
34}
35fn cooldown() -> u64 {
36 60
37}
38
39impl HaConfig {
40 pub fn validate(&self, cas: &[super::CaConfig]) -> Result<(), String> {
41 if !self.enabled {
42 return Ok(());
43 }
44 if self.probe_interval_secs == 0
45 || self.probe_timeout_secs == 0
46 || self.failure_threshold == 0
47 || self.cooldown_secs == 0
48 {
49 return Err("HA timing and failure threshold values must be positive".into());
50 }
51 for (id, endpoint) in &self.endpoints {
52 if !cas.iter().any(|ca| &ca.id == id) {
53 return Err(format!("unknown HA CA: {id}"));
54 }
55 let url = url::Url::parse(endpoint).map_err(|e| e.to_string())?;
56 if url.scheme() != "https"
57 || url.host_str().is_none()
58 || !url.username().is_empty()
59 || url.password().is_some()
60 {
61 return Err("HA endpoints must use HTTPS without URL credentials".into());
62 }
63 }
64 Ok(())
65 }
66}