Skip to content

Commit b9a7bdd

Browse files
committed
feat(otlp): add env entry support for select config keys
1 parent 843c6e4 commit b9a7bdd

1 file changed

Lines changed: 88 additions & 2 deletions

File tree

  • lib/datadog-agent/config-overlay-model/src

lib/datadog-agent/config-overlay-model/src/lib.rs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,9 @@ fn load_otel_receiver(otel_dir: &Path) -> Result<serde_yaml::Value, Error> {
596596
// Convert to Datadog schema dialect.
597597
convert_to_datadog_dialect(&mut protocols);
598598

599+
// Tag env bindings. Only a subset is is registered and gets an explicit `env_vars` designation; the rest get `no-env`.
600+
tag_otel_env_bindings(&mut protocols, &["protocols"]);
601+
599602
// Wrap in a "receiver" section to match the Datadog schema structure.
600603
let mut receiver_section = serde_yaml::Mapping::new();
601604
receiver_section.insert(
@@ -658,7 +661,7 @@ fn merge_receiver_node(otel: &mut serde_yaml::Value, datadog: &serde_yaml::Value
658661
otel_map.get_mut("properties").and_then(|v| v.as_mapping_mut()),
659662
datadog_map.get("properties").and_then(|v| v.as_mapping()),
660663
) {
661-
// Merge overlapping OTEL properties with corresponding Datadog properties.
664+
// Merge overlapping OTel properties with corresponding Datadog properties.
662665
let otel_keys: Vec<serde_yaml::Value> = otel_props.keys().cloned().collect();
663666
for key in &otel_keys {
664667
if let Some(datadog_val) = datadog_props.get(key) {
@@ -667,7 +670,7 @@ fn merge_receiver_node(otel: &mut serde_yaml::Value, datadog: &serde_yaml::Value
667670
}
668671
}
669672
}
670-
// Add Datadog-only properties that don't exist in OTEL.
673+
// Add Datadog-only properties that don't exist in OTel.
671674
for (key, val) in datadog_props.iter() {
672675
if !otel_props.contains_key(key) {
673676
otel_props.insert(key.clone(), val.clone());
@@ -857,6 +860,58 @@ fn convert_to_datadog_dialect(value: &mut serde_yaml::Value) {
857860
}
858861
}
859862

863+
/// List of env-reachable `otlp_config.receiver` keys
864+
const ENV_REGISTERED_OTEL_KEYS: &[&str] = &[
865+
"otlp_config.receiver.protocols.grpc.endpoint",
866+
"otlp_config.receiver.protocols.grpc.transport",
867+
"otlp_config.receiver.protocols.grpc.max_recv_msg_size_mib",
868+
"otlp_config.receiver.protocols.grpc.max_concurrent_streams",
869+
"otlp_config.receiver.protocols.grpc.read_buffer_size",
870+
"otlp_config.receiver.protocols.grpc.write_buffer_size",
871+
"otlp_config.receiver.protocols.grpc.include_metadata",
872+
"otlp_config.receiver.protocols.grpc.keepalive.enforcement_policy.min_time",
873+
"otlp_config.receiver.protocols.http.endpoint",
874+
"otlp_config.receiver.protocols.http.max_request_body_size",
875+
"otlp_config.receiver.protocols.http.include_metadata",
876+
"otlp_config.receiver.protocols.http.cors.allowed_headers",
877+
"otlp_config.receiver.protocols.http.cors.allowed_origins",
878+
];
879+
880+
/// Walks the OTel receiver subtree and tags each setting with `env_vars` or `no-env`.
881+
fn tag_otel_env_bindings(value: &mut serde_yaml::Value, path_parts: &[&str]) {
882+
let Some(map) = value.as_mapping_mut() else { return };
883+
884+
if map.get("node_type").and_then(|v| v.as_str()) == Some("setting") {
885+
let full_path = format!("otlp_config.receiver.{}", path_parts.join("."));
886+
if ENV_REGISTERED_OTEL_KEYS.contains(&full_path.as_str()) {
887+
let env_var = format!("DD_{}", full_path.replace('.', "_").to_uppercase());
888+
let env_vars = serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(env_var)]);
889+
map.insert(serde_yaml::Value::String("env_vars".to_string()), env_vars);
890+
} else {
891+
let tags_key = serde_yaml::Value::String("tags".to_string());
892+
let mut tags = map
893+
.get(&tags_key)
894+
.and_then(|v| v.as_sequence().cloned())
895+
.unwrap_or_default();
896+
if !tags.iter().any(|t| t.as_str() == Some("no-env")) {
897+
tags.push(serde_yaml::Value::String("no-env".to_string()));
898+
}
899+
map.insert(tags_key, serde_yaml::Value::Sequence(tags));
900+
}
901+
return;
902+
}
903+
904+
if let Some(props) = map.get_mut("properties").and_then(|v| v.as_mapping_mut()) {
905+
for (key, val) in props.iter_mut() {
906+
if let Some(key_str) = key.as_str() {
907+
let mut parts = path_parts.to_vec();
908+
parts.push(key_str);
909+
tag_otel_env_bindings(val, &parts);
910+
}
911+
}
912+
}
913+
}
914+
860915
const VALIDATION_RULES: &str = "\n\
861916
\n\
862917
Rules that must hold in schema_overlay.yaml:\n\
@@ -1344,4 +1399,35 @@ excluded: {}
13441399
"unexpected error: {err}"
13451400
);
13461401
}
1402+
1403+
#[test]
1404+
fn verify_otel_receiver_env_bindings() {
1405+
let schema_path = Path::new(env!("CARGO_MANIFEST_DIR"))
1406+
.join("..")
1407+
.join("config")
1408+
.join("schema")
1409+
.join("core")
1410+
.join("core_schema.yaml");
1411+
let otel_schema_dir = otel_schema_dir_for_tests();
1412+
let schema_map = crate::schema_gen::load_schema(&schema_path, &otel_schema_dir);
1413+
1414+
let registered: HashSet<&str> = super::ENV_REGISTERED_OTEL_KEYS.iter().copied().collect();
1415+
1416+
for (key, info) in &schema_map {
1417+
if !key.starts_with("otlp_config.receiver.protocols.") {
1418+
continue;
1419+
}
1420+
if registered.contains(key.as_str()) {
1421+
assert!(
1422+
matches!(info.env, crate::schema_gen::EnvBinding::Overridden(_)),
1423+
"registered key `{key}` should have explicit env_vars"
1424+
);
1425+
} else {
1426+
assert!(
1427+
matches!(info.env, crate::schema_gen::EnvBinding::None),
1428+
"unregistered key `{key}` should be no-env"
1429+
);
1430+
}
1431+
}
1432+
}
13471433
}

0 commit comments

Comments
 (0)