Skip to content

Commit 476a5b6

Browse files
committed
chore(agent-data-plane): Antithesis test harness and workload
Adds an Antithesis test harness for agent-data-plane under `test/antithesis/`. It brings ADP up in a minimal, runnable topology and gives Antithesis a workload to drive: - An instrumented ADP image (LLVM SanitizerCoverage), gated behind a new, off-by-default `antithesis` cargo feature, with DWARF symbols exposed under `/symbols`. - A mock `datadog-intake` and a `millstone`-based workload (the `adp-antithesis-workload` crate: a high-cardinality DogStatsD driver plus an end-to-end delivery verifier). - `docker-compose.yaml`, a standalone ADP config with a self-signed IPC cert, `setup_complete` wiring, and a `.dockerignore` re-include so `test/antithesis/` reaches the Docker build context. The `antithesis` feature and SDK are net-new and off by default, so production builds are unchanged. - [ ] Bug fix - [ ] New feature - [x] Non-functional (chore, refactoring, docs) - [ ] Performance - `docker compose -f test/antithesis/config/docker-compose.yaml build` builds all three images (amd64; the in-build instrumentation symbol checks pass). - `snouty validate test/antithesis/config` brings the stack up healthy, observes `setup_complete`, and discovers the workload test commands. - `cargo check` (default features) is unaffected; `cargo fmt --check` is clean. CI notes (please action before merge): - This adds optional deps to `Cargo.lock`, so `check-licenses` (`make check-licenses`) will fail until `LICENSE-3rdparty.csv` is regenerated (`make license`). - Worth confirming `check-deny` and `check-unused-deps` are happy with the feature-gated `antithesis_sdk` / `antithesis-instrumentation` deps. - The research and property catalog this harness targets live in `test/antithesis/scratchbook/` (the next PR in this stack). - Antithesis Rust SDK and coverage-instrumentation documentation.
1 parent 1bd1613 commit 476a5b6

19 files changed

Lines changed: 845 additions & 1 deletion

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ node_modules/
88
target/
99
**/fuzz/target
1010
test/
11+
!test/antithesis/
1112
.gitignore
1213
.gitlab-ci.yml
1314
CONTRIBUTING.md

Cargo.lock

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ members = [
2929
"lib/saluki-metrics",
3030
"lib/saluki-tls",
3131
"lib/stringtheory",
32+
"test/antithesis/harness",
3233
]
3334
resolver = "2"
3435

@@ -66,6 +67,7 @@ async-trait = { version = "0.1", default-features = false }
6667
atty = { version = "0.2", default-features = false }
6768
axum = { version = "0.8", default-features = false }
6869
bytes = { version = "1", default-features = false }
70+
clap = { version = "4", default-features = false, features = [] }
6971
protobuf = { version = "3.7", default-features = false, features = [
7072
"with-bytes",
7173
] }
@@ -223,6 +225,8 @@ chumsky = { version = "0.13", default-features = false }
223225
logos = { version = "0.16", default-features = false }
224226
lru-slab = { version = "0.1.2", default-features = false }
225227
hickory-resolver = { version = "0.26", default-features = false }
228+
antithesis-instrumentation = { version = "0.1" }
229+
antithesis_sdk = { git = "https://github.com/antithesishq/antithesis-sdk-rust", rev = "6829a946e7e970cc743ffe17c3cee7d2bc25425a", default-features = false } # 0.2.8 is pinned to rand 0.8, rev version allows us to select workspace rand
226230

227231
[patch.crates-io]
228232
# Forked version of `hyper-http-proxy` that removes an unused dependency on `rustls-native-certs`, which transitively depends

LICENSE-3rdparty.csv

Lines changed: 77 additions & 0 deletions
Large diffs are not rendered by default.

Makefile

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ run-adp-standalone: build-adp create-dummy-agent-config create-dummy-ipc-cert
306306
run-adp-standalone: ## Runs ADP locally in standalone mode (debug)
307307
@echo "[*] Running ADP..."
308308
@DD_DATA_PLANE_STANDALONE_MODE=true DD_DATA_PLANE_DOGSTATSD_ENABLED=true \
309-
DD_API_KEY=api-key-adp-standalone DD_HOSTNAME=adp-standalone \
309+
DD_API_KEY=api-key-adp-standalone DD_HOSTNAME=adp-standalone \
310310
DD_DOGSTATSD_PORT=9191 DD_DOGSTATSD_SOCKET=/tmp/adp-dogstatsd-dgram.sock DD_DOGSTATSD_STREAM_SOCKET=/tmp/adp-dogstatsd-stream.sock \
311311
DD_IPC_CERT_FILE_PATH=$(ADP_STANDALONE_IPC_CERT_FILE) \
312312
target/devel/agent-data-plane --config /tmp/adp-empty-config.yaml run
@@ -579,6 +579,28 @@ endif
579579
@echo "[*] Ensuring Miri is setup..."
580580
@cargo +nightly-2025-06-16 miri setup
581581

