diff --git a/crates/datadog-agent-config/src/lib.rs b/crates/datadog-agent-config/src/lib.rs index f671293..f79aa63 100644 --- a/crates/datadog-agent-config/src/lib.rs +++ b/crates/datadog-agent-config/src/lib.rs @@ -42,6 +42,10 @@ use crate::{ pub struct Config { pub site: String, pub api_key: String, + /// Datadog organization UUID. When set, enables delegated authentication + /// against the Datadog SaaS auth tier without requiring an api_key in some + /// flows. Sourced from `DD_ORG_UUID` / `org_uuid` in datadog.yaml. + pub dd_org_uuid: String, pub log_level: LogLevel, // Timeout for the request to flush data to Datadog endpoint @@ -160,6 +164,7 @@ impl Default for Config { Self { site: String::default(), api_key: String::default(), + dd_org_uuid: String::default(), log_level: LogLevel::default(), flush_timeout: 30, diff --git a/crates/datadog-agent-config/src/sources/env.rs b/crates/datadog-agent-config/src/sources/env.rs index 6fb96a1..9523a66 100644 --- a/crates/datadog-agent-config/src/sources/env.rs +++ b/crates/datadog-agent-config/src/sources/env.rs @@ -35,6 +35,12 @@ pub struct EnvConfig { /// The Datadog API key used to submit telemetry to Datadog #[serde(deserialize_with = "deserialize_optional_string")] pub api_key: Option, + /// @env `DD_ORG_UUID` + /// + /// The Datadog organization UUID. When set, enables delegated auth so the + /// agent can submit telemetry without a long-lived API key. + #[serde(deserialize_with = "deserialize_optional_string")] + pub org_uuid: Option, /// @env `DD_LOG_LEVEL` /// /// Minimum log level of the Datadog Agent. @@ -372,6 +378,7 @@ fn merge_config(config: &mut Config, env_config: &EnvConf // Basic fields merge_string!(config, env_config, site); merge_string!(config, env_config, api_key); + merge_string!(config, dd_org_uuid, env_config, org_uuid); merge_option_to_value!(config, env_config, log_level); merge_option_to_value!(config, env_config, flush_timeout); @@ -688,6 +695,7 @@ mod tests { let string_env_vars: &[(&str, &str)] = &[ ("DD_SITE", "custom-site.example.com"), ("DD_API_KEY", "test-api-key-12345"), + ("DD_ORG_UUID", "00000000-0000-0000-0000-000000000001"), ("DD_PROXY_HTTPS", "https://proxy.example.com"), ("DD_HTTP_PROTOCOL", "http1"), ("DD_TLS_CERT_FILE", "/opt/ca-cert.pem"), @@ -770,6 +778,7 @@ mod tests { // String fields (merge_string! → Config String) expected.site = "custom-site.example.com".to_string(); expected.api_key = "test-api-key-12345".to_string(); + expected.dd_org_uuid = "00000000-0000-0000-0000-000000000001".to_string(); expected.dd_url = "https://custom-metrics.example.com".to_string(); expected.url = "https://custom-app.example.com".to_string(); expected.logs_config_logs_dd_url = "https://custom-logs.example.com".to_string(); @@ -954,6 +963,7 @@ mod tests { let expected_config = Config { site: "test-site".to_string(), api_key: "test-api-key".to_string(), + dd_org_uuid: String::default(), log_level: LogLevel::Debug, compression_level: 4, flush_timeout: 42, @@ -1112,6 +1122,37 @@ mod tests { }); } + #[test] + fn test_dd_org_uuid_from_env() { + figment::Jail::expect_with(|jail| { + jail.clear_env(); + jail.set_env("DD_ORG_UUID", "11111111-2222-3333-4444-555555555555"); + + let mut config: Config = Config::default(); + EnvConfigSource + .load(&mut config) + .expect("Failed to load config"); + + assert_eq!(config.dd_org_uuid, "11111111-2222-3333-4444-555555555555"); + Ok(()) + }); + } + + #[test] + fn test_dd_org_uuid_default_empty_when_unset() { + figment::Jail::expect_with(|jail| { + jail.clear_env(); + + let mut config: Config = Config::default(); + EnvConfigSource + .load(&mut config) + .expect("Failed to load config"); + + assert!(config.dd_org_uuid.is_empty()); + Ok(()) + }); + } + #[test] fn test_dogstatsd_config_defaults_to_none() { figment::Jail::expect_with(|jail| { diff --git a/crates/datadog-agent-config/src/sources/yaml.rs b/crates/datadog-agent-config/src/sources/yaml.rs index 933a9fd..75c1a08 100644 --- a/crates/datadog-agent-config/src/sources/yaml.rs +++ b/crates/datadog-agent-config/src/sources/yaml.rs @@ -29,6 +29,11 @@ pub struct YamlConfig { pub site: Option, #[serde(deserialize_with = "deserialize_optional_string")] pub api_key: Option, + /// YAML key: `org_uuid`. Datadog organization UUID. When set, enables + /// delegated auth so the agent can submit telemetry without a long-lived + /// API key. Merges into the resolved config field `dd_org_uuid`. + #[serde(deserialize_with = "deserialize_optional_string")] + pub org_uuid: Option, #[serde(deserialize_with = "deserialize_with_default")] pub log_level: Option, @@ -410,6 +415,7 @@ fn merge_config(config: &mut Config, yaml_config: &YamlCo // Basic fields merge_string!(config, yaml_config, site); merge_string!(config, yaml_config, api_key); + merge_string!(config, dd_org_uuid, yaml_config, org_uuid); merge_option_to_value!(config, yaml_config, log_level); merge_option_to_value!(config, yaml_config, flush_timeout); @@ -747,6 +753,7 @@ mod tests { # Basic fields — string fields get valid non-default values site: "custom-site.example.com" api_key: "test-api-key-12345" +org_uuid: "00000000-0000-0000-0000-000000000001" log_level: [1, 2, 3] flush_timeout: [1, 2, 3] compression_level: [1, 2, 3] @@ -858,6 +865,7 @@ otlp_config: let mut expected: Config = Config::default(); expected.site = "custom-site.example.com".to_string(); expected.api_key = "test-api-key-12345".to_string(); + expected.dd_org_uuid = "00000000-0000-0000-0000-000000000001".to_string(); expected.dd_url = "https://custom-metrics.example.com".to_string(); expected.logs_config_logs_dd_url = "https://custom-logs.example.com".to_string(); expected.apm_dd_url = "https://custom-apm.example.com".to_string(); @@ -1024,6 +1032,7 @@ otlp_config: let expected_config = Config { site: "test-site".to_string(), api_key: "test-api-key".to_string(), + dd_org_uuid: String::default(), log_level: LogLevel::Debug, flush_timeout: 42, compression_level: 4,