@@ -115,7 +115,7 @@ pub(super) mod api {
115115 use nonzero_ext:: nonzero;
116116 use semver:: Version ;
117117 use serde:: Deserialize ;
118- use snafu:: ResultExt ;
118+ use snafu:: { OptionExt , ResultExt } ;
119119 use std:: process:: { Command , Output } ;
120120 use tokio:: time:: Duration ;
121121 use tokio_retry:: {
@@ -233,15 +233,22 @@ pub(super) mod api {
233233 /// Extract error statuscode from stderr string
234234 /// Error Example:
235235 /// "Failed POST request to '/actions/refresh-updates': Status 423 when POSTing /actions/refresh-updates: Update lock held\n"
236- fn extract_status_code_from_error ( error : & str ) -> Option < StatusCode > {
237- error
236+ pub fn extract_status_code_from_error ( error : & str ) -> Result < StatusCode > {
237+ let status_str = error
238238 . split ( "Status " )
239- . nth ( 1 ) ?
239+ . nth ( 1 )
240+ . context ( apiclient_error:: StatusCodeParsingSnafu ) ?;
241+
242+ let code_str = status_str
240243 . split_whitespace ( )
241- . next ( ) ?
244+ . next ( )
245+ . context ( apiclient_error:: StatusCodeParsingSnafu ) ?;
246+
247+ let code = code_str
242248 . parse :: < u16 > ( )
243- . ok ( )
244- . and_then ( |code| StatusCode :: from_u16 ( code) . ok ( ) )
249+ . context ( apiclient_error:: StatusCodeIntParsingSnafu ) ?;
250+
251+ StatusCode :: from_u16 ( code) . context ( apiclient_error:: InvalidStatusCodeSnafu )
245252 }
246253
247254 /// Wait time between invoking the Bottlerocket API
@@ -298,7 +305,7 @@ pub(super) mod api {
298305 let error_statuscode = extract_status_code_from_error ( & error_content) ;
299306
300307 match error_statuscode {
301- Some ( UPDATE_API_BUSY_STATUSCODE ) => {
308+ Ok ( UPDATE_API_BUSY_STATUSCODE ) => {
302309 event ! (
303310 Level :: DEBUG ,
304311 "The lock for the update API is held by another process ..."
@@ -312,7 +319,7 @@ pub(super) mod api {
312319 args : args. clone ( ) ,
313320 error_content : & error_content,
314321 statuscode : error_statuscode. map_or_else (
315- || "None" . to_string ( ) ,
322+ |_ | "None" . to_string ( ) ,
316323 |s| s. as_u16 ( ) . to_string ( ) ,
317324 ) ,
318325 }
@@ -454,5 +461,69 @@ pub mod apiclient_error {
454461
455462 #[ snafu( display( "Unable to parse version information: '{}'" , source) ) ]
456463 VersionParseError { source : semver:: Error } ,
464+
465+ #[ snafu( display( "Failed to parse status code from error string" ) ) ]
466+ StatusCodeParsing ,
467+
468+ #[ snafu( display( "Failed to parse integer from status code string" ) ) ]
469+ StatusCodeIntParsing { source : std:: num:: ParseIntError } ,
470+
471+ #[ snafu( display( "Invalid HTTP status code" ) ) ]
472+ InvalidStatusCode {
473+ source : http:: status:: InvalidStatusCode ,
474+ } ,
475+ }
476+ }
477+
478+ #[ cfg( test) ]
479+ mod tests {
480+ use super :: api:: * ;
481+ use super :: apiclient_error:: Error ;
482+ use http:: StatusCode ;
483+
484+ #[ test]
485+ fn test_extract_status_code_from_error_success ( ) {
486+ let error = "Failed POST request to '/actions/refresh-updates': Status 423 when POSTing /actions/refresh-updates: Update lock held" ;
487+ let result = extract_status_code_from_error ( error) ;
488+ assert ! ( result. is_ok( ) ) ;
489+ assert_eq ! ( result. unwrap( ) , StatusCode :: LOCKED ) ;
490+ }
491+
492+ #[ test]
493+ fn test_extract_status_code_from_error_no_status ( ) {
494+ let error = "Some error without status code" ;
495+ let result = extract_status_code_from_error ( error) ;
496+ assert ! ( result. is_err( ) ) ;
497+ assert ! ( matches!( result. unwrap_err( ) , Error :: StatusCodeParsing ) ) ;
498+ }
499+
500+ #[ test]
501+ fn test_extract_status_code_from_error_invalid_number ( ) {
502+ let error = "Failed POST request: Status abc when POSTing" ;
503+ let result = extract_status_code_from_error ( error) ;
504+ assert ! ( result. is_err( ) ) ;
505+ assert ! ( matches!(
506+ result. unwrap_err( ) ,
507+ Error :: StatusCodeIntParsing { .. }
508+ ) ) ;
509+ }
510+
511+ #[ test]
512+ fn test_extract_status_code_from_error_invalid_status_code ( ) {
513+ let error = "Failed POST request: Status 1000 when POSTing" ;
514+ let result = extract_status_code_from_error ( error) ;
515+ assert ! ( result. is_err( ) ) ;
516+ assert ! ( matches!(
517+ result. unwrap_err( ) ,
518+ Error :: InvalidStatusCode { .. }
519+ ) ) ;
520+ }
521+
522+ #[ test]
523+ fn test_extract_status_code_from_error_no_whitespace ( ) {
524+ let error = "Status 200" ;
525+ let result = extract_status_code_from_error ( error) ;
526+ assert ! ( result. is_ok( ) ) ;
527+ assert_eq ! ( result. unwrap( ) , StatusCode :: OK ) ;
457528 }
458529}
0 commit comments