|
1 | | -//! Configuration system facade: loading, authority resolution, translation, validation, config |
2 | | -//! views, and runtime updates. |
| 1 | +//! The Configuration system: translation and subscription of external configuration sources into an |
| 2 | +//! ADP-typed model. |
3 | 3 | //! |
4 | | -//! This is the only ADP production crate permitted to use the raw-map APIs of `saluki-config`; all |
5 | | -//! raw configuration access is confined here. It wires the Datadog source-normalization metadata |
6 | | -//! (`KEY_ALIASES` and the remapper) from `datadog-agent-config` into the loader, drives the witness |
7 | | -//! translation, and produces a `SalukiConfiguration`. |
| 4 | +//! This crate turns configuration sources into `SalukiConfiguration`: |
8 | 5 | //! |
9 | | -//! It does not construct components and does not depend on `saluki-components`. |
| 6 | +//! - the typed Datadog source (`DatadogConfiguration`), whose supported keys the generated `drive` |
| 7 | +//! feeds to `DatadogTranslator` (a `DatadogConfigWitness`) one key at a time, and |
| 8 | +//! - the Saluki-schema-only source ([`SalukiOnly`]), whose values [`SalukiOnly::seed`] copies into |
| 9 | +//! the fields the Datadog schema does not cover. |
| 10 | +//! |
| 11 | +//! [`translate`] runs both writers and returns the assembled model. This is the only ADP production |
| 12 | +//! crate that bridges the source configuration to the model; it constructs no components and does |
| 13 | +//! not depend on `saluki-components`. |
| 14 | +
|
| 15 | +mod saluki_only; |
| 16 | +mod translators; |
| 17 | + |
| 18 | +use agent_data_plane_config::SalukiConfiguration; |
| 19 | +use datadog_agent_config::{DatadogConfiguration, TranslateError}; |
| 20 | +pub use saluki_only::SalukiOnly; |
| 21 | +pub use translators::ConfigTranslator; |
| 22 | +use translators::DatadogTranslator; |
| 23 | + |
| 24 | +/// Translates the Datadog and Saluki-only sources into one [`SalukiConfiguration`]. |
| 25 | +/// |
| 26 | +/// The Datadog `drive` feeds every supported key in `datadog` to a `DatadogTranslator`, which |
| 27 | +/// assembles the multi-key endpoint field. Finally [`SalukiOnly::seed`] copies the Saluki-only |
| 28 | +/// values into their (disjoint) destinations. |
| 29 | +/// |
| 30 | +/// # Errors |
| 31 | +/// |
| 32 | +/// Returns the first [`TranslateError`] recorded while consuming a Datadog value (for example, an |
| 33 | +/// enum or byte-size string that cannot be parsed). Seeding does not fail. |
| 34 | +pub fn translate( |
| 35 | + datadog: &DatadogConfiguration, saluki_only: &SalukiOnly, |
| 36 | +) -> Result<SalukiConfiguration, TranslateError> { |
| 37 | + let mut config = DatadogTranslator::new(datadog).translate()?; |
| 38 | + saluki_only.seed(&mut config); |
| 39 | + Ok(config) |
| 40 | +} |
| 41 | + |
| 42 | +#[cfg(test)] |
| 43 | +mod tests { |
| 44 | + use std::time::Duration; |
| 45 | + |
| 46 | + use agent_data_plane_config::domains::dogstatsd::OriginTagCardinality; |
| 47 | + use datadog_agent_config::DatadogConfiguration; |
| 48 | + use serde_json::json; |
| 49 | + |
| 50 | + use super::{translate, SalukiOnly}; |
| 51 | + |
| 52 | + #[test] |
| 53 | + fn translate_small_map_through_witness_and_seed() { |
| 54 | + // A small raw Datadog source map exercising a scalar conversion, an enum parse, a |
| 55 | + // seconds->Duration conversion, and the endpoint-assembly inputs. |
| 56 | + let datadog: DatadogConfiguration = serde_json::from_value(json!({ |
| 57 | + "api_key": "abc", |
| 58 | + "dd_url": "https://custom.example.com", |
| 59 | + "dogstatsd_port": 9125, |
| 60 | + "dogstatsd_tag_cardinality": "high", |
| 61 | + "expected_tags_duration": 15.0, |
| 62 | + })) |
| 63 | + .expect("datadog source deserializes"); |
| 64 | + |
| 65 | + // A small Saluki-only source setting one seeded field. |
| 66 | + let saluki_only: SalukiOnly = serde_json::from_value(json!({ |
| 67 | + "dogstatsd": { "tcp_port": 8126 }, |
| 68 | + })) |
| 69 | + .expect("saluki-only source deserializes"); |
| 70 | + |
| 71 | + let config = translate(&datadog, &saluki_only).expect("translation succeeds"); |
| 72 | + |
| 73 | + // Driven scalar conversion: i64 -> u16. |
| 74 | + assert_eq!(config.domains.dogstatsd.listeners.port, 9125); |
| 75 | + // Driven enum parse. |
| 76 | + assert_eq!( |
| 77 | + config.domains.dogstatsd.origin.tag_cardinality, |
| 78 | + OriginTagCardinality::High |
| 79 | + ); |
| 80 | + // Driven seconds(f64) -> Duration. |
| 81 | + assert_eq!(config.shared.tags.expected_tags_duration, Duration::from_secs_f64(15.0)); |
| 82 | + // Assembled multi-key field: the primary endpoint carries the api key and the resolved URL |
| 83 | + // (`dd_url` takes precedence over `site`). |
| 84 | + let primary = &config.shared.endpoints.endpoints[0]; |
| 85 | + assert_eq!(primary.api_keys, vec!["abc".to_string()]); |
| 86 | + assert_eq!(primary.url, "https://custom.example.com"); |
| 87 | + // Seeded Saluki-only field. |
| 88 | + assert_eq!(config.domains.dogstatsd.listeners.tcp_port, 8126); |
| 89 | + } |
| 90 | +} |
0 commit comments