Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
/.idea
/.worktrees

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ resolver = "2"
members = [
"crates/*",
]
exclude = [
"crates/.claude",
]

[workspace.package]
edition = "2024"
Expand Down
38 changes: 38 additions & 0 deletions crates/datadog-trace-agent/src/http_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@ pub fn verify_request_content_length(
None
}

/// Environment variable set by the Lambda runtime to indicate the initialisation type.
/// Lambda Web Adapter (web function / native-http mode) sets this to `"native-http"`;
/// standard on-demand invocations set it to `"on-demand"`.
const ENV_LAMBDA_INIT_TYPE: &str = "AWS_LAMBDA_INITIALIZATION_TYPE";

/// Returns true if the current environment is Lambda Lite (web function / native-http mode).
///
/// Determined by checking [`ENV_LAMBDA_INIT_TYPE`] == `"native-http"`. This is used to gate
/// behaviour specific to long-running web server deployments on Lambda Lite.
pub fn is_lambda_lite() -> bool {
is_lambda_lite_from_env(std::env::var(ENV_LAMBDA_INIT_TYPE).ok().as_deref())
}

fn is_lambda_lite_from_env(val: Option<&str>) -> bool {
val == Some("native-http")
}

/// Builds a reqwest client with optional proxy configuration and timeout.
/// Uses rustls TLS by default. FIPS-compliant TLS is available via the fips feature
pub fn build_client(
Expand All @@ -134,8 +151,29 @@ mod tests {
use hyper::header;
use libdd_common::hyper_migration;

use super::is_lambda_lite_from_env;
use super::verify_request_content_length;

#[test]
fn test_is_lambda_lite_native_http() {
assert!(is_lambda_lite_from_env(Some("native-http")));
}

#[test]
fn test_is_lambda_lite_on_demand() {
assert!(!is_lambda_lite_from_env(Some("on-demand")));
}

#[test]
fn test_is_lambda_lite_empty_string() {
assert!(!is_lambda_lite_from_env(Some("")));
}

#[test]
fn test_is_lambda_lite_unset() {
assert!(!is_lambda_lite_from_env(None));
}

fn create_test_headers_with_content_length(val: &str) -> HeaderMap {
let mut map = HeaderMap::new();
map.insert(header::CONTENT_LENGTH, val.parse().unwrap());
Expand Down
30 changes: 29 additions & 1 deletion crates/datadog-trace-agent/src/mini_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tracing::{debug, error};
use tracing::{debug, error, warn};

use crate::http_utils::{log_and_create_http_response, verify_request_content_length};
use crate::proxy_flusher::{ProxyFlusher, ProxyRequest};
Expand All @@ -31,6 +31,13 @@ const PROFILING_ENDPOINT_PATH: &str = "/profiling/v1/input";
const TRACER_PAYLOAD_CHANNEL_BUFFER_SIZE: usize = 10;
const STATS_PAYLOAD_CHANNEL_BUFFER_SIZE: usize = 10;
const PROXY_PAYLOAD_CHANNEL_BUFFER_SIZE: usize = 10;
/// Sentinel file written on startup in Lambda Lite mode.
/// dd-trace (Node.js) checks this path via DATADOG_MINI_AGENT_PATH in constants.js
/// (datadog/dd-trace-js) to decide whether to switch from LogExporter (stdout) to
/// AgentExporter (HTTP :8126).
/// The parent directory `/tmp/datadog/` is created by the serverless-compat JS layer
/// before this binary is spawned.
const LAMBDA_LITE_SENTINEL_PATH: &str = "/tmp/datadog/mini_agent_ready";

pub struct MiniAgent {
pub config: Arc<config::Config>,
Expand Down Expand Up @@ -147,6 +154,27 @@ impl MiniAgent {
now.elapsed().as_millis()
);

// Write a sentinel file so that Node.js dd-trace detects a running agent and
// switches from LogExporter (stdout) to AgentExporter (HTTP :8126).
// Only written for Lambda Lite; standard Lambda invocations use the Lambda
// Extension path (/opt/extensions/datadog-agent) instead.
// /opt is read-only in Lambda Lite, so we use /tmp/datadog/ (created by the
// serverless-compat JS layer before spawning this binary).
if crate::http_utils::is_lambda_lite() {
let sentinel = std::path::Path::new(LAMBDA_LITE_SENTINEL_PATH);
if let Some(parent) = sentinel.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Err(e) = std::fs::write(sentinel, b"") {
warn!(
"Could not write Lambda Lite sentinel file at {}: {}. \
dd-trace (Node.js) will fall back to LogExporter (stdout), \
traces may not reach Datadog.",
LAMBDA_LITE_SENTINEL_PATH, e
);
}
}

if let Some(pipe_name) = pipe_name_opt {
// Windows named pipe transport
#[cfg(all(windows, feature = "windows-pipes"))]
Expand Down
Loading