Skip to content

Commit 5974b21

Browse files
committed
chore(antithesis): Intro Intake V3
This commit introduces a v3 intake into antithesis harness to support symmetric difference with ADP-on -- v2 by default -- and ADP-off -- v3 by default.
1 parent ca4fe36 commit 5974b21

27 files changed

Lines changed: 3457 additions & 415 deletions

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/antithesis/harness/src/config.rs

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,55 @@ impl DogStatsdConfig {
117117
/// unset. The current value caps the preallocation at 512 MiB.
118118
const MAX_STRING_INTERNER_ENTRIES: u64 = 1_048_576;
119119

120+
/// Compressor both targets serialize metric payloads with.
121+
#[derive(Debug, Clone, Copy, Serialize)]
122+
#[serde(rename_all = "lowercase")]
123+
pub(crate) enum CompressorKind {
124+
/// Deflate. Disables the v3 series intake on both targets.
125+
Zlib,
126+
/// Zstandard.
127+
Zstd,
128+
/// Gzip.
129+
Gzip,
130+
/// No compression, the Agent's `NoneKind`.
131+
None,
132+
/// A codec neither target implements, so each falls back its own way and the two lanes disagree
133+
/// on the wire from one config value.
134+
Snappy,
135+
}
136+
137+
impl Distribution<CompressorKind> for StandardUniform {
138+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> CompressorKind {
139+
match rng.random_range(0..5u8) {
140+
0 => CompressorKind::Zlib,
141+
1 => CompressorKind::Zstd,
142+
2 => CompressorKind::Gzip,
143+
3 => CompressorKind::None,
144+
_ => CompressorKind::Snappy,
145+
}
146+
}
147+
}
148+
149+
/// The Agent's nested `use_v3_api.series` switch between the v2 and v3 series
150+
/// intake.
151+
#[derive(Debug, Serialize)]
152+
pub(crate) struct UseV3ApiConfig {
153+
/// The series sub-tree.
154+
series: V3SeriesConfig,
155+
}
156+
157+
/// The `enabled` leaf under a v3 series key.
158+
#[derive(Debug, Serialize)]
159+
pub(crate) struct V3SeriesConfig {
160+
/// Whether the series intake is v3 rather than v2. A string because both the Agent and ADP read
161+
/// this leaf as one, and ADP's typed model rejects a YAML boolean outright.
162+
enabled: &'static str,
163+
}
164+
120165
/// Agent-facing config. `hostname`, `api_key`, `dd_url`, and the socket are
121-
/// supplied by the environment; `log_level` and the `DogStatsD` options are
122-
/// sampled per branch. The static flags are appended by [`Self::to_yaml`], not
123-
/// fields here.
166+
/// supplied by the environment; `log_level`, the series intake API, and the
167+
/// `DogStatsD` options are sampled per branch. The static flags are appended by
168+
/// [`Self::to_yaml`], not fields here.
124169
#[derive(Debug, Serialize)]
125170
pub struct DatadogConfig {
126171
/// Agent hostname. Supplied by the environment. ADP requires it
@@ -132,6 +177,13 @@ pub struct DatadogConfig {
132177
dd_url: String,
133178
/// Agent log verbosity. Pinned to `error` (see [`LogLevel`]).
134179
log_level: LogLevel,
180+
/// Series intake API for this timeline.
181+
use_v3_api: UseV3ApiConfig,
182+
/// Compressor for metric payloads. Sampled independently of the series API.
183+
serializer_compressor_kind: CompressorKind,
184+
/// ADP's safety gate for authoritative v3 series, which the Agent has no counterpart for.
185+
/// Sampled with [`Self::use_v3_api`] so ADP and the Agent never split encodings in a timeline.
186+
data_plane_metrics_v3_series_enabled: bool,
135187
/// `DogStatsD` options, flattened to top-level `dogstatsd_*` keys.
136188
#[serde(flatten)]
137189
dogstatsd: DogStatsdConfig,
@@ -145,11 +197,19 @@ impl DatadogConfig {
145197
pub fn sample<R: Rng + ?Sized>(
146198
rng: &mut R, hostname: &str, api_key: &str, dd_url: &str, dogstatsd_socket: &Path,
147199
) -> Self {
200+
let series_v3 = rng.random();
148201
Self {
149202
hostname: hostname.to_owned(),
150203
api_key: api_key.to_owned(),
151204
dd_url: dd_url.to_owned(),
152205
log_level: LogLevel::Error,
206+
use_v3_api: UseV3ApiConfig {
207+
series: V3SeriesConfig {
208+
enabled: if series_v3 { "true" } else { "false" },
209+
},
210+
},
211+
serializer_compressor_kind: rng.random(),
212+
data_plane_metrics_v3_series_enabled: series_v3,
153213
dogstatsd: DogStatsdConfig::sample(rng, dogstatsd_socket),
154214
}
155215
}
@@ -177,7 +237,6 @@ impl DatadogConfig {
177237

178238
/// Yaml flags the Agent reads at boot that never vary.
179239
const STATIC_YAML_TAIL: &str = "use_dogstatsd: true
180-
use_v2_api_series: true
181240
inventories_enabled: false
182241
enable_metadata_collection: false
183242
cloud_provider_metadata: []
@@ -239,6 +298,7 @@ impl DriverConfig {
239298

240299
#[cfg(test)]
241300
mod tests {
301+
use std::collections::BTreeSet;
242302
use std::convert::Infallible;
243303

244304
use rand::rand_core::TryRng;
@@ -303,4 +363,60 @@ mod tests {
303363
assert!(has_key(&render(0), "log_level"));
304364
assert!(render(0).contains("log_level: error"));
305365
}
366+
367+
/// The Agent's nested switch and ADP's safety gate, as a timeline renders them.
368+
fn series_api(seed: u64) -> (bool, bool) {
369+
let yaml = render(seed);
370+
let parsed: serde_yaml::Value = serde_yaml::from_str(&yaml).expect("parse rendered yaml");
371+
// A string, not a YAML boolean: ADP's typed model reads this leaf as `String`, so an unquoted
372+
// boolean fails deserialization and the target never boots.
373+
let agent = match parsed["use_v3_api"]["series"]["enabled"]
374+
.as_str()
375+
.expect("use_v3_api.series.enabled is a string")
376+
{
377+
"true" => true,
378+
"false" => false,
379+
other => panic!("unexpected series mode {other}"),
380+
};
381+
let adp = parsed["data_plane_metrics_v3_series_enabled"]
382+
.as_bool()
383+
.expect("data_plane_metrics_v3_series_enabled");
384+
(agent, adp)
385+
}
386+
387+
#[test]
388+
fn both_lanes_share_one_series_api() {
389+
for seed in 0..16 {
390+
let (agent, adp) = series_api(seed);
391+
assert_eq!(agent, adp, "seed {seed}");
392+
}
393+
}
394+
395+
#[test]
396+
fn series_api_samples_both_intakes() {
397+
let mut seen = [false, false];
398+
for seed in 0..16 {
399+
seen[usize::from(series_api(seed).0)] = true;
400+
}
401+
assert_eq!(seen, [true, true]);
402+
}
403+
404+
#[test]
405+
fn compressor_samples_every_kind() {
406+
let mut seen = BTreeSet::new();
407+
for seed in 0..64 {
408+
let yaml = render(seed);
409+
let kind = yaml
410+
.lines()
411+
.find_map(|line| line.strip_prefix("serializer_compressor_kind: "))
412+
.expect("serializer_compressor_kind")
413+
.to_owned();
414+
seen.insert(kind);
415+
}
416+
let want = ["gzip", "none", "snappy", "zlib", "zstd"]
417+
.into_iter()
418+
.map(str::to_owned)
419+
.collect::<BTreeSet<_>>();
420+
assert_eq!(seen, want);
421+
}
306422
}

test/antithesis/intake/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ axum = { workspace = true, features = ["http1", "json", "tokio", "tracing"] }
2020
clap = { workspace = true, features = ["derive", "env", "error-context", "help", "std", "usage"] }
2121
datadog-protos = { workspace = true }
2222
headers = { workspace = true }
23+
http-body-util = { workspace = true }
2324
mime = { workspace = true }
2425
protobuf = { workspace = true }
2526
serde = { workspace = true, features = ["derive"] }
2627
serde_json = { workspace = true }
27-
stele = { workspace = true }
28+
serde_yaml = { workspace = true }
2829
tokio = { workspace = true, features = [
30+
"io-util",
2931
"macros",
3032
"net",
3133
"rt",

0 commit comments

Comments
 (0)