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}