1212
1313use config:: { ExternalConfig , GwConfig } ;
1414use k8s_intf:: generated:: gateway_agent_crd:: GatewayAgent ;
15- use ordermap:: OrderMap ;
1615use serde:: { Deserialize , Serialize } ;
1716use std:: io:: { self , Read } ;
1817
19- #[ derive( Serialize , Deserialize , Default ) ]
18+ #[ derive( Default ) ]
2019struct ConfigErrors {
2120 errors : Vec < String > , // only one error is supported at the moment
2221}
2322
24- #[ derive( Serialize , Deserialize , Default ) ]
2523struct DeserializeError {
2624 hint : String ,
27- line : usize ,
28- column : usize ,
29- category : String ,
30- context : OrderMap < usize , String > ,
25+ subtype : String ,
3126}
3227
33- /// The type representing the outcome of a validation request
34- #[ derive( Serialize , Deserialize , Default ) ]
35- enum ValidateReply {
28+ /// The type representing an error when validating a request
29+ enum ValidateError {
3630 /// This type contains errors that may occur when using this tool.
3731 EnvironmentError ( String ) ,
3832
@@ -55,108 +49,175 @@ enum ValidateReply {
5549 /// - contains values that are not allowed / supported
5650 ///
5751 /// which would prevent the gateway from functioning correctly.
58- /// Unlike any of the previous , these are errors the user is responsible for.
52+ /// Together with some conversion errors , these are errors the user is responsible for.
5953 Configuration ( ConfigErrors ) ,
54+ }
55+ impl ValidateError {
56+ /// Provide a string indicating the type of error
57+ fn get_type ( & self ) -> & str {
58+ match self {
59+ ValidateError :: EnvironmentError ( _) => "Environment" ,
60+ ValidateError :: DeserializeError ( _) => "Deserialization" ,
61+ ValidateError :: MetadataError ( _) => "Metadata" ,
62+ ValidateError :: ConversionError ( _) => "Conversion" ,
63+ ValidateError :: Configuration ( _) => "Configuration" ,
64+ }
65+ }
66+ /// Provide a string indicating the subtype of an error
67+ fn get_subtype ( & self ) -> Option < String > {
68+ match self {
69+ ValidateError :: DeserializeError ( e) => Some ( e. subtype . clone ( ) ) ,
70+ _ => None ,
71+ }
72+ }
6073
61- /// This type signals that the configuration could be processed and that it is VALID.
62- #[ default]
63- Success ,
74+ /// Provide a list of messages depending on the error type
75+ fn get_msg ( & self ) -> Vec < String > {
76+ match self {
77+ ValidateError :: EnvironmentError ( v) => vec ! [ v. clone( ) ] ,
78+ ValidateError :: DeserializeError ( v) => vec ! [ v. hint. clone( ) ] ,
79+ ValidateError :: MetadataError ( v) => vec ! [ v. clone( ) ] ,
80+ ValidateError :: ConversionError ( v) => vec ! [ v. clone( ) ] ,
81+ ValidateError :: Configuration ( v) => v. errors . to_vec ( ) ,
82+ }
83+ }
6484}
6585
66- /// Deserialize json string as a `GatewayAgent`
67- fn deserialize ( ga_json : & str ) -> Result < GatewayAgent , ValidateReply > {
68- let crd = serde_json:: from_str :: < GatewayAgent > ( ga_json) . map_err ( |e| {
69- let mut deserr = DeserializeError :: default ( ) ;
70- deserr. hint = e. to_string ( ) ;
71- deserr. line = e. line ( ) ;
72- deserr. column = e. column ( ) ;
73- deserr. category = format ! ( "{:?}" , e. classify( ) ) ;
74-
75- let mut context = OrderMap :: new ( ) ;
76- let start = e. line ( ) - 5 ;
77- let end = e. line ( ) + 5 ;
78- for k in start..end {
79- let line = ga_json. lines ( ) . nth ( k) . unwrap_or ( "" ) . to_string ( ) ;
80- context. insert ( k, line) ;
86+ impl From < & ValidateError > for ValidateReply {
87+ fn from ( value : & ValidateError ) -> Self {
88+ let r#type = value. get_type ( ) ;
89+ let subtype = value. get_subtype ( ) ;
90+ let msg = value. get_msg ( ) ;
91+
92+ ValidateReply {
93+ success : false ,
94+ errors : msg
95+ . iter ( )
96+ . map ( |m| ValidateErrorJson {
97+ r#type : r#type. to_owned ( ) ,
98+ subtype : subtype. clone ( ) ,
99+ message : m. clone ( ) ,
100+ context : None ,
101+ } )
102+ . collect ( ) ,
81103 }
82- deserr. context = context;
104+ }
105+ }
106+
107+ #[ derive( Serialize , Deserialize ) ]
108+ struct ValidateErrorJson {
109+ r#type : String ,
110+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
111+ subtype : Option < String > ,
112+ message : String ,
113+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
114+ context : Option < String > ,
115+ }
83116
84- ValidateReply :: DeserializeError ( deserr)
117+ /// The type representing the outcome of a validation request
118+ #[ derive( Serialize , Deserialize ) ]
119+ struct ValidateReply {
120+ success : bool ,
121+ errors : Vec < ValidateErrorJson > ,
122+ }
123+ impl ValidateReply {
124+ fn success ( ) -> Self {
125+ Self {
126+ success : true ,
127+ errors : vec ! [ ] ,
128+ }
129+ }
130+ }
131+
132+ /// Deserialize JSON string as a `GatewayAgent`
133+ fn deserialize ( ga_json : & str ) -> Result < GatewayAgent , ValidateError > {
134+ let crd = serde_json:: from_str :: < GatewayAgent > ( ga_json) . map_err ( |e| {
135+ let deserr = DeserializeError {
136+ hint : e. to_string ( ) ,
137+ subtype : format ! ( "{:?}" , e. classify( ) ) ,
138+ } ;
139+ ValidateError :: DeserializeError ( deserr)
85140 } ) ?;
86141
87142 Ok ( crd)
88143}
89144
90145/// Validate metadata
91- fn validate_metadata ( crd : & GatewayAgent ) -> Result < & str , ValidateReply > {
92- let genid = crd. metadata . generation . ok_or ( ValidateReply :: MetadataError (
146+ fn validate_metadata ( crd : & GatewayAgent ) -> Result < & str , ValidateError > {
147+ let genid = crd. metadata . generation . ok_or ( ValidateError :: MetadataError (
93148 "Missing generation Id" . to_string ( ) ,
94149 ) ) ?;
95150 if genid == 0 {
96- return Err ( ValidateReply :: MetadataError (
151+ return Err ( ValidateError :: MetadataError (
97152 "Invalid generation Id" . to_string ( ) ,
98153 ) ) ;
99154 }
100155 let gwname = crd
101156 . metadata
102157 . name
103158 . as_ref ( )
104- . ok_or ( ValidateReply :: MetadataError (
159+ . ok_or ( ValidateError :: MetadataError (
105160 "Missing gateway name" . to_string ( ) ,
106161 ) ) ?;
107162 if gwname. is_empty ( ) {
108- return Err ( ValidateReply :: MetadataError (
163+ return Err ( ValidateError :: MetadataError (
109164 "Invalid gateway name" . to_string ( ) ,
110165 ) ) ;
111166 }
112167 let namespace = crd
113168 . metadata
114169 . namespace
115170 . as_ref ( )
116- . ok_or ( ValidateReply :: MetadataError (
171+ . ok_or ( ValidateError :: MetadataError (
117172 "Missing namespace" . to_string ( ) ,
118173 ) ) ?;
119174 if namespace. as_str ( ) != "fab" {
120- return Err ( ValidateReply :: MetadataError ( format ! (
175+ return Err ( ValidateError :: MetadataError ( format ! (
121176 "Invalid namespace {namespace}"
122177 ) ) ) ;
123178 }
124179
125180 Ok ( gwname. as_str ( ) )
126181}
127182
128- fn validate ( gwagent_json : & str ) -> Result < ValidateReply , ValidateReply > {
183+ /// Main validation function
184+ fn validate ( gwagent_json : & str ) -> Result < ( ) , ValidateError > {
129185 let crd = deserialize ( gwagent_json) ?;
130186 let gwname = validate_metadata ( & crd) ?;
131187
132188 let external = ExternalConfig :: try_from ( & crd)
133- . map_err ( |e| ValidateReply :: ConversionError ( e. to_string ( ) ) ) ?;
189+ . map_err ( |e| ValidateError :: ConversionError ( e. to_string ( ) ) ) ?;
134190
135191 let mut gwconfig = GwConfig :: new ( gwname, external) ;
136192 gwconfig. validate ( ) . map_err ( |e| {
137- let mut errors = ConfigErrors :: default ( ) ;
138- errors . errors . push ( e. to_string ( ) ) ;
139- ValidateReply :: Configuration ( errors )
193+ let mut config = ConfigErrors :: default ( ) ;
194+ config . errors . push ( e. to_string ( ) ) ;
195+ ValidateError :: Configuration ( config )
140196 } ) ?;
141197
142- Ok ( ValidateReply :: Success )
198+ Ok ( ( ) )
143199}
144200
145- fn validate_from_stdin ( ) -> Result < ValidateReply , ValidateReply > {
201+ /// Read from stdin, deserialize as JSON and validate
202+ fn validate_from_stdin ( ) -> Result < ( ) , ValidateError > {
146203 let mut input = String :: new ( ) ;
147204 io:: stdin ( )
148205 . read_to_string ( & mut input)
149- . map_err ( |e| ValidateReply :: EnvironmentError ( format ! ( "Failed to read from stdin: {e}" ) ) ) ?;
206+ . map_err ( |e| ValidateError :: EnvironmentError ( format ! ( "Failed to read from stdin: {e}" ) ) ) ?;
150207
151208 validate ( & input)
152209}
153210
154- fn main ( ) {
155- match validate_from_stdin ( ) {
156- Ok ( reply) => println ! ( "{}" , serde_json:: to_string_pretty( & reply) . unwrap( ) ) ,
157- Err ( reply) => {
158- let json = serde_json:: to_string_pretty ( & reply) . unwrap ( ) ;
159- println ! ( "{json}" ) ;
160- }
211+ /// Build a validation reply to be output as JSON
212+ fn build_reply ( result : Result < ( ) , ValidateError > ) -> ValidateReply {
213+ match result {
214+ Ok ( ( ) ) => ValidateReply :: success ( ) ,
215+ Err ( e) => ValidateReply :: from ( & e) ,
161216 }
162217}
218+
219+ fn main ( ) {
220+ let result = validate_from_stdin ( ) ;
221+ let reply = build_reply ( result) ;
222+ println ! ( "{}" , serde_json:: to_string_pretty( & reply) . unwrap( ) ) ;
223+ }
0 commit comments