582+
##@ Antithesis
583+
584+
ANTITHESIS_CONFIG_DIR := test/antithesis/deploy
585+
ANTITHESIS_COMPOSE_FILE := $(ANTITHESIS_CONFIG_DIR)/docker-compose.yaml
586+
587+
.PHONY: check-antithesis-tools
588+
check-antithesis-tools:
589+
ifeq ($(shell command -v snouty >/dev/null || echo not-found), not-found)
590+
$(error "snouty must be present to validate the Antithesis harness, see https://github.com/antithesishq/snouty")
591+
endif
592+
593+
.PHONY: antithesis-build
594+
antithesis-build: ## Builds the Antithesis harness container images
595+
@echo "[*] Building Antithesis harness images..."
596+
@docker compose -f $(ANTITHESIS_COMPOSE_FILE) build
597+
598+
.PHONY: antithesis-validate
599+
antithesis-validate: check-antithesis-tools antithesis-build
600+
antithesis-validate: ## Validates the Antithesis harness: builds images, runs 'snouty validate'
601+
@echo "[*] Validating Antithesis harness with snouty..."
602+
@snouty validate $(ANTITHESIS_CONFIG_DIR)
603+
582604
##@ Profiling
583605

584606
.PHONY: profile-run-blackhole

bin/agent-data-plane/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ workspace = true
1111
[features]
1212
default = []
1313
fips = ["saluki-app/tls-fips", "saluki-components/fips"]
14+
antithesis = ["dep:antithesis_sdk", "antithesis_sdk/full", "dep:antithesis-instrumentation"]
1415

1516
[dependencies]
17+
antithesis-instrumentation = { workspace = true, optional = true }
18+
antithesis_sdk = { workspace = true, optional = true }
1619
argh = { workspace = true, features = ["help"] }
1720
async-trait = { workspace = true }
1821
bytesize = { workspace = true }

bin/agent-data-plane/src/main.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
#![deny(missing_docs)]
88
use std::time::Instant;
99

