Skip to main content

zlayer_core/
config.rs

1//! Configuration structures for `ZLayer`
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5use std::time::Duration;
6
7/// Main agent configuration
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
9pub struct AgentConfig {
10    /// Deployment name this agent belongs to
11    pub deployment_name: String,
12
13    /// Unique node identifier
14    pub node_id: String,
15
16    /// Raft consensus configuration
17    #[serde(default)]
18    pub raft: RaftConfig,
19
20    /// Overlay network configuration
21    #[serde(default)]
22    pub overlay: OverlayAgentConfig,
23
24    /// Data directory for state persistence
25    #[serde(default = "default_data_dir")]
26    pub data_dir: PathBuf,
27
28    /// Metrics configuration
29    #[serde(default)]
30    pub metrics: MetricsConfig,
31
32    /// Logging configuration
33    #[serde(default)]
34    pub logging: LoggingConfig,
35
36    /// Registry authentication configuration
37    #[serde(default)]
38    pub auth: crate::auth::AuthConfig,
39}
40
41fn default_data_dir() -> PathBuf {
42    zlayer_paths::ZLayerDirs::default_data_dir()
43}
44
45/// Raft consensus configuration
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
47pub struct RaftConfig {
48    /// Address to bind for Raft RPC
49    pub raft_addr: String,
50
51    /// Advertised address for other nodes to connect
52    pub advertise_addr: Option<String>,
53
54    /// Snapshot threshold (number of logs before snapshot)
55    #[serde(default = "default_snapshot_threshold")]
56    pub snapshot_threshold: u64,
57
58    /// Snapshot policy (logs since last snapshot)
59    #[serde(default = "default_snapshot_policy_count")]
60    pub snapshot_policy_count: u64,
61
62    /// Election timeout minimum
63    #[serde(default = "default_election_timeout_min")]
64    pub election_timeout_min: u64,
65
66    /// Election timeout maximum
67    #[serde(default = "default_election_timeout_max")]
68    pub election_timeout_max: u64,
69
70    /// Heartbeat interval
71    #[serde(default = "default_heartbeat_interval")]
72    pub heartbeat_interval: u64,
73}
74
75impl Default for RaftConfig {
76    fn default() -> Self {
77        Self {
78            raft_addr: "0.0.0.0:27001".to_string(),
79            advertise_addr: None,
80            snapshot_threshold: default_snapshot_threshold(),
81            snapshot_policy_count: default_snapshot_policy_count(),
82            election_timeout_min: default_election_timeout_min(),
83            election_timeout_max: default_election_timeout_max(),
84            heartbeat_interval: default_heartbeat_interval(),
85        }
86    }
87}
88
89fn default_snapshot_threshold() -> u64 {
90    10000
91}
92
93fn default_snapshot_policy_count() -> u64 {
94    8000
95}
96
97fn default_election_timeout_min() -> u64 {
98    150
99}
100
101fn default_election_timeout_max() -> u64 {
102    300
103}
104
105fn default_heartbeat_interval() -> u64 {
106    50
107}
108
109/// Overlay network configuration for agents
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
111pub struct OverlayAgentConfig {
112    /// Overlay private key (x25519)
113    pub private_key: String,
114
115    /// Overlay public key (derived from private key)
116    pub public_key: Option<String>,
117
118    /// Listen port for overlay network (`WireGuard` protocol)
119    #[serde(default = "default_wg_port")]
120    pub wg_port: u16,
121
122    /// Global overlay network configuration
123    #[serde(default)]
124    pub global: GlobalOverlayConfig,
125
126    /// DNS configuration
127    #[serde(default)]
128    pub dns: DnsConfig,
129}
130
131impl Default for OverlayAgentConfig {
132    fn default() -> Self {
133        Self {
134            private_key: String::new(),
135            public_key: None,
136            wg_port: default_wg_port(),
137            global: GlobalOverlayConfig::default(),
138            dns: DnsConfig::default(),
139        }
140    }
141}
142
143/// Default overlay listen port (`WireGuard` protocol).
144pub const DEFAULT_WG_PORT: u16 = 51420;
145
146fn default_wg_port() -> u16 {
147    DEFAULT_WG_PORT
148}
149
150/// Global overlay network configuration
151#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
152pub struct GlobalOverlayConfig {
153    /// Overlay network CIDR (e.g., "10.0.0.0/8")
154    #[serde(default = "default_overlay_cidr")]
155    pub overlay_cidr: String,
156
157    /// Peer discovery interval
158    #[serde(default = "default_peer_discovery")]
159    pub peer_discovery_interval: Duration,
160}
161
162impl Default for GlobalOverlayConfig {
163    fn default() -> Self {
164        Self {
165            overlay_cidr: default_overlay_cidr(),
166            peer_discovery_interval: default_peer_discovery(),
167        }
168    }
169}
170
171fn default_overlay_cidr() -> String {
172    "10.0.0.0/8".to_string()
173}
174
175fn default_peer_discovery() -> Duration {
176    Duration::from_secs(30)
177}
178
179/// DNS configuration
180#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
181pub struct DnsConfig {
182    /// DNS listen address
183    #[serde(default = "default_dns_addr")]
184    pub listen_addr: String,
185
186    /// DNS TLD for global overlay
187    #[serde(default = "default_global_tld")]
188    pub global_tld: String,
189
190    /// DNS TLD for service-scoped overlay
191    #[serde(default = "default_service_tld")]
192    pub service_tld: String,
193}
194
195impl Default for DnsConfig {
196    fn default() -> Self {
197        Self {
198            listen_addr: default_dns_addr(),
199            global_tld: default_global_tld(),
200            service_tld: default_service_tld(),
201        }
202    }
203}
204
205fn default_dns_addr() -> String {
206    "0.0.0.0:53".to_string()
207}
208
209fn default_global_tld() -> String {
210    "global".to_string()
211}
212
213fn default_service_tld() -> String {
214    "service".to_string()
215}
216
217/// Metrics configuration
218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
219pub struct MetricsConfig {
220    /// Enable metrics collection
221    #[serde(default = "default_metrics_enabled")]
222    pub enabled: bool,
223
224    /// Metrics exposition address
225    #[serde(default = "default_metrics_addr")]
226    pub listen_addr: String,
227
228    /// Metrics path
229    #[serde(default = "default_metrics_path")]
230    pub path: String,
231}
232
233impl Default for MetricsConfig {
234    fn default() -> Self {
235        Self {
236            enabled: default_metrics_enabled(),
237            listen_addr: default_metrics_addr(),
238            path: default_metrics_path(),
239        }
240    }
241}
242
243fn default_metrics_enabled() -> bool {
244    true
245}
246
247fn default_metrics_addr() -> String {
248    "0.0.0.0:9090".to_string()
249}
250
251fn default_metrics_path() -> String {
252    "/metrics".to_string()
253}
254
255/// Logging configuration
256#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
257pub struct LoggingConfig {
258    /// Log level (trace, debug, info, warn, error)
259    #[serde(default = "default_log_level")]
260    pub level: String,
261
262    /// Log format (json, pretty)
263    #[serde(default = "default_log_format")]
264    pub format: String,
265
266    /// Log to stdout
267    #[serde(default = "default_log_stdout")]
268    pub stdout: bool,
269
270    /// Log file path
271    pub file: Option<PathBuf>,
272}
273
274impl Default for LoggingConfig {
275    fn default() -> Self {
276        Self {
277            level: default_log_level(),
278            format: default_log_format(),
279            stdout: default_log_stdout(),
280            file: None,
281        }
282    }
283}
284
285fn default_log_level() -> String {
286    "info".to_string()
287}
288
289fn default_log_format() -> String {
290    "json".to_string()
291}
292
293fn default_log_stdout() -> bool {
294    true
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300
301    #[test]
302    fn test_agent_config_default() {
303        let config = AgentConfig {
304            deployment_name: "test".to_string(),
305            node_id: "node-1".to_string(),
306            ..Default::default()
307        };
308        assert_eq!(config.deployment_name, "test");
309        assert_eq!(config.node_id, "node-1");
310    }
311}