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 auth_provider: Option<slim_auth::auth_provider::AuthProvider>,
133 ) -> ControlPlane {
134 let connection_details = dataplane_servers.iter().map(from_server_config).collect();
135
136 ControlPlane::new(ControlPlaneSettings {
137 id: node_id,
138 domain_name,
139 servers: self.servers.clone(),
140 clients: self.clients.clone(),
141 outbound_clients: self.outbound_clients.clone(),
142 message_processor,
143 connection_details,
144 auth_provider,
145 })
146 }
147}
148
149impl Configuration for Config {
150 type Error = ControllerError;
151
152 fn validate(&self) -> Result<(), Self::Error> {
153 self.check_pqc_policy()?;
154
155 for server in self.servers.iter() {
157 server.validate()?;
158 }
159
160 for client in &self.clients {
161 client.validate()?;
162 }
163
164 Ok(())
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use slim_config::server::ServerConfig;
172 use slim_datapath::message_processing::MessageProcessor;
173
174 fn create_test_server_config() -> ServerConfig {
175 ServerConfig::with_endpoint("127.0.0.1:50051")
176 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure())
177 }
178
179 fn create_test_client_config() -> ClientConfig {
180 ClientConfig::with_endpoint("http://127.0.0.1:50051")
181 .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure())
182 }
183
184 fn server_with_pqc(enforced: bool) -> ServerConfig {
185 let mut server = create_test_server_config();
186 server.tls_setting.config.enforce_pqc = enforced;
187 server
188 }
189
190 #[test]
191 fn test_normalize_pqc_propagates_to_tls_runtime_config() {
192 let mut config = Config {
193 enforce_pqc: EnforcePqcPolicy::enforced(),
194 servers: vec![server_with_pqc(false)],
195 ..Config::default()
196 };
197
198 config.normalize_pqc().unwrap();
199
200 assert!(config.enforce_pqc().is_enforced());
201 assert!(config.servers[0].tls_setting.config.enforce_pqc);
202 }
203
204 #[test]
205 fn test_config_new() {
206 let config = Config::new();
207 assert!(config.servers.is_empty());
208 assert!(config.clients.is_empty());
209 }
210
211 #[test]
212 fn test_config_default() {
213 let config = Config::default();
214 assert!(config.servers.is_empty());
215 assert!(config.clients.is_empty());
216 }
217
218 #[test]
219 fn test_config_with_servers() {
220 let server_config = create_test_server_config();
221 let config = Config::new().with_servers(vec![server_config.clone()]);
222
223 assert_eq!(config.servers.len(), 1);
224 assert_eq!(config.servers[0], server_config);
225 assert!(config.clients.is_empty());
226 }
227
228 #[test]
229 fn test_config_with_clients() {
230 let client_config = create_test_client_config();
231 let config = Config::new().with_clients(vec![client_config.clone()]);
232
233 assert_eq!(config.clients.len(), 1);
234 assert_eq!(config.clients[0], client_config);
235 assert!(config.servers.is_empty());
236 }
237
238 #[test]
239 fn test_config_servers_getter() {
240 let server_config = create_test_server_config();
241 let config = Config::new().with_servers(vec![server_config.clone()]);
242
243 let servers = config.servers();
244 assert_eq!(servers.len(), 1);
245 assert_eq!(servers[0], server_config);
246 }
247
248 #[test]
249 fn test_config_clients_getter() {
250 let client_config = create_test_client_config();
251 let config = Config::new().with_clients(vec![client_config.clone()]);
252
253 let clients = config.clients();
254 assert_eq!(clients.len(), 1);
255 assert_eq!(clients[0], client_config);
256 }
257
258 #[test]
259 fn test_config_chaining() {
260 let server_config = create_test_server_config();
261 let client_config = create_test_client_config();
262
263 let config = Config::new()
264 .with_servers(vec![server_config.clone()])
265 .with_clients(vec![client_config.clone()]);
266
267 assert_eq!(config.servers.len(), 1);
268 assert_eq!(config.clients.len(), 1);
269 }
270
271 #[test]
272 fn test_config_validate_empty() {
273 let config = Config::new();
274 assert!(config.validate().is_ok());
275 }
276
277 #[test]
278 fn test_config_validate_with_valid_servers_and_clients() {
279 let server_config = create_test_server_config();
280 let client_config = create_test_client_config();
281 let config = Config::new()
282 .with_servers(vec![server_config])
283 .with_clients(vec![client_config]);
284
285 assert!(config.validate().is_ok());
286 }
287
288 #[test]
289 fn test_config_clone() {
290 let server_config = create_test_server_config();
291 let client_config = create_test_client_config();
292
293 let config1 = Config::new()
294 .with_servers(vec![server_config])
295 .with_clients(vec![client_config]);
296
297 let config2 = config1.clone();
298
299 assert_eq!(config1.servers, config2.servers);
300 assert_eq!(config1.clients, config2.clients);
301 }
302
303 #[tokio::test]
304 async fn test_config_into_service() {
305 let server_config = create_test_server_config();
306 let client_config = create_test_client_config();
307
308 let config = Config::new()
309 .with_servers(vec![server_config.clone()])
310 .with_clients(vec![client_config]);
311
312 let domain_name = Some("test-domain".to_string());
313 let message_processor = MessageProcessor::new();
314
315 let _control_plane = config.into_service(
316 "test-instance".to_string(),
317 domain_name,
318 message_processor,
319 &[server_config],
320 None,
321 );
322 }
323
324 #[test]
325 fn test_config_debug_trait() {
326 let config = Config::new();
327 let debug_str = format!("{:?}", config);
328 assert!(debug_str.contains("Config"));
329 assert!(debug_str.contains("servers"));
330 assert!(debug_str.contains("clients"));
331 }
332
333 #[test]
334 fn test_config_validate_with_multiple_servers() {
335 let server1 = create_test_server_config();
336 let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
337 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
338
339 let config = Config::new().with_servers(vec![server1, server2]);
340 assert!(config.validate().is_ok());
341 }
342
343 #[test]
344 fn test_config_validate_with_multiple_clients() {
345 let client1 = create_test_client_config();
346 let client2 = ClientConfig::with_endpoint("http://127.0.0.1:50052")
347 .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure());
348
349 let config = Config::new().with_clients(vec![client1, client2]);
350 assert!(config.validate().is_ok());
351 }
352
353 #[test]
354 fn test_config_partial_eq() {
355 let config1 = Config::new();
356 let config2 = Config::new();
357
358 assert_eq!(config1, config2);
359
360 let server_config = create_test_server_config();
361 let config3 = config1.clone().with_servers(vec![server_config]);
362
363 assert_ne!(config1, config3);
364 }
365
366 #[test]
367 fn test_config_builder_pattern_reuse() {
368 let base_config = Config::new();
369
370 let config1 = base_config
371 .clone()
372 .with_servers(vec![create_test_server_config()]);
373 let config2 = base_config
374 .clone()
375 .with_clients(vec![create_test_client_config()]);
376
377 assert!(base_config.servers.is_empty());
378 assert!(base_config.clients.is_empty());
379
380 assert_eq!(config1.servers.len(), 1);
381 assert!(config1.clients.is_empty());
382
383 assert!(config2.servers.is_empty());
384 assert_eq!(config2.clients.len(), 1);
385 }
386
387 #[test]
388 fn test_config_overwrite_behavior() {
389 let server1 = create_test_server_config();
390 let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
391 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
392
393 let config = Config::new()
394 .with_servers(vec![server1])
395 .with_servers(vec![server2.clone()]);
396
397 assert_eq!(config.servers.len(), 1);
398 assert_eq!(config.servers[0], server2);
399 }
400}