@@ -117,10 +117,55 @@ impl DogStatsdConfig {
117117/// unset. The current value caps the preallocation at 512 MiB.
118118const 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 ) ]
125170pub 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.
179239const STATIC_YAML_TAIL : & str = "use_dogstatsd: true
180- use_v2_api_series: true
181240inventories_enabled: false
182241enable_metadata_collection: false
183242cloud_provider_metadata: []
@@ -239,6 +298,7 @@ impl DriverConfig {
239298
240299#[ cfg( test) ]
241300mod 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}
0 commit comments