diff --git a/wicketd/src/context.rs b/wicketd/src/context.rs index c5d8c97e012..9eefaefb0b8 100644 --- a/wicketd/src/context.rs +++ b/wicketd/src/context.rs @@ -8,6 +8,7 @@ use crate::MgsHandle; use crate::bgp_auth_keys::BgpAuthKeyError; use crate::bgp_auth_keys::BgpAuthKeys; use crate::bootstrap_addrs::BootstrapPeersFromDdm; +use crate::http_helpers::http_error_with_message; use crate::multirack_config::CurrentMultirackJoinConfig; use crate::preflight_check::PreflightCheckerHandler; use crate::rss_config::CurrentRssConfig; @@ -22,6 +23,7 @@ use iddqd::IdOrdMap; use internal_dns_resolver::Resolver; use sled_hardware_types::Baseboard; use slog::info; +use slog_error_chain::InlineErrorChain; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::mem; @@ -329,10 +331,12 @@ impl ServerContext { } } - pub(crate) async fn local_switch_id(&self) -> Option { + pub(crate) async fn local_switch_id( + &self, + ) -> Result { // Do we already have it cached from a previous invocation? if let Some(&switch_id) = self.local_switch_id.get() { - return Some(switch_id); + return Ok(switch_id); } // We don't have a cached switch ID; try to fetch it from MGS. We @@ -352,7 +356,7 @@ impl ServerContext { self.transceiver_handle.set_local_switch_id(switch_id); } - Some(switch_id) + Ok(switch_id) } Err(err) => { slog::warn!( @@ -360,8 +364,25 @@ impl ServerContext { "Failed to fetch local switch ID from MGS"; "err" => #%err, ); - None + Err(LocalSwitchIdError::from(err)) } } } } + +/// An error returned when the local switch ID cannot be fetched from MGS. +#[derive(Debug, thiserror::Error)] +#[error("failed to fetch local switch ID from MGS")] +pub(crate) struct LocalSwitchIdError( + #[from] gateway_client::Error, +); + +impl LocalSwitchIdError { + pub(crate) fn to_http_error(&self) -> HttpError { + http_error_with_message( + dropshot::ErrorStatusCode::SERVICE_UNAVAILABLE, + Some("UnknownSwitchSlot".to_string()), + format!("{} (is MGS running?)", InlineErrorChain::new(self)), + ) + } +} diff --git a/wicketd/src/http_entrypoints.rs b/wicketd/src/http_entrypoints.rs index 198d7f4cfe8..aa7a861c26c 100644 --- a/wicketd/src/http_entrypoints.rs +++ b/wicketd/src/http_entrypoints.rs @@ -459,7 +459,10 @@ impl WicketdApi for WicketdApiImpl { let rqctx = rqctx.context(); let inventory = mgs_inventory_or_unavail(&rqctx.mgs_handle).await?; - let switch_id = rqctx.local_switch_id().await; + // We don't error out in get_location on the local switch ID not being + // available, so discard the error here (it's already logged in + // local_switch_id). + let switch_id = rqctx.local_switch_id().await.ok(); let sled_baseboard = rqctx.baseboard.clone(); let mut switch_baseboard = None; @@ -592,7 +595,7 @@ impl WicketdApi for WicketdApiImpl { let options = body.into_inner(); let our_switch_slot = match rqctx.local_switch_id().await { - Some(SpIdentifier { slot, typ: SpType::Switch }) => match slot { + Ok(SpIdentifier { slot, typ: SpType::Switch }) => match slot { 0 => SwitchSlot::Switch0, 1 => SwitchSlot::Switch1, _ => { @@ -601,16 +604,13 @@ impl WicketdApi for WicketdApiImpl { ))); } }, - Some(other) => { + Ok(other) => { return Err(HttpError::for_internal_error(format!( "unexpected switch SP identifier {other:?}" ))); } - None => { - return Err(HttpError::for_unavail( - Some("UnknownSwitchSlot".to_string()), - "local switch slot not yet determined".to_string(), - )); + Err(err) => { + return Err(err.to_http_error()); } }; diff --git a/wicketd/src/http_helpers.rs b/wicketd/src/http_helpers.rs index 7625b256842..c44d71edd04 100644 --- a/wicketd/src/http_helpers.rs +++ b/wicketd/src/http_helpers.rs @@ -163,7 +163,7 @@ pub(crate) fn ba_lockstep_error_to_http( /// /// This avoids using methods on `HttpError`, many of which don't expose the /// full message to clients for security reasons. -fn http_error_with_message( +pub(crate) fn http_error_with_message( status_code: dropshot::ErrorStatusCode, error_code: Option, message: String,