10+
// Pull in the Antithesis coverage-instrumentation runtime shim only when
11+
// building for antithesis. Load-baring: equired to avoid the shim being dropped
12+
// as unused.
13+
#[cfg(feature = "antithesis")]
14+
use antithesis_instrumentation as _;
1015
use datadog_agent_commons::platform::PlatformSettings;
1116
use metrics::Level;
1217
use saluki_app::bootstrap::{AppBootstrapper, Bootstrap, BootstrapGuard};
@@ -39,6 +44,12 @@ static ALLOC: resource_accounting::TrackingAllocator<std::alloc::System> =
3944
#[tokio::main]
4045
async fn main() -> Result<(), GenericError> {
4146
let started = Instant::now();
47+
48+
// Initialize the Antithesis SDK as early as possible so assertions and lifecycle hooks register
49+
// their catalog before any are evaluated. No-op outside Antithesis and absent in production builds.
50+
#[cfg(feature = "antithesis")]
51+
antithesis_sdk::antithesis_init();
52+
4253
let cli: Cli = argh::from_env();
4354

4455
// Print version and exit early without requiring config.
@@ -83,6 +94,11 @@ async fn main() -> Result<(), GenericError> {
8394
.await
8495
.error_context("Failed to complete bootstrap phase.")?;
8596

97+
// Bootstrap-integration probe: proves the Antithesis SDK is linked, cataloging works, and the
98+
// instrumentation path is wired.
99+
#[cfg(feature = "antithesis")]
100+
antithesis_sdk::assert_reachable!("agent-data-plane completed bootstrap", &serde_json::json!({}));
101+
86102
// Run the given subcommand. The bootstrap supervisor is forwarded by value; only the long-lived `run`
87103
// subcommand actually drives it (it is added as a child of the internal supervisor inside
88104
// `handle_run_command`). All other subcommands drop it on entry.

test/antithesis/AGENTS.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
This directory contains files relevant to running tests in Antithesis.
2+
3+
# Skills
4+
5+
Use the `antithesis-setup` skill to scaffold and manage this directory. Use the
6+
`antithesis-research` skill to analyze the system and build a property
7+
catalog. Use the `antithesis-workload` skill to implement assertions and test
8+
commands. Use the `antithesis-launch` skill to build, validate, and submit
9+
Antithesis runs — do not run `snouty launch` directly.
10+
11+
**snouty launch**
12+
13+
Use `snouty launch --json --webhook basic_test --config test/antithesis/deploy`
14+
to start an Antithesis run. Always run `compose build` first to ensure images
15+
are up to date.
16+
17+
**snouty validate**
18+
19+
Use this command to quickly validate changes to the Antithesis scaffolding. See
20+
`snouty validate --help` for details.
21+
22+
**setup-complete.sh**
23+
24+
Inject this script into a Dockerfile to notify Antithesis that setup is
25+
complete. This script should only run once the system under test is ready for
26+
testing. Antithesis will not run any test commands until it receives this event.
27+
28+
**Directory layout**
29+
30+
- `harness/` — the harness Rust crate (`harness`), a member of the
31+
repository-root workspace. `src/lib.rs` holds shared helpers; each
32+
`src/bin/*.rs` is an Antithesis test command named after its file. Run cargo
33+
from this directory; it is built, fmt'd, Clippy'd, and tested from the repo
34+
root via the usual `make check-all` / `make test`.
35+
- `deploy/` — all Antithesis/Docker infrastructure: the `Dockerfile`,
36+
`docker-compose.yaml`, and per-container build inputs grouped by service
37+
(`deploy/adp/`, `deploy/workload/`). This is the directory snouty consumes as
38+
`--config`; it contains `docker-compose.yaml` at its top. Snouty will push
39+
tagged images, consume this directory, and launch the run.
40+
41+
**scratchbook**
42+
43+
This directory is the Antithesis scratchbook for the codebase. It contains
44+
documents such as system analysis, property catalogs, topology plans,
45+
per-property evidence files (in `scratchbook/properties/`), property
46+
relationship maps, and other persistent integration notes. Keep it up to date as
47+
Antithesis-related decisions change.
48+
49+
**test templates** (`deploy/workload/test/`)
50+
51+
This directory contains test templates. A test template is a directory
52+
containing test command executable files. Each test command must have a valid
53+
prefix: `parallel_driver_, singleton_driver_, serial_driver_, first_,
54+
eventually_, finally_, anytime_`. Prefixes constrain when and how commands are
55+
composed in a single timeline. Files or subdirectories prefixed with `helper_`
56+
are ignored by Antithesis and can be used for helper scripts kept alongside the
57+
commands.
58+
59+
# Agent Behavior
60+
61+
Agent behavior will be governed by the following dictums:
62+
63+
- **The human is primary.** If you run into any confusion, pause and ask for
64+
clarification.
65+
- When you are faced with a choice between doing the right, time-consuming thing
66+
or the wrong, fast thing do the right thing.
67+
- Code is liability. The status quo is not worth preserving if it does not have
68+
utility. Be unsentimental and delete what is not needed.
69+
- **Truth over comfort.** Say what is true regardless of the presumed comfort of
70+
the receiver. Do not soften findings, hedge claims or omit bad news. To do so
71+
is _not kindness_. It is, rather, an insidious form of lie. Note that this
72+
dictum should be understood less in terms of Kim Scott's "Radical Candor" -- a
73+
gift from the elite to the undeserving common -- but more in Walter
74+
Brueggemann's "Prophetic Imagination" where truth erodes a "royal
75+
consciousness" that ablates one's ability to do new and interesting things
76+
_and_ shouts a path toward those new and interesting things, against the
77+
status quo. Consider in this same vein Tony Hoare's "The Emperor's Old
78+
Clothes".
79+
- **Honor the spirit of a request, not just its letter.** A "random string
80+
pool" requires actual variation. Returning `["foo", "bar"]` is technically
81+
a pool but a semantic mismatch. When the literal reading is unusually
82+
narrow or cheap, reach for the generous reading. Hostile compliance is
83+
worse than asking.

test/antithesis/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Antithesis Tests
2+
3+
This directory contains a sub-project to run Antithesis tests for the saluki
4+
project. Primary focus is on establishing that ADP and DogStatsD behave
5+
'equivalently', which is to say if ADP is dropped in for Datadog Agent's
6+
DogStatsD users will not notice shifts in their telemetry. Better operational
7+
behavior is acceptable deviation.
8+
9+
## Prerequisites
10+
11+
* snouty -- https://github.com/antithesishq/snouty
12+
* antithesis-skills + claude -- https://github.com/antithesishq/antithesis-skills
13+
14+
## Running Scenarios
15+
16+
This effort is extremely early. Today we assume claude drives scenarios runs,
17+
command it to do so with `/antithesis-launch`. In order for this to work you
18+
must already have credentials available. Eventually we will have CI rigged up to
19+
do nightly shots.

0 commit comments

Comments
 (0)