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}