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