datadog_agent_config/
lib.rs

1pub mod classifier;
2
3mod duration_de;
4
5/// Decoders that turn a raw environment-variable string into the JSON shape a schema leaf declares.
6pub mod env_decode;
7mod list_de;
8mod string_de;
9
10/// Builds the typed configuration base by reading environment variables directly and decoding them
11/// into the nested configuration shape.
12pub mod env_reader;
13
14/// Relocates underscore-joined environment keys into the nested Datadog configuration shape.
15// TODO: remove when all callers use the direct typed environment reader.
16pub mod env_overlay;
17
18/// Build-time generated code, produced from `core_schema.yaml` plus `schema_overlay.yaml`.
19mod generated;
20
21/// Compatibility support for the by-key configuration path (key aliases and environment remapping).
22// TODO: remove when all callers use the typed configuration path.
23pub mod remapper;
24
25/// The translation error type recorded by the translator and surfaced by the witness driver.
26mod translate_error;
27
28pub use env_decode::EnvDecode;
29pub use env_overlay::{apply_env_overlay, EnvOverlayMode};
30pub use env_reader::{apply_datadog_env, apply_env_at_path, datadog_leaf_paths, EnvKey};
31pub use generated::{drive, DatadogConfigWitness, DatadogConfiguration};
32pub use remapper::{DatadogRemapper, KEY_ALIASES};
33pub use translate_error::{TranslateError, TranslateErrors};
34
35#[cfg(test)]
36mod string_list_shape_tests {
37    use super::DatadogConfiguration;
38
39    // A string-list leaf must accept both shapes the config sources produce: a real sequence (from a
40    // file or the remote Agent stream) and a single space-separated string (from an environment
41    // variable, e.g. `DD_DOGSTATSD_TAGS="env:prod team:core"`). The generated deserializer wires the
42    // shape-tolerant reader onto every `Vec<String>` leaf; these assertions guard that wiring so a
43    // regenerate that drops it fails loudly instead of crashing config load on the string form.
44
45    #[test]
46    fn string_list_leaf_accepts_a_space_separated_string() {
47        let config: DatadogConfiguration =
48            serde_json::from_value(serde_json::json!({ "dogstatsd_tags": "env:prod team:core" }))
49                .expect("space-separated string deserializes into the string-list leaf");
50        assert_eq!(config.dogstatsd_tags, vec!["env:prod", "team:core"]);
51    }
52
53    #[test]
54    fn string_list_leaf_accepts_a_sequence() {
55        let config: DatadogConfiguration =
56            serde_json::from_value(serde_json::json!({ "dogstatsd_tags": ["env:prod", "team:core"] }))
57                .expect("sequence deserializes into the string-list leaf");
58        assert_eq!(config.dogstatsd_tags, vec!["env:prod", "team:core"]);
59    }
60}
61
62#[cfg(test)]
63mod string_map_list_shape_tests {
64    use super::DatadogConfiguration;
65
66    #[test]
67    fn additional_endpoints_accept_scalar_values() {
68        let config: DatadogConfiguration = serde_json::from_value(serde_json::json!({
69            "additional_endpoints": {
70                "https://agent.datadoghq.com.": "ENC[vault://api-key]"
71            }
72        }))
73        .expect("scalar additional endpoint API key deserializes");
74
75        assert_eq!(
76            config.additional_endpoints["https://agent.datadoghq.com."],
77            ["ENC[vault://api-key]"]
78        );
79    }
80
81    #[test]
82    fn additional_endpoints_accept_sequence_values() {
83        let config: DatadogConfiguration = serde_json::from_value(serde_json::json!({
84            "additional_endpoints": {
85                "https://agent.datadoghq.com.": ["first", "second"]
86            }
87        }))
88        .expect("additional endpoint API key sequence deserializes");
89
90        assert_eq!(
91            config.additional_endpoints["https://agent.datadoghq.com."],
92            ["first", "second"]
93        );
94    }
95}
96
97#[cfg(test)]
98mod byte_size_shape_tests {
99    use super::DatadogConfiguration;
100
101    // A byte-size leaf declared `input_shape: string_or_integer` in the overlay must accept both the
102    // canonical string form (`"10MB"`) and the documented bare-integer byte count (`10485760`). The
103    // generated deserializer normalizes the integer to a decimal string; these assertions guard that
104    // wiring so a regenerate that drops it fails loudly instead of failing config load on the numeric
105    // form (which previously aborted a strict-gate startup).
106
107    #[test]
108    fn byte_size_leaf_accepts_a_string() {
109        let config: DatadogConfiguration =
110            serde_json::from_value(serde_json::json!({ "dogstatsd_log_file_max_size": "10MB" }))
111                .expect("string byte size deserializes");
112        assert_eq!(config.dogstatsd_log_file_max_size, "10MB");
113    }
114
115    #[test]
116    fn byte_size_leaf_accepts_a_numeric_byte_count() {
117        let config: DatadogConfiguration =
118            serde_json::from_value(serde_json::json!({ "dogstatsd_log_file_max_size": 10485760 }))
119                .expect("numeric byte size deserializes");
120        assert_eq!(config.dogstatsd_log_file_max_size, "10485760");
121
122        let config: DatadogConfiguration = serde_json::from_value(serde_json::json!({ "log_file_max_size": 10485760 }))
123            .expect("numeric byte size deserializes");
124        assert_eq!(config.log_file_max_size, "10485760");
125    }
126
127    #[test]
128    fn byte_size_leaf_rejects_a_float() {
129        let result: Result<DatadogConfiguration, _> =
130            serde_json::from_value(serde_json::json!({ "dogstatsd_log_file_max_size": 10.5 }));
131        assert!(result.is_err());
132    }
133}