1+ use std:: collections:: HashSet ;
12use std:: fs;
23
34use serde:: Deserialize ;
45
56#[ derive( Debug , Deserialize , Clone ) ]
67pub struct GatewayConfig {
8+ #[ serde( default ) ]
79 pub orchestrator : OrchestratorConfig ,
810 pub detectors : Vec < DetectorConfig > ,
911 pub routes : Vec < RouteConfig > ,
@@ -15,25 +17,48 @@ pub struct OrchestratorConfig {
1517 pub port : Option < u16 > ,
1618}
1719
20+ impl Default for OrchestratorConfig {
21+ fn default ( ) -> Self {
22+ OrchestratorConfig {
23+ host : "localhost" . to_string ( ) ,
24+ port : Some ( 8032 ) ,
25+ }
26+ }
27+ }
28+
1829#[ derive( Debug , Deserialize , Clone ) ]
1930pub struct DetectorConfig {
2031 pub name : String ,
32+ #[ serde( default ) ]
33+ pub server : Option < String > ,
2134 pub input : bool ,
2235 pub output : bool ,
2336 pub detector_params : Option < serde_json:: Value > ,
2437}
2538
39+ impl DetectorConfig {
40+ pub fn with_server_default ( mut self ) -> Self {
41+ if self . server . is_none ( ) {
42+ self . server = Some ( self . name . clone ( ) ) ;
43+ }
44+ self
45+ }
46+ }
47+
2648#[ derive( Debug , Deserialize , Clone ) ]
2749pub struct RouteConfig {
2850 pub name : String ,
2951 pub detectors : Vec < String > ,
3052 pub fallback_message : Option < String > ,
3153}
3254
55+
3356pub fn read_config ( path : & str ) -> GatewayConfig {
3457 let result = fs:: read_to_string ( path) . expect ( & format ! ( "could not read file: {}" , path) ) ;
3558
36- serde_yml:: from_str ( & result) . expect ( "failed to read in yaml config" )
59+ let mut cfg: GatewayConfig = serde_yml:: from_str ( & result) . expect ( "failed to read in yaml config" ) ;
60+ cfg. detectors = cfg. detectors . into_iter ( ) . map ( |d| d. with_server_default ( ) ) . collect ( ) ;
61+ cfg
3762}
3863
3964pub fn validate_registered_detectors ( gateway_cfg : & GatewayConfig ) {
@@ -43,15 +68,43 @@ pub fn validate_registered_detectors(gateway_cfg: &GatewayConfig) {
4368 . map ( |detector| & detector. name )
4469 . collect ( ) ;
4570
71+ let mut issues = Vec :: new ( ) ;
4672 for route in gateway_cfg. routes . iter ( ) {
4773 for detector in & route. detectors {
4874 if !detector_names. contains ( & detector) {
49- panic ! (
50- "could not find detector {} in route {} " ,
75+ issues . push ( format ! (
76+ "- could not find detector '{}' in route '{}' " ,
5177 detector, route. name
52- ) ;
78+ ) ) ;
5379 }
5480 }
81+
82+ // Validate no duplicate input/output servers
83+ let mut seen_input = HashSet :: new ( ) ;
84+ let mut seen_output = HashSet :: new ( ) ;
85+
86+ for detector_name in & route. detectors {
87+ if let Some ( detector_cfg) = gateway_cfg. detectors . iter ( ) . find ( |d| & d. name == detector_name) {
88+ if detector_cfg. input {
89+ let server = detector_cfg. server . as_ref ( ) . unwrap ( ) ;
90+ if !seen_input. insert ( server) {
91+ issues. push ( format ! (
92+ "- route '{}' contains more than one input detector with server '{}'" ,
93+ route. name, server
94+ ) ) ;
95+ }
96+ if !seen_output. insert ( server) {
97+ issues. push ( format ! (
98+ "- route '{}' contains more than one output detector with server '{}'" ,
99+ route. name, server
100+ ) ) ;
101+ }
102+ }
103+ }
104+ }
105+ }
106+ if !issues. is_empty ( ) {
107+ panic ! ( "Config validation failed:\n {}" , issues. join( "\n " ) ) ;
55108 }
56109}
57110
@@ -69,6 +122,7 @@ mod tests {
69122 } ,
70123 detectors : vec ! [ DetectorConfig {
71124 name: "regex" . to_string( ) ,
125+ server: None ,
72126 input: false ,
73127 output: false ,
74128 detector_params: None ,
@@ -82,4 +136,102 @@ mod tests {
82136
83137 validate_registered_detectors ( & gc) ;
84138 }
139+
140+ #[ test]
141+ #[ should_panic]
142+ fn test_validate_multiple_same_server_input_detectors ( ) {
143+ let gc = GatewayConfig {
144+ orchestrator : OrchestratorConfig {
145+ host : "localhost" . to_string ( ) ,
146+ port : Some ( 1234 ) ,
147+ } ,
148+ detectors : vec ! [ DetectorConfig {
149+ name: "regex-1" . to_string( ) ,
150+ server: Some ( "server-a" . to_string( ) ) ,
151+ input: true ,
152+ output: false ,
153+ detector_params: None ,
154+ } , DetectorConfig {
155+ name: "regex-2" . to_string( ) ,
156+ server: Some ( "server-a" . to_string( ) ) ,
157+ input: true ,
158+ output: false ,
159+ detector_params: None ,
160+ } ,
161+
162+ ] ,
163+ routes : vec ! [ RouteConfig {
164+ name: "route1" . to_string( ) ,
165+ detectors: vec![ "regex-1" . to_string( ) , "regex-2" . to_string( ) ] ,
166+ fallback_message: None ,
167+ } ] ,
168+ } ;
169+
170+ validate_registered_detectors ( & gc) ;
171+ }
172+
173+ #[ test]
174+ #[ should_panic]
175+ fn test_validate_multiple_same_server_output_detectors ( ) {
176+ let gc = GatewayConfig {
177+ orchestrator : OrchestratorConfig {
178+ host : "localhost" . to_string ( ) ,
179+ port : Some ( 1234 ) ,
180+ } ,
181+ detectors : vec ! [ DetectorConfig {
182+ name: "regex-1" . to_string( ) ,
183+ server: Some ( "server-a" . to_string( ) ) ,
184+ input: false ,
185+ output: true ,
186+ detector_params: None ,
187+ } , DetectorConfig {
188+ name: "regex-2" . to_string( ) ,
189+ server: Some ( "server-a" . to_string( ) ) ,
190+ input: false ,
191+ output: true ,
192+ detector_params: None ,
193+ } ,
194+
195+ ] ,
196+ routes : vec ! [ RouteConfig {
197+ name: "route1" . to_string( ) ,
198+ detectors: vec![ "regex-1" . to_string( ) , "regex-2" . to_string( ) ] ,
199+ fallback_message: None ,
200+ } ] ,
201+ } ;
202+
203+ validate_registered_detectors ( & gc) ;
204+ }
205+
206+ #[ test]
207+ fn test_validate_multiple_same_server_detectors ( ) {
208+ let gc = GatewayConfig {
209+ orchestrator : OrchestratorConfig {
210+ host : "localhost" . to_string ( ) ,
211+ port : Some ( 1234 ) ,
212+ } ,
213+ detectors : vec ! [ DetectorConfig {
214+ name: "regex-1" . to_string( ) ,
215+ server: Some ( "server-a" . to_string( ) ) ,
216+ input: true ,
217+ output: false ,
218+ detector_params: None ,
219+ } , DetectorConfig {
220+ name: "regex-2" . to_string( ) ,
221+ server: Some ( "server-a" . to_string( ) ) ,
222+ input: false ,
223+ output: true ,
224+ detector_params: None ,
225+ } ,
226+
227+ ] ,
228+ routes : vec ! [ RouteConfig {
229+ name: "route1" . to_string( ) ,
230+ detectors: vec![ "regex-1" . to_string( ) , "regex-2" . to_string( ) ] ,
231+ fallback_message: None ,
232+ } ] ,
233+ } ;
234+
235+ validate_registered_detectors ( & gc) ;
236+ }
85237}
0 commit comments