agent_data_plane_config/
control.rs

1//! Topology gates, orchestration decisions and application configuration.
2//!
3//! `ControlConfiguration` is read only by config-system and the topology builder, not by
4//! components. It carries pipeline activation gates, topology-shaping decisions, listen addresses,
5//! logging (read before topology exists), bootstrap IPC parameters, and process-lifecycle knobs.
6
7use std::{num::NonZeroUsize, path::PathBuf, time::Duration};
8
9use serde::Serialize;
10
11use crate::{defaults::DEFAULT_REMOTE_AGENT_STRING_INTERNER_SIZE_BYTES, ConfigValue};
12
13/// Topology gates and orchestration decisions. Most are static; `logging.level` is live.
14///
15/// The derived `Default` is all zeroes, empty, and `false`, and serves only as the starting point for translation. The
16/// effective default of each field is the one translation resolves, noted per field below.
17#[derive(Clone, Debug, Default, PartialEq, Serialize)]
18pub struct ControlConfiguration {
19    /// Master switch for the whole data plane; when false, no pipelines are built.
20    pub enabled: bool,
21
22    /// Whether the DogStatsD metrics pipeline is built.
23    pub dogstatsd: bool,
24
25    /// Whether the checks metrics pipeline is built. (not in Datadog Agent config schema)
26    pub checks: bool,
27
28    /// Whether the OTLP pipeline is built.
29    pub otlp: bool,
30
31    /// Whether standalone mode is active, running without a core Agent. (not in Datadog Agent
32    /// config schema)
33    pub standalone_mode: bool,
34
35    /// Whether the process registers itself with the core Agent as a remote agent.
36    pub remote_agent_enabled: bool,
37
38    /// Whether to subscribe to core Agent configuration updates over the newer config-stream
39    /// endpoint.
40    pub use_new_config_stream_endpoint: bool,
41
42    /// Address the unsecured control API listens on.
43    pub api_listen_address: String,
44
45    /// Address the mutually authenticated control API listens on. Every HTTP and gRPC client must
46    /// present the exact configured Agent IPC certificate during the TLS handshake.
47    pub secure_api_listen_address: String,
48
49    /// Logging configuration, read before runtime authority exists.
50    pub logging: Logging,
51
52    /// Bootstrap IPC and remote-agent connection parameters.
53    pub ipc: ControlIpc,
54
55    /// Grace period the aggregator is given to flush before shutdown.
56    pub aggregator_stop_timeout: Duration,
57
58    /// Override for the topology shutdown grace period.
59    ///
60    /// Defaults to `None`. When absent, the topology timeout is the sum of
61    /// `aggregator_stop_timeout` and `forwarder_stop_timeout`.
62    pub stop_timeout: Option<Duration>,
63
64    /// Process memory ceiling, in bytes, that bounds validation and the global limiter work against.
65    ///
66    /// Defaults to `None`. When absent, ADP reads the ceiling from the process cgroup, but only when `DOCKER_DD_AGENT`
67    /// is set to a non-empty value. When neither source supplies a value, bounds validation is skipped and the global
68    /// limiter never exerts backpressure, whatever `memory_mode` and `enable_global_limiter` say.
69    ///
70    /// `Some(0)` is a ceiling of zero bytes rather than "no ceiling": every component bound then exceeds it, which is
71    /// fatal under [`MemoryMode::Strict`]. A ceiling above 2^53 bytes is rejected during startup.
72    ///
73    /// Set this to the memory the process is allowed to use, and leave it unset only where cgroup detection supplies
74    /// that number.
75    pub memory_limit: Option<u64>,
76
77    /// Fraction of `memory_limit` held back as headroom for memory the component bounds do not account for.
78    ///
79    /// Defaults to [`DEFAULT_MEMORY_SLOP_FACTOR`](crate::defaults::DEFAULT_MEMORY_SLOP_FACTOR) (`0.25`), which
80    /// validates bounds against 75% of `memory_limit`. Valid values run from `0.0` up to but excluding `1.0`, where
81    /// `0.0` holds nothing back. A value outside that range, including `NaN`, fails startup once a memory ceiling
82    /// resolves, and goes unused when none does.
83    ///
84    /// Raise this for a workload whose real usage overshoots its validated bounds; lower it to hand more of a tight
85    /// ceiling to the components that do account for their usage.
86    pub memory_slop_factor: f64,
87
88    /// Whether the global memory limiter exerts backpressure as usage approaches the effective ceiling.
89    ///
90    /// Defaults to [`DEFAULT_ENABLE_GLOBAL_LIMITER`](crate::defaults::DEFAULT_ENABLE_GLOBAL_LIMITER) (`true`). When
91    /// `false`, the limiter is a no-op: it throttles nothing, and only the components' own bounds hold memory usage
92    /// down. Either way it does nothing unless a memory ceiling resolves and `memory_mode` is
93    /// [`MemoryMode::Permissive`] or [`MemoryMode::Strict`], because no other case installs a limiter.
94    ///
95    /// Turn this off to attribute a throughput drop to memory backpressure, accepting that the process can then run
96    /// past `memory_limit`.
97    pub enable_global_limiter: bool,
98
99    /// How the component memory bounds are reconciled with the effective memory ceiling during startup.
100    ///
101    /// Defaults to [`MemoryMode::Disabled`]. Validation runs only when a memory ceiling resolves; without one,
102    /// `Permissive` and `Strict` log that validation was skipped and startup continues.
103    ///
104    /// Run `Permissive` first to learn whether a ceiling fits the topology, then move to `Strict` where the platform
105    /// kills a process that exceeds its ceiling and refusing to start is the better failure.
106    pub memory_mode: MemoryMode,
107}
108
109/// Memory bounds validation and limiter behavior.
110#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
111#[serde(rename_all = "lowercase")]
112pub enum MemoryMode {
113    /// Bounds validation is skipped and no limiter is installed, whatever `enable_global_limiter` says.
114    #[default]
115    Disabled,
116
117    /// Bounds that do not fit the ceiling are logged as a warning and startup continues.
118    ///
119    /// Memory limiting is best effort: the limiter is installed when a ceiling resolves and
120    /// `enable_global_limiter` is `true`.
121    Permissive,
122
123    /// Bounds that do not fit the ceiling fail startup.
124    ///
125    /// The limiter is installed on the same terms as [`MemoryMode::Permissive`].
126    Strict,
127}
128
129impl ControlConfiguration {
130    /// Derived decision the topology builder reads. The outbound Datadog forwarder is needed only
131    /// if some pipeline that emits to Datadog is enabled.
132    pub fn requires_datadog_forwarder(&self) -> bool {
133        self.dogstatsd || self.checks || self.otlp
134    }
135}
136
137/// Logging configuration, read before runtime authority exists.
138#[derive(Clone, Debug, Default, PartialEq, Serialize)]
139pub struct Logging {
140    /// Minimum severity a record must reach to be emitted.
141    pub level: String,
142
143    /// Whether log timestamps are formatted as RFC 3339.
144    pub format_rfc3339: bool,
145
146    /// Whether log records are emitted as JSON.
147    pub format_json: bool,
148
149    /// Whether logs are written to the console.
150    pub to_console: bool,
151
152    /// Whether logs are forwarded to syslog.
153    pub to_syslog: bool,
154
155    /// Whether syslog messages use the RFC 5424 framing.
156    pub syslog_rfc: bool,
157
158    /// Destination URI for syslog forwarding.
159    pub syslog_uri: String,
160
161    /// Path of the log file.
162    ///
163    /// A defaulted or explicitly empty path selects the platform-specific ADP log file path.
164    pub file: ConfigValue<String>,
165
166    /// Whether file logging is turned off entirely.
167    pub disable_file_logging: bool,
168
169    /// Number of rotated log files retained.
170    ///
171    /// Defaults to `1`. The file writer retains one rotated file when this is `0`. A negative value is
172    /// rejected during translation.
173    pub file_max_rolls: usize,
174
175    /// Maximum size, in bytes, a log file reaches before it is rotated.
176    ///
177    /// When defaulted, the logging stack keeps its own 10 MiB threshold instead.
178    pub file_max_size: ConfigValue<u64>,
179}
180
181/// IPC and remote-agent connection parameters, read once at bootstrap before runtime authority
182/// exists and again from the authoritative configuration once it does.
183///
184/// Witnessed fields get their effective defaults during translation. The Saluki-only interner
185/// budget uses its Rust `Default`.
186#[derive(Clone, Debug, PartialEq, Serialize)]
187pub struct ControlIpc {
188    /// Path to the Agent authentication token file.
189    ///
190    /// ADP sends the file contents as a bearer token to the Core Agent. Override this path only when the Core Agent
191    /// uses a non-default token path, and configure both processes to use the same token.
192    ///
193    /// Defaults to an empty path, which selects the platform-specific Agent authentication token path.
194    pub auth_token_file_path: PathBuf,
195
196    /// Path to the shared Agent IPC mTLS identity file.
197    ///
198    /// The PEM file contains the certificate and private key used by ADP and its IPC peers. Every peer must use the
199    /// same identity because authentication requires an exact certificate match. Override this path only when the Core
200    /// Agent uses a non-default identity path.
201    ///
202    /// Defaults to an empty path, which selects `ipc_cert.pem` beside the resolved authentication token path.
203    pub ipc_cert_file_path: PathBuf,
204
205    /// TCP port the command API listens on.
206    ///
207    /// Defaults to `5001`.
208    pub cmd_port: u16,
209
210    /// vsock address used for guest/host IPC.
211    ///
212    /// Defaults to empty, which reaches the Core Agent over TCP on localhost at `cmd_port`.
213    pub vsock_addr: String,
214
215    /// Maximum gRPC message size, in bytes, accepted over the remote-agent IPC channel.
216    ///
217    /// Defaults to `134217728` (128 MiB).
218    pub grpc_max_message_size: usize,
219
220    /// Byte budget for the remote-agent workload metadata string interner. (not in Datadog Agent config schema)
221    ///
222    /// The workload provider interns entity IDs and tags into a single allocation of this size, taken at startup and
223    /// charged in full against the memory bounds ceiling. A workload whose tag cardinality outgrows the budget starts
224    /// failing to intern, which drops the affected tags and entity updates and increments the collectors'
225    /// `intern_failed_total` counters.
226    ///
227    /// Defaults to `524288` (512 KiB). An explicit `0` fails the configuration load. Change it when tuning ADP itself;
228    /// operators are not expected to.
229    pub remote_agent_string_interner_size_bytes: NonZeroUsize,
230}
231
232impl Default for ControlIpc {
233    fn default() -> Self {
234        Self {
235            // Written by the Datadog witness driver.
236            auth_token_file_path: PathBuf::new(),
237            ipc_cert_file_path: PathBuf::new(),
238            cmd_port: 0,
239            vsock_addr: String::new(),
240            grpc_max_message_size: 0,
241            remote_agent_string_interner_size_bytes: DEFAULT_REMOTE_AGENT_STRING_INTERNER_SIZE_BYTES,
242        }
243    }
244}