agent_data_plane_config/domains/multi_region_failover.rs
1//! Multi-Region Failover domain. Self-contained: it carries its own failover endpoint (`api_key`,
2//! `site`, `dd_url`), distinct from the primary forwarder endpoint in `shared.endpoints`.
3
4use serde::Serialize;
5
6/// Prefix of the Datadog failover-region metrics intake, completed by the failover site.
7const MRF_METRICS_ENDPOINT_PREFIX: &str = "https://app.mrf.";
8
9/// Resolved Multi-Region Failover configuration.
10#[derive(Clone, Debug, Default, PartialEq, Serialize)]
11pub struct Domain {
12 /// Whether multi-region failover is active.
13 ///
14 /// Defaults to `false`. The failover pipeline is wired when the topology is built, so turning this on takes a
15 /// restart, and it takes a reachable failover region as well: an [`api_key`](Self::api_key), plus either a
16 /// [`site`](Self::site) or a [`dd_url`](Self::dd_url). Without those, the pipeline is not wired at all.
17 pub enabled: bool,
18
19 /// Which metrics are mirrored to the failover region.
20 pub metric_mirroring: MetricMirroring,
21
22 /// API key used to authenticate to the failover region.
23 pub api_key: Option<String>,
24
25 /// Datadog site of the failover region.
26 pub site: Option<String>,
27
28 /// Explicit intake URL for the failover region, overriding the site.
29 pub dd_url: Option<String>,
30}
31
32/// Which metrics are mirrored to the failover region.
33///
34/// The two settings are grouped because they are consumed together: the routing state of the failover metrics
35/// pipeline is derived from both at once. A consumer that watched them separately could rebuild that state from a
36/// fresh value of one and a stale value of the other, describing a configuration that was never published; a live
37/// view of this struct delivers both from one configuration version instead.
38#[derive(Clone, Debug, Default, PartialEq, Serialize)]
39pub struct MetricMirroring {
40 /// Whether metrics are mirrored to the failover region.
41 ///
42 /// Defaults to `false`. Mirroring also requires [`Domain::enabled`], but unlike it this setting is read live,
43 /// which is the point of it: an operator starts and stops mirroring on a running process.
44 pub enabled: bool,
45
46 /// Metrics permitted to be sent to the failover region.
47 ///
48 /// Defaults to empty, which mirrors every metric rather than none: the list narrows mirroring, it does not enable
49 /// it. Names match exactly. Read live, alongside [`enabled`](Self::enabled), so an operator can restrict mirroring
50 /// to the metrics the failover region needs without restarting.
51 pub allowlist: Vec<String>,
52}
53
54impl Domain {
55 /// Returns the failover-region metrics intake URL, if the region is addressable.
56 ///
57 /// [`dd_url`](Self::dd_url) takes precedence and is used as provided. When only
58 /// [`site`](Self::site) is set, the Datadog failover metrics intake is derived from it. Neither
59 /// setting has a default, so a failover region that is configured with neither has no endpoint.
60 pub fn metrics_endpoint_url(&self) -> Option<String> {
61 self.dd_url.clone().or_else(|| {
62 self.site
63 .as_deref()
64 .map(|site| format!("{MRF_METRICS_ENDPOINT_PREFIX}{site}"))
65 })
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::Domain;
72
73 #[test]
74 fn dd_url_takes_precedence_over_site() {
75 let domain = Domain {
76 site: Some("datadoghq.eu".to_string()),
77 dd_url: Some("https://custom-mrf.example.com".to_string()),
78 ..Default::default()
79 };
80
81 assert_eq!(
82 Some("https://custom-mrf.example.com".to_string()),
83 domain.metrics_endpoint_url()
84 );
85 }
86
87 #[test]
88 fn the_site_derives_the_failover_metrics_intake() {
89 let domain = Domain {
90 site: Some("datadoghq.eu".to_string()),
91 ..Default::default()
92 };
93
94 assert_eq!(
95 Some("https://app.mrf.datadoghq.eu".to_string()),
96 domain.metrics_endpoint_url()
97 );
98 }
99
100 #[test]
101 fn a_region_configured_with_neither_setting_has_no_endpoint() {
102 assert_eq!(None, Domain::default().metrics_endpoint_url());
103 }
104}