slim_controller/
config.rs1use serde::Deserialize;
5
6use slim_config::client::ClientConfig;
7use slim_config::component::configuration::Configuration;
8use slim_config::pqc::EnforcePqcPolicy;
9use slim_config::server::ServerConfig;
10use slim_datapath::message_processing::MessageProcessor;
11
12use crate::errors::ControllerError;
13use crate::service::{ControlPlane, ControlPlaneSettings, from_server_config};
14
15#[derive(Debug, Clone, Deserialize, Default, PartialEq)]
17#[serde(deny_unknown_fields)]
18pub struct Config {
19 #[serde(default)]
21 pub enforce_pqc: EnforcePqcPolicy,
22
23 #[serde(default)]
25 pub servers: Vec<ServerConfig>,
26
27 #[serde(default)]
29 pub clients: Vec<ClientConfig>,
30
31 #[serde(default)]
33 pub outbound_clients: Vec<ClientConfig>,
34}
35
36impl Config {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn is_default(&self) -> bool {
43 self == &Self::default()
44 }
45
46 pub fn enforce_pqc(&self) -> EnforcePqcPolicy {
48 self.enforce_pqc
49 }
50
51 pub fn normalize_pqc(&mut self) -> Result<(), ControllerError> {
53 self.apply_pqc_policy(self.enforce_pqc)?;
54 Ok(())
55 }
56
57 fn check_pqc_policy(&self) -> Result<(), ControllerError> {
58 self.validate_pqc_for_endpoints(self.enforce_pqc)
59 }
60
61 fn apply_pqc_policy(&mut self, policy: EnforcePqcPolicy) -> Result<(), ControllerError> {
62 for server in &mut self.servers {
63 server.tls_setting.config.enforce_pqc = policy.is_enforced();
64 policy
65 .validate_tls_version(&server.tls_setting.config.tls_version)
66 .map_err(|e| ControllerError::ConfigError(e.into()))?;
67 }
68 for client in &mut self.clients {
69 client.tls_setting.config.enforce_pqc = policy.is_enforced();
70 policy
71 .validate_tls_version(&client.tls_setting.config.tls_version)
72 .map_err(|e| ControllerError::ConfigError(e.into()))?;
73 }
74 for client in &mut self.outbound_clients {
75 client.tls_setting.config.enforce_pqc = policy.is_enforced();
76 policy
77 .validate_tls_version(&client.tls_setting.config.tls_version)
78 .map_err(|e| ControllerError::ConfigError(e.into()))?;
79 }
80 Ok(())
81 }
82
83 fn validate_pqc_for_endpoints(&self, policy: EnforcePqcPolicy) -> Result<(), ControllerError> {
84 for server in &self.servers {
85 policy
86 .validate_tls_version(&server.tls_setting.config.tls_version)
87 .map_err(|e| ControllerError::ConfigError(e.into()))?;
88 }
89 for client in &self.clients {
90 policy
91 .validate_tls_version(&client.tls_setting.config.tls_version)
92 .map_err(|e| ControllerError::ConfigError(e.into()))?;
93 }
94 for client in &self.outbound_clients {
95 policy
96 .validate_tls_version(&client.tls_setting.config.tls_version)
97 .map_err(|e| ControllerError::ConfigError(e.into()))?;
98 }
99 Ok(())
100 }
101
102 pub fn with_servers(self, servers: Vec<ServerConfig>) -> Self {
104 Self { servers, ..self }
105 }
106
107 pub fn with_clients(self, clients: Vec<ClientConfig>) -> Self {
109 Self { clients, ..self }
110 }
111
112 pub fn servers(&self) -> &[ServerConfig] {
114 &self.servers
115 }
116
117 pub fn clients(&self) -> &[ClientConfig] {
119 &self.clients
120 }
121
122 pub fn into_service(
124 &self,
125 node_id: String,
126 domain_name: Option<String>,
127 message_processor: MessageProcessor,
128 dataplane_servers: &[ServerConfig],
132 dataplane_clients: &[ClientConfig],
137 auth_provider: Option<slim_auth::auth_provider::AuthProvider>,
138 ) -> ControlPlane {
139 let connection_details = dataplane_servers.iter().map(from_server_config).collect();
140
141 ControlPlane::new(ControlPlaneSettings {
142 id: node_id,
143 domain_name,
144 servers: self.servers.clone(),
145 clients: self.clients.clone(),
146 outbound_clients: self.outbound_clients.clone(),
147 dataplane_clients: dataplane_clients.to_vec(),
148 message_processor,
149 connection_details,
150 auth_provider,
151 })
152 }
153}
154
155impl Configuration for Config {
156 type Error = ControllerError;
157
158 fn validate(&self) -> Result<(), Self::Error> {
159 self.check_pqc_policy()?;
160
161 for server in self.servers.iter() {
163 server.validate()?;
164 }
165
166 for client in &self.clients {
167 client.validate()?;
168 }
169
170 Ok(())
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use slim_config::server::ServerConfig;
178 use slim_datapath::message_processing::MessageProcessor;
179
180 fn create_test_server_config() -> ServerConfig {
181 ServerConfig::with_endpoint("127.0.0.1:50051")
182 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure())
183 }
184
185 fn create_test_client_config() -> ClientConfig {
186 ClientConfig::with_endpoint("http://127.0.0.1:50051")
187 .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure())
188 }
189
190 fn server_with_pqc(enforced: bool) -> ServerConfig {
191 let mut server = create_test_server_config();
192 server.tls_setting.config.enforce_pqc = enforced;
193 server
194 }
195
196 #[test]
197 fn test_normalize_pqc_propagates_to_tls_runtime_config() {
198 let mut config = Config {
199 enforce_pqc: EnforcePqcPolicy::enforced(),
200 servers: vec![server_with_pqc(false)],
201 ..Config::default()
202 };
203
204 config.normalize_pqc().unwrap();
205
206 assert!(config.enforce_pqc().is_enforced());
207 assert!(config.servers[0].tls_setting.config.enforce_pqc);
208 }
209
210 #[test]
211 fn test_config_new() {
212 let config = Config::new();
213 assert!(config.servers.is_empty());
214 assert!(config.clients.is_empty());
215 }
216
217 #[test]
218 fn test_config_default() {
219 let config = Config::default();
220 assert!(config.servers.is_empty());
221 assert!(config.clients.is_empty());
222 }
223
224 #[test]
225 fn test_config_with_servers() {
226 let server_config = create_test_server_config();
227 let config = Config::new().with_servers(vec![server_config.clone()]);
228
229 assert_eq!(config.servers.len(), 1);
230 assert_eq!(config.servers[0], server_config);
231 assert!(config.clients.is_empty());
232 }
233
234 #[test]
235 fn test_config_with_clients() {
236 let client_config = create_test_client_config();
237 let config = Config::new().with_clients(vec![client_config.clone()]);
238
239 assert_eq!(config.clients.len(), 1);
240 assert_eq!(config.clients[0], client_config);
241 assert!(config.servers.is_empty());
242 }
243
244 #[test]
245 fn test_config_servers_getter() {
246 let server_config = create_test_server_config();
247 let config = Config::new().with_servers(vec![server_config.clone()]);
248
249 let servers = config.servers();
250 assert_eq!(servers.len(), 1);
251 assert_eq!(servers[0], server_config);
252 }
253
254 #[test]
255 fn test_config_clients_getter() {
256 let client_config = create_test_client_config();
257 let config = Config::new().with_clients(vec![client_config.clone()]);
258
259 let clients = config.clients();
260 assert_eq!(clients.len(), 1);
261 assert_eq!(clients[0], client_config);
262 }
263
264 #[test]
265 fn test_config_chaining() {
266 let server_config = create_test_server_config();
267 let client_config = create_test_client_config();
268
269 let config = Config::new()
270 .with_servers(vec![server_config.clone()])
271 .with_clients(vec![client_config.clone()]);
272
273 assert_eq!(config.servers.len(), 1);
274 assert_eq!(config.clients.len(), 1);
275 }
276
277 #[test]
278 fn test_config_validate_empty() {
279 let config = Config::new();
280 assert!(config.validate().is_ok());
281 }
282
283 #[test]
284 fn test_config_validate_with_valid_servers_and_clients() {
285 let server_config = create_test_server_config();
286 let client_config = create_test_client_config();
287 let config = Config::new()
288 .with_servers(vec![server_config])
289 .with_clients(vec![client_config]);
290
291 assert!(config.validate().is_ok());
292 }
293
294 #[test]
295 fn test_config_clone() {
296 let server_config = create_test_server_config();
297 let client_config = create_test_client_config();
298
299 let config1 = Config::new()
300 .with_servers(vec![server_config])
301 .with_clients(vec![client_config]);
302
303 let config2 = config1.clone();
304
305 assert_eq!(config1.servers, config2.servers);
306 assert_eq!(config1.clients, config2.clients);
307 }
308
309 #[tokio::test]
310 async fn test_config_into_service() {
311 let server_config = create_test_server_config();
312 let client_config = create_test_client_config();
313
314 let config = Config::new()
315 .with_servers(vec![server_config.clone()])
316 .with_clients(vec![client_config]);
317
318 let domain_name = Some("test-domain".to_string());
319 let message_processor = MessageProcessor::new();
320
321 let _control_plane = config.into_service(
322 "test-instance".to_string(),
323 domain_name,
324 message_processor,
325 &[server_config],
326 &[],
327 None,
328 );
329 }
330
331 #[test]
332 fn test_config_debug_trait() {
333 let config = Config::new();
334 let debug_str = format!("{:?}", config);
335 assert!(debug_str.contains("Config"));
336 assert!(debug_str.contains("servers"));
337 assert!(debug_str.contains("clients"));
338 }
339
340 #[test]
341 fn test_config_validate_with_multiple_servers() {
342 let server1 = create_test_server_config();
343 let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
344 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
345
346 let config = Config::new().with_servers(vec![server1, server2]);
347 assert!(config.validate().is_ok());
348 }
349
350 #[test]
351 fn test_config_validate_with_multiple_clients() {
352 let client1 = create_test_client_config();
353 let client2 = ClientConfig::with_endpoint("http://127.0.0.1:50052")
354 .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure());
355
356 let config = Config::new().with_clients(vec![client1, client2]);
357 assert!(config.validate().is_ok());
358 }
359
360 #[test]
361 fn test_config_partial_eq() {
362 let config1 = Config::new();
363 let config2 = Config::new();
364
365 assert_eq!(config1, config2);
366
367 let server_config = create_test_server_config();
368 let config3 = config1.clone().with_servers(vec![server_config]);
369
370 assert_ne!(config1, config3);
371 }
372
373 #[test]
374 fn test_config_builder_pattern_reuse() {
375 let base_config = Config::new();
376
377 let config1 = base_config
378 .clone()
379 .with_servers(vec![create_test_server_config()]);
380 let config2 = base_config
381 .clone()
382 .with_clients(vec![create_test_client_config()]);
383
384 assert!(base_config.servers.is_empty());
385 assert!(base_config.clients.is_empty());
386
387 assert_eq!(config1.servers.len(), 1);
388 assert!(config1.clients.is_empty());
389
390 assert!(config2.servers.is_empty());
391 assert_eq!(config2.clients.len(), 1);
392 }
393
394 #[test]
395 fn test_config_overwrite_behavior() {
396 let server1 = create_test_server_config();
397 let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
398 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
399
400 let config = Config::new()
401 .with_servers(vec![server1])
402 .with_servers(vec![server2.clone()]);
403
404 assert_eq!(config.servers.len(), 1);
405 assert_eq!(config.servers[0], server2);
406 }
407}