Add datadog-mock-intake crate and serve trace-agent tests with it - #174
lucaspimentel wants to merge 4 commits into
Conversation
In-process mock Datadog intake that accepts the APM endpoints serverless components flush to (/api/v0.2/stats, /api/v0.2/traces, /api/v0.1/pipeline_stats), decodes msgpack/protobuf payloads on arrival, and exposes typed query methods plus raw request capture for test assertions. Ported from the bottlecap fake intake and served with hyper; preserves the 2 MiB wire-body limit, gzip/zstd decoding, stats failure injection, request summaries, and JSON dumps.
Replace the test-local MockServer with the shared datadog-mock-intake crate. Payload assertions now use the intake's decoded capture instead of re-decoding captured bytes, positive assertions wait on decoded payload arrival, and the DSM forwarding test sends a valid msgpack pipeline-stats payload so it verifies both unchanged byte forwarding and intake acceptance.
datadog-mock-intake crate and serve trace-agent tests with it
The datadog-lambda-extension standalone binary and its environment variables are now named mock-intake / MOCK_INTAKE_*; align the crate documentation.
…-intake Guards decompress() against decompression-bomb-style memory blow-up by capping gzip/zstd output at 64 MiB. Also skips building the diagnostic JSON dump when no dump_dir is configured (the default), avoiding a wasted allocation on every request, and marks DSM pipeline-stats payload fields #[serde(default)] so a payload missing an optional field still decodes. 🤖
|
@DataDog review |
There was a problem hiding this comment.
Two of the most critical issues are in datadog-mock-intake: accepted connection tasks are not cancelled on MockIntake drop, allowing traffic to leak across tests; and typed payload captures can record requests out of arrival order due to async decoding. Additionally, the no-stats integration test asserts against decoded stats_payloads() rather than raw captures, meaning a malformed stats POST would silently pass the assertion.
🤖 Bits Code Review · Commit a074193 · @DataDog review to ask questions
|
|
||
| let io = TokioIo::new(stream); | ||
| let state = std::sync::Arc::clone(&state); | ||
| tokio::spawn(async move { |
There was a problem hiding this comment.
Dropping the intake leaves accepted connections running
Each accepted connection runs in a detached task, while dropping MockIntake aborts only the listener. An established HTTP/1 keep-alive connection can therefore continue sending requests after the intake is dropped, violating its shutdown contract and allowing state or traffic to leak across tests. The connection tasks need coordinated cancellation or tracking.
Was this helpful? React 👍 or 👎
🤖 Bits Code Review · @DataDog review to ask questions · Open Bits AI session
| let stats_payloads = mock_intake.stats_payloads(); | ||
| assert!( | ||
| stats_reqs.is_empty(), | ||
| "Expected no stats request to mock server, received {} request(s)", | ||
| stats_reqs.len() | ||
| stats_payloads.is_empty(), | ||
| "Expected no stats request to mock intake, received {} request(s)", | ||
| stats_payloads.len() | ||
| ); |
There was a problem hiding this comment.
Assert against raw stats requests
The negative assertion checks only successfully decoded stats payloads. If the agent sends a malformed stats POST, the intake records the request but excludes it from stats_payloads(), so this test incorrectly passes despite outbound stats traffic.
| let stats_payloads = mock_intake.stats_payloads(); | |
| assert!( | |
| stats_reqs.is_empty(), | |
| "Expected no stats request to mock server, received {} request(s)", | |
| stats_reqs.len() | |
| stats_payloads.is_empty(), | |
| "Expected no stats request to mock intake, received {} request(s)", | |
| stats_payloads.len() | |
| ); | |
| let stats_requests = mock_intake.requests_for_path("/api/v0.2/stats"); | |
| assert!( | |
| stats_requests.is_empty(), | |
| "Expected no stats request to mock intake, received {} request(s)", | |
| stats_requests.len() | |
| ); |
Was this helpful? React 👍 or 👎
🤖 Bits Code Review · @DataDog review to ask questions · Open Bits AI session
What does this PR do?
Adds a shared
datadog-mock-intakecrate: an in-process mock Datadog APM intake served with hyper that accepts the APM endpoints serverless components flush to, decodes msgpack/protobuf payloads on arrival, and exposes typed query methods plus raw request capture for test assertions.POST /api/v0.2/statsPOST /api/v0.2/tracesPOST /api/v0.1/pipeline_statsRewires the
trace-agentintegration tests onto the crate, replacing the test-localMockServer.Motivation
The mock intake was originally built in
datadog-lambda-extension(#1385) as a bottlecap test fixture and then promoted to a feature-gated library module plus a standalonemock-intakebinary. It has already proven useful there:MOCK_INTAKE_FAIL_STATS_FIRST_N) is what lets those tests cover tracer retry behavior against 500 responses, including the guarantee that exactly the first N attempts are rejected.The APM/DSM integration tests in
datadog-lambda-extension(bottlecap) anddatadog-trace-agenteach carried their own intake fixture. Extracting the bottlecap mock intake into this shared crate lets both test suites use one implementation. A follow-up PR indatadog-lambda-extension(#1392) consumes this crate (stacked on this PR's revision), deletes the bottlecap-local copy, and supersedes #1385.Additional Notes
413before decode), 404/405 routing, stats failure injection, request summaries, and JSON dumps.pipeline_stats_url()accessor and raw request capture (CapturedRequest,requests_for_path()). Typed query methods return only accepted, successfully decoded payloads; raw capture records completed POST attempts including rejected ones.LICENSE-3rdparty.csv;dd-rust-license-tool checkpasses.Describe how to test/QA your changes
cargo test -p datadog-mock-intake: 23 tests, including ports of the original bottlecap intake's unit tests plus new transport regression tests (body limit, 404/405, raw-capture semantics, zstd decoding).cargo test --workspace: 432 passed, 0 failed.cargo clippy --workspace --all-targets: clean.dd-rust-license-tool check: passes.