Skip to main content

slim_controller/
config.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use serde::Deserialize;
5
6use slim_config::client::ClientConfig;
7use slim_config::component::configuration::Configuration;
8use slim_config::server::ServerConfig;
9use slim_datapath::message_processing::MessageProcessor;
10
11use crate::errors::ControllerError;
12use crate::service::{ControlPlane, ControlPlaneSettings, from_server_config};
13
14/// Configuration for the Control-Plane / Data-Plane component
15#[derive(Debug, Clone, Deserialize, Default, PartialEq)]
16#[serde(deny_unknown_fields)]
17pub struct Config {
18    /// Controller GRPC server settings
19    #[serde(default)]
20    pub servers: Vec<ServerConfig>,
21
22    /// Controller client config to connect to control plane
23    #[serde(default)]
24    pub clients: Vec<ClientConfig>,
25
26    /// Controller client config to connect to data-plane server nodes
27    #[serde(default)]
28    pub outbound_clients: Vec<ClientConfig>,
29}
30
31impl Config {
32    /// Create a new Config instance with default values
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    pub fn is_default(&self) -> bool {
38        self == &Self::default()
39    }
40
41    /// Create a new Config instance with the given servers
42    pub fn with_servers(self, servers: Vec<ServerConfig>) -> Self {
43        Self { servers, ..self }
44    }
45
46    /// Create a new Config instance with the given clients
47    pub fn with_clients(self, clients: Vec<ClientConfig>) -> Self {
48        Self { clients, ..self }
49    }
50
51    /// Get the list of server configurations
52    pub fn servers(&self) -> &[ServerConfig] {
53        &self.servers
54    }
55
56    /// Get the list of client configurations
57    pub fn clients(&self) -> &[ClientConfig] {
58        &self.clients
59    }
60
61    /// Create a ControlPlane service instance from this configuration
62    pub fn into_service(
63        &self,
64        node_id: String,
65        group_name: Option<String>,
66        message_processor: MessageProcessor,
67        // List of server configurations for the dataplane services.
68        // Used to extract connection type information required to connect to the node
69        // (e.g., TLS settings). This information is used by the control plane.
70        dataplane_servers: &[ServerConfig],
71        auth_provider: Option<slim_auth::auth_provider::AuthProvider>,
72    ) -> ControlPlane {
73        let connection_details = dataplane_servers.iter().map(from_server_config).collect();
74
75        ControlPlane::new(ControlPlaneSettings {
76            id: node_id,
77            group_name,
78            servers: self.servers.clone(),
79            clients: self.clients.clone(),
80            outbound_clients: self.outbound_clients.clone(),
81            message_processor,
82            connection_details,
83            auth_provider,
84        })
85    }
86}
87
88impl Configuration for Config {
89    type Error = ControllerError;
90
91    fn validate(&self) -> Result<(), Self::Error> {
92        // Validate client and server configurations
93        for server in self.servers.iter() {
94            server.validate()?;
95        }
96
97        for client in &self.clients {
98            client.validate()?;
99        }
100
101        Ok(())
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use slim_config::server::ServerConfig;
109    use slim_datapath::message_processing::MessageProcessor;
110
111    fn create_test_server_config() -> ServerConfig {
112        ServerConfig::with_endpoint("127.0.0.1:50051")
113            .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure())
114    }
115
116    fn create_test_client_config() -> ClientConfig {
117        ClientConfig::with_endpoint("http://127.0.0.1:50051")
118            .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure())
119    }
120
121    #[test]
122    fn test_config_new() {
123        let config = Config::new();
124        assert!(config.servers.is_empty());
125        assert!(config.clients.is_empty());
126    }
127
128    #[test]
129    fn test_config_default() {
130        let config = Config::default();
131        assert!(config.servers.is_empty());
132        assert!(config.clients.is_empty());
133    }
134
135    #[test]
136    fn test_config_with_servers() {
137        let server_config = create_test_server_config();
138        let config = Config::new().with_servers(vec![server_config.clone()]);
139
140        assert_eq!(config.servers.len(), 1);
141        assert_eq!(config.servers[0], server_config);
142        assert!(config.clients.is_empty());
143    }
144
145    #[test]
146    fn test_config_with_clients() {
147        let client_config = create_test_client_config();
148        let config = Config::new().with_clients(vec![client_config.clone()]);
149
150        assert_eq!(config.clients.len(), 1);
151        assert_eq!(config.clients[0], client_config);
152        assert!(config.servers.is_empty());
153    }
154
155    #[test]
156    fn test_config_servers_getter() {
157        let server_config = create_test_server_config();
158        let config = Config::new().with_servers(vec![server_config.clone()]);
159
160        let servers = config.servers();
161        assert_eq!(servers.len(), 1);
162        assert_eq!(servers[0], server_config);
163    }
164
165    #[test]
166    fn test_config_clients_getter() {
167        let client_config = create_test_client_config();
168        let config = Config::new().with_clients(vec![client_config.clone()]);
169
170        let clients = config.clients();
171        assert_eq!(clients.len(), 1);
172        assert_eq!(clients[0], client_config);
173    }
174
175    #[test]
176    fn test_config_chaining() {
177        let server_config = create_test_server_config();
178        let client_config = create_test_client_config();
179
180        let config = Config::new()
181            .with_servers(vec![server_config.clone()])
182            .with_clients(vec![client_config.clone()]);
183
184        assert_eq!(config.servers.len(), 1);
185        assert_eq!(config.clients.len(), 1);
186    }
187
188    #[test]
189    fn test_config_validate_empty() {
190        let config = Config::new();
191        assert!(config.validate().is_ok());
192    }
193
194    #[test]
195    fn test_config_validate_with_valid_servers_and_clients() {
196        let server_config = create_test_server_config();
197        let client_config = create_test_client_config();
198        let config = Config::new()
199            .with_servers(vec![server_config])
200            .with_clients(vec![client_config]);
201
202        assert!(config.validate().is_ok());
203    }
204
205    #[test]
206    fn test_config_clone() {
207        let server_config = create_test_server_config();
208        let client_config = create_test_client_config();
209
210        let config1 = Config::new()
211            .with_servers(vec![server_config])
212            .with_clients(vec![client_config]);
213
214        let config2 = config1.clone();
215
216        assert_eq!(config1.servers, config2.servers);
217        assert_eq!(config1.clients, config2.clients);
218    }
219
220    #[tokio::test]
221    async fn test_config_into_service() {
222        let server_config = create_test_server_config();
223        let client_config = create_test_client_config();
224
225        let config = Config::new()
226            .with_servers(vec![server_config.clone()])
227            .with_clients(vec![client_config]);
228
229        let group_name = Some("test-group".to_string());
230        let message_processor = MessageProcessor::new();
231
232        let _control_plane = config.into_service(
233            "test-instance".to_string(),
234            group_name,
235            message_processor,
236            &[server_config],
237            None,
238        );
239    }
240
241    #[test]
242    fn test_config_debug_trait() {
243        let config = Config::new();
244        let debug_str = format!("{:?}", config);
245        assert!(debug_str.contains("Config"));
246        assert!(debug_str.contains("servers"));
247        assert!(debug_str.contains("clients"));
248    }
249
250    #[test]
251    fn test_config_validate_with_multiple_servers() {
252        let server1 = create_test_server_config();
253        let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
254            .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
255
256        let config = Config::new().with_servers(vec![server1, server2]);
257        assert!(config.validate().is_ok());
258    }
259
260    #[test]
261    fn test_config_validate_with_multiple_clients() {
262        let client1 = create_test_client_config();
263        let client2 = ClientConfig::with_endpoint("http://127.0.0.1:50052")
264            .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure());
265
266        let config = Config::new().with_clients(vec![client1, client2]);
267        assert!(config.validate().is_ok());
268    }
269
270    #[test]
271    fn test_config_partial_eq() {
272        let config1 = Config::new();
273        let config2 = Config::new();
274
275        assert_eq!(config1, config2);
276
277        let server_config = create_test_server_config();
278        let config3 = config1.clone().with_servers(vec![server_config]);
279
280        assert_ne!(config1, config3);
281    }
282
283    #[test]
284    fn test_config_builder_pattern_reuse() {
285        let base_config = Config::new();
286
287        let config1 = base_config
288            .clone()
289            .with_servers(vec![create_test_server_config()]);
290        let config2 = base_config
291            .clone()
292            .with_clients(vec![create_test_client_config()]);
293
294        assert!(base_config.servers.is_empty());
295        assert!(base_config.clients.is_empty());
296
297        assert_eq!(config1.servers.len(), 1);
298        assert!(config1.clients.is_empty());
299
300        assert!(config2.servers.is_empty());
301        assert_eq!(config2.clients.len(), 1);
302    }
303
304    #[test]
305    fn test_config_overwrite_behavior() {
306        let server1 = create_test_server_config();
307        let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
308            .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
309
310        let config = Config::new()
311            .with_servers(vec![server1])
312            .with_servers(vec![server2.clone()]);
313
314        assert_eq!(config.servers.len(), 1);
315        assert_eq!(config.servers[0], server2);
316    }
317}