Skip to content

Commit 608721f

Browse files
committed
fix: add improved error handling when parsing status code
Add error handling messages for status code parsing Add unit tests to validate changes
1 parent d7bba86 commit 608721f

File tree

1 file changed

+80
-9
lines changed

1 file changed

+80
-9
lines changed

agent/src/apiclient.rs

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)