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