From d0aa6a6f44b969d022b3c37547b0e5a15b0a5d85 Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 21 Jul 2026 16:03:41 -0700 Subject: [PATCH] [spr] changes to main this commit is based on Created using spr 1.3.6-beta.1 [skip ci] --- clients/wicketd-client/src/lib.rs | 2 +- openapi/wicketd.json | 1 + sp-sim/src/lib.rs | 4 + sp-sim/src/update.rs | 4 +- wicket-common/src/rack_update.rs | 13 +-- wicket/src/cli/rack_update.rs | 8 +- wicketd-api/src/lib.rs | 2 +- .../versions/src/initial/update.rs | 12 ++ .../versions/src/latest.rs | 1 + wicketd/src/context.rs | 29 ++++- wicketd/src/http_entrypoints.rs | 51 ++++++--- wicketd/src/http_helpers.rs | 16 +-- wicketd/src/mgs.rs | 107 +++++++++++++++++- wicketd/src/transceivers.rs | 46 ++++++-- wicketd/src/update_tracker.rs | 2 +- wicketd/tests/integration_tests/updates.rs | 7 +- 16 files changed, 231 insertions(+), 74 deletions(-) diff --git a/clients/wicketd-client/src/lib.rs b/clients/wicketd-client/src/lib.rs index 27369ec048c..53b272007a7 100644 --- a/clients/wicketd-client/src/lib.rs +++ b/clients/wicketd-client/src/lib.rs @@ -50,7 +50,7 @@ progenitor::generate_api!( BootstrapSledDescription = wicket_common::rack_setup::BootstrapSledDescription, CertificateUploadResponse = wicketd_commission_types_versions::latest::rack_setup::CertificateUploadResponse, ClearUpdateStateOptions = wicket_common::rack_update::ClearUpdateStateOptions, - ClearUpdateStateResponse = wicket_common::rack_update::ClearUpdateStateResponse, + ClearUpdateStateResponse = wicketd_commission_types_versions::latest::update::ClearUpdateStateResponse, CurrentRssUserConfigInsensitive = wicket_common::rack_setup::CurrentRssUserConfigInsensitive, Duration = std::time::Duration, EventReportForUplinkPreflightCheckSpec = wicket_common::preflight_check::EventReport, diff --git a/openapi/wicketd.json b/openapi/wicketd.json index 93dab1882af..d0b6c0b98a6 100644 --- a/openapi/wicketd.json +++ b/openapi/wicketd.json @@ -1483,6 +1483,7 @@ ] }, "ClearUpdateStateResponse": { + "description": "Response to an instruction to clear update data.", "type": "object", "properties": { "cleared": { diff --git a/sp-sim/src/lib.rs b/sp-sim/src/lib.rs index cf6c7b1997e..9546ffb0473 100644 --- a/sp-sim/src/lib.rs +++ b/sp-sim/src/lib.rs @@ -33,6 +33,10 @@ pub use update::HostFlashHashPolicy; pub const SIM_ROT_BOARD: &str = "SimRot"; +/// The staging/devel key signature reported in simulated RoT/stage0 cabooses. +pub const ROT_STAGING_DEVEL_SIGN: &str = + "11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf"; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Responsiveness { Responsive, diff --git a/sp-sim/src/update.rs b/sp-sim/src/update.rs index e6138eef016..acea690ca1a 100644 --- a/sp-sim/src/update.rs +++ b/sp-sim/src/update.rs @@ -8,6 +8,7 @@ use std::mem; use std::time::Duration; use std::time::Instant; +use crate::ROT_STAGING_DEVEL_SIGN; use crate::SIM_GIMLET_BOARD; use crate::SIM_ROT_BOARD; use crate::SIM_SIDECAR_BOARD; @@ -90,9 +91,6 @@ impl SimSpUpdate { const ROT_GITC1: &str = "edededed"; const ROT_VERS0: &str = "0.0.4"; const ROT_VERS1: &str = "0.0.3"; - // staging/devel key signature - const ROT_STAGING_DEVEL_SIGN: &str = - "11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf"; const STAGE0_GITC0: &str = "ddddddddd"; const STAGE0_GITC1: &str = "dadadadad"; diff --git a/wicket-common/src/rack_update.rs b/wicket-common/src/rack_update.rs index ee21ae10f52..65fe11058a6 100644 --- a/wicket-common/src/rack_update.rs +++ b/wicket-common/src/rack_update.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use std::{collections::BTreeSet, time::Duration}; +use std::time::Duration; use semver::Version; @@ -80,17 +80,6 @@ pub struct AbortUpdateOptions { pub test_error: Option, } -#[derive( - Clone, Debug, Default, PartialEq, Eq, JsonSchema, Serialize, Deserialize, -)] -pub struct ClearUpdateStateResponse { - /// The SPs for which update data was cleared. - pub cleared: BTreeSet, - - /// The SPs that had no update state to clear. - pub no_update_data: BTreeSet, -} - #[derive( Copy, Clone, Debug, JsonSchema, Deserialize, Serialize, PartialEq, Eq, )] diff --git a/wicket/src/cli/rack_update.rs b/wicket/src/cli/rack_update.rs index c648e04ff62..149d7e72900 100644 --- a/wicket/src/cli/rack_update.rs +++ b/wicket/src/cli/rack_update.rs @@ -37,8 +37,8 @@ use tokio::{sync::watch, task::JoinHandle}; use wicket_common::{ WICKETD_TIMEOUT, rack_update::{ - ClearUpdateStateResponse, ComponentUpdateStatus, ExitMessage, - RackUpdateStatus, UpdateState, UpdateStateCounts, rollup_update_state, + ComponentUpdateStatus, ExitMessage, RackUpdateStatus, UpdateState, + UpdateStateCounts, rollup_update_state, }, update_events::{EventReport, WicketdEngineSpec}, }; @@ -46,7 +46,9 @@ use wicketd_client::types::{ ClearUpdateStateParams, GetArtifactsAndEventReportsResponse, StartUpdateParams, }; -use wicketd_commission_types::update::UpdateTargets; +use wicketd_commission_types::update::{ + ClearUpdateStateResponse, UpdateTargets, +}; use super::command::CommandOutput; diff --git a/wicketd-api/src/lib.rs b/wicketd-api/src/lib.rs index a935f84444b..6dd3cc61610 100644 --- a/wicketd-api/src/lib.rs +++ b/wicketd-api/src/lib.rs @@ -34,12 +34,12 @@ use wicket_common::rack_setup::CurrentRssUserConfigInsensitive; use wicket_common::rack_setup::GetBgpAuthKeyInfoResponse; use wicket_common::rack_update::AbortUpdateOptions; use wicket_common::rack_update::ClearUpdateStateOptions; -use wicket_common::rack_update::ClearUpdateStateResponse; use wicket_common::rack_update::StartUpdateOptions; use wicket_common::update_events::EventReport; use wicketd_commission_types::rack_setup::BgpAuthKeyId; use wicketd_commission_types::rack_setup::CertificateUploadResponse; use wicketd_commission_types::rack_setup::PutRssUserConfigInsensitive; +use wicketd_commission_types::update::ClearUpdateStateResponse; use wicketd_commission_types::update::UpdateTargets; /// Full release repositories are currently (Dec 2024) 1.8 GiB and are likely to diff --git a/wicketd-commission-types/versions/src/initial/update.rs b/wicketd-commission-types/versions/src/initial/update.rs index 4df7a577ee6..f0bb3856c75 100644 --- a/wicketd-commission-types/versions/src/initial/update.rs +++ b/wicketd-commission-types/versions/src/initial/update.rs @@ -71,6 +71,18 @@ impl JsonSchema for UpdateTargets { } } +/// Response to an instruction to clear update data. +#[derive( + Clone, Debug, Default, PartialEq, Eq, JsonSchema, Serialize, Deserialize, +)] +pub struct ClearUpdateStateResponse { + /// The SPs for which update data was cleared. + pub cleared: BTreeSet, + + /// The SPs that had no update state to clear. + pub no_update_data: BTreeSet, +} + /// Error returned when UpdateTargets is constructed from an empty set. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EmptyUpdateTargets; diff --git a/wicketd-commission-types/versions/src/latest.rs b/wicketd-commission-types/versions/src/latest.rs index 2b4383a8a1e..f433d8ddcf9 100644 --- a/wicketd-commission-types/versions/src/latest.rs +++ b/wicketd-commission-types/versions/src/latest.rs @@ -35,6 +35,7 @@ pub mod rack_setup { } pub mod update { + pub use crate::v1::update::ClearUpdateStateResponse; pub use crate::v1::update::EmptyUpdateTargets; pub use crate::v1::update::UpdateTargets; } 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..da94c7df1c3 100644 --- a/wicketd/src/http_entrypoints.rs +++ b/wicketd/src/http_entrypoints.rs @@ -9,7 +9,6 @@ use crate::context::CommonConfigContainer; use crate::context::RssOrMultirackJoinConfig; use crate::http_helpers::ba_lockstep_client; use crate::http_helpers::ba_lockstep_error_to_http; -use crate::http_helpers::inventory_err_to_http; use crate::http_helpers::mgs_inventory_or_unavail; use crate::http_helpers::start_update; use crate::mgs::GetInventoryResponse as GetMgsInventoryResponse; @@ -41,11 +40,11 @@ use wicket_common::multirack_setup::CurrentMultirackJoinUserConfig; use wicket_common::multirack_setup::MultirackJoinConfigBaseUserInput; use wicket_common::rack_setup::GetBgpAuthKeyInfoResponse; use wicket_common::rack_update::AbortUpdateOptions; -use wicket_common::rack_update::ClearUpdateStateResponse; use wicket_common::update_events::EventReport; use wicketd_api::*; use wicketd_commission_types::rack_setup::CertificateUploadResponse; use wicketd_commission_types::rack_setup::PutRssUserConfigInsensitive; +use wicketd_commission_types::update::ClearUpdateStateResponse; use crate::ServerContext; @@ -389,17 +388,39 @@ impl WicketdApi for WicketdApiImpl { }) => Some((inventory, mgs_last_seen)), Ok(GetMgsInventoryResponse::Unavailable) => None, Err(err) => { - return Err(inventory_err_to_http(err)); + return Err(err.to_http_error()); } }; // Fetch the transceiver information from the SP. let maybe_transceiver_inventory = match rqctx.context().transceiver_handle.get_transceivers() { - GetTransceiversResponse::Response { - transceivers, - transceivers_last_seen, - } => Some((transceivers, transceivers_last_seen)), + GetTransceiversResponse::Response { transceivers } => { + // transceivers tracks the last_seen for each switch + // independently. But the (currently frozen) wicketd API + // wire shape only has a single last_seen field. So we must + // pick: min or max? We choose max here, so that if one of + // the fetch tasks is wedged, the timestamp indicates that. + // + // TODO: clean this up (report per-switch last_seen) once + // rkdeploy is on the stable commissioning API. + let last_seen = transceivers + .iter() + .map(|switch| switch.updated_at.elapsed()) + .max(); + last_seen.map(|last_seen| { + // The (currently frozen) wicketd API is a HashMap, so + // collect into that. + // + // TODO: switch to IdOrdMap once rkdeploy is on the + // stable commissioning API. + let inventory = transceivers + .into_iter() + .map(|switch| (switch.switch, switch.transceivers)) + .collect(); + (inventory, last_seen) + }) + } GetTransceiversResponse::Unavailable => None, }; @@ -459,7 +480,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 +616,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 +625,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..1e698186304 100644 --- a/wicketd/src/http_helpers.rs +++ b/wicketd/src/http_helpers.rs @@ -24,7 +24,6 @@ use wicketd_commission_types::update::UpdateTargets; use crate::ServerContext; use crate::helpers::SpIdentifierDisplay; use crate::helpers::sps_to_string; -use crate::mgs::GetInventoryError; use crate::mgs::GetInventoryResponse; use crate::mgs::MgsHandle; use crate::mgs::ShutdownInProgress; @@ -59,19 +58,6 @@ pub(crate) fn shutdown_to_http(_err: ShutdownInProgress) -> HttpError { ) } -pub(crate) fn inventory_err_to_http(err: GetInventoryError) -> HttpError { - match err { - GetInventoryError::ShutdownInProgress => { - shutdown_to_http(ShutdownInProgress) - } - GetInventoryError::InvalidSpIdentifier => http_error_with_message( - ErrorStatusCode::SERVICE_UNAVAILABLE, - None, - "Invalid SP identifier in request".to_owned(), - ), - } -} - pub(crate) fn ba_lockstep_client( ctx: &ServerContext, ) -> Result { @@ -163,7 +149,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, diff --git a/wicketd/src/mgs.rs b/wicketd/src/mgs.rs index d105379f435..db463ba08a4 100644 --- a/wicketd/src/mgs.rs +++ b/wicketd/src/mgs.rs @@ -5,6 +5,7 @@ //! The collection of tasks used for interacting with MGS and maintaining //! runtime state. +use dropshot::HttpError; use futures::StreamExt; use gateway_types::ignition::SpIgnition; use slog::{Logger, info, o, warn}; @@ -15,6 +16,10 @@ use tokio::time::{Duration, Instant}; use tokio_stream::StreamMap; use wicket_common::inventory::{MgsV1Inventory, SpIdentifier, SpInventory}; +use crate::helpers::SpIdentifierDisplay; +use crate::http_helpers::http_error_with_message; +use crate::http_helpers::shutdown_to_http; + use self::inventory::{ FetchedIgnitionState, FetchedSpData, IgnitionPresence, IgnitionStateFetcher, SpStateFetcher, @@ -68,7 +73,30 @@ pub enum GetInventoryError { /// The client specified an invalid SP identifier in a `force_refresh` /// request. - InvalidSpIdentifier, + InvalidSpIdentifier { + /// The invalid SP identifier. + id: SpIdentifier, + }, +} + +impl GetInventoryError { + pub(crate) fn to_http_error(&self) -> HttpError { + match self { + GetInventoryError::ShutdownInProgress => { + shutdown_to_http(ShutdownInProgress) + } + GetInventoryError::InvalidSpIdentifier { id } => { + http_error_with_message( + dropshot::ErrorStatusCode::BAD_REQUEST, + None, + format!( + "invalid SP identifier in force_refresh request: {}", + SpIdentifierDisplay(*id) + ), + ) + } + } + } } impl MgsHandle { @@ -80,10 +108,12 @@ impl MgsHandle { Err(GetInventoryError::ShutdownInProgress) => { Err(ShutdownInProgress) } - Err(GetInventoryError::InvalidSpIdentifier) => { + Err(GetInventoryError::InvalidSpIdentifier { id }) => { // We pass no SP identifiers to refresh, so it's not possible // for one of them to be invalid. - unreachable!("empty SP list cannot contain an invalid ID"); + unreachable!( + "empty SP list cannot contain an invalid ID, but got {id:?}" + ); } } } @@ -273,7 +303,15 @@ impl MgsManager { } } + /// Drop waiters whose receiver is closed (e.g., the HTTP request timed out) + /// so they don't pile up in case a wedged SP never refreshes. + fn prune_dead_waiters(&mut self) { + self.waiting_for_update.retain(|waiter| !waiter.reply_tx.is_closed()); + } + fn check_completed_waiters(&mut self, mgs_last_seen: Instant) { + self.prune_dead_waiters(); + // This really wants `Vec::drain_filter()`, but it's unstable; instead, // use its sample code (but use `swap_remove()` instead of `remove()` // because we don't care about order). @@ -302,6 +340,8 @@ impl MgsManager { >, force_refresh: Vec, ) { + self.prune_dead_waiters(); + if force_refresh.is_empty() { // No force refresh: just return our latest cached inventory. _ = reply_tx.send(Ok(self.current_inventory(mgs_last_seen))); @@ -311,7 +351,8 @@ impl MgsManager { // Trigger immediate refreshes for all SPs listed in `force_refresh`. for &id in &force_refresh { let Some(handle) = sp_handles.get(&id) else { - _ = reply_tx.send(Err(GetInventoryError::InvalidSpIdentifier)); + _ = reply_tx + .send(Err(GetInventoryError::InvalidSpIdentifier { id })); return; }; @@ -403,3 +444,61 @@ struct WaitingForRefresh { sps_to_refresh: BTreeSet, need_ignition_refresh: bool, } + +#[cfg(test)] +mod tests { + use super::*; + use std::net::Ipv6Addr; + use wicket_common::inventory::SpType; + + fn dummy_manager() -> MgsManager { + let log = Logger::root(slog::Discard, o!()); + let addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0); + MgsManager::new(&log, addr) + } + + fn sled(slot: u16) -> SpIdentifier { + SpIdentifier { typ: SpType::Sled, slot } + } + + #[test] + fn prune_dead_waiters_drops_only_closed_receivers() { + let mut manager = dummy_manager(); + + // Simulate a dead waiter with a closed receiver and a non-empty + // sps_to_refresh. + let (dead_tx, dead_rx) = oneshot::channel(); + drop(dead_rx); + manager.waiting_for_update.push(WaitingForRefresh { + reply_tx: dead_tx, + sps_to_refresh: std::iter::once(sled(0)).collect(), + need_ignition_refresh: true, + }); + + // Similarly, simulate a live waiter. + let (live_tx, _live_rx) = oneshot::channel(); + manager.waiting_for_update.push(WaitingForRefresh { + reply_tx: live_tx, + sps_to_refresh: std::iter::once(sled(1)).collect(), + need_ignition_refresh: true, + }); + + manager.prune_dead_waiters(); + + assert_eq!( + manager.waiting_for_update.len(), + 1, + "only the live waiter remains after pruning", + ); + let remaining = &manager.waiting_for_update[0]; + assert!( + !remaining.reply_tx.is_closed(), + "the surviving waiter still has a live receiver", + ); + assert_eq!( + remaining.sps_to_refresh, + std::iter::once(sled(1)).collect::>(), + "the surviving waiter is the one waiting on sled 1", + ); + } +} diff --git a/wicketd/src/transceivers.rs b/wicketd/src/transceivers.rs index 128fdd38613..c6c39ea6904 100644 --- a/wicketd/src/transceivers.rs +++ b/wicketd/src/transceivers.rs @@ -5,11 +5,11 @@ //! Fetching transceiver state from the SP. use gateway_types::component::SpIdentifier; +use iddqd::{IdOrdItem, IdOrdMap, id_ord_map, id_upcast}; use sled_agent_types::early_networking::SwitchSlot; use slog::{Logger, debug, error}; use slog_error_chain::InlineErrorChain; use std::{ - collections::HashMap, sync::{Arc, Mutex}, time::Duration, }; @@ -24,7 +24,30 @@ use transceiver_controller::{SpRequest, message::ExtendedStatus}; use wicket_common::inventory::{SpType, Transceiver}; /// Type alias for a map of all transceivers on each switch. -pub type TransceiverMap = HashMap>; +pub type TransceiverMap = IdOrdMap; + +/// Item in a [`TransceiverMap`]. +/// +/// This tracks the state of all transceivers on a single switch. +#[derive(Clone, Debug)] +pub struct SwitchTransceivers { + /// The switch slot. + pub switch: SwitchSlot, + /// The list of transceivers on this switch. + pub transceivers: Vec, + /// The last time we fetched transceivers from this switch. + pub updated_at: Instant, +} + +impl IdOrdItem for SwitchTransceivers { + type Key<'a> = SwitchSlot; + + fn key(&self) -> Self::Key<'_> { + self.switch + } + + id_upcast!(); +} // Queue size for passing messages between transceiver fetch task. const CHANNEL_CAPACITY: usize = 4; @@ -50,7 +73,7 @@ const OTHER_SWITCH_SP_INTERFACE: &str = "sidecar1"; #[derive(Clone, Debug)] pub enum GetTransceiversResponse { - Response { transceivers: TransceiverMap, transceivers_last_seen: Duration }, + Response { transceivers: TransceiverMap }, Unavailable, } @@ -171,22 +194,21 @@ impl Manager { error!(self.log, "all transceiver fetch tasks have exited"); return; }; + let update = SwitchTransceivers { + switch: switch_slot, + transceivers: these_transceivers, + updated_at, + }; let mut transceivers_by_switch = self.transceivers.lock().unwrap(); match &mut *transceivers_by_switch { - GetTransceiversResponse::Response { - transceivers, - transceivers_last_seen, - } => { - transceivers.insert(switch_slot, these_transceivers); - *transceivers_last_seen = updated_at.elapsed(); + GetTransceiversResponse::Response { transceivers } => { + transceivers.insert_overwrite(update); } GetTransceiversResponse::Unavailable => { - let mut all_transceivers = TransceiverMap::new(); - all_transceivers.insert(switch_slot, these_transceivers); + let all_transceivers = id_ord_map! { update }; *transceivers_by_switch = GetTransceiversResponse::Response { transceivers: all_transceivers, - transceivers_last_seen: updated_at.elapsed(), }; } } diff --git a/wicketd/src/update_tracker.rs b/wicketd/src/update_tracker.rs index 7ba9dbc45c8..b5e241edcb8 100644 --- a/wicketd/src/update_tracker.rs +++ b/wicketd/src/update_tracker.rs @@ -72,7 +72,6 @@ use uuid::Uuid; use wicket_common::inventory::SpComponentCaboose; use wicket_common::inventory::SpIdentifier; use wicket_common::inventory::SpType; -use wicket_common::rack_update::ClearUpdateStateResponse; use wicket_common::rack_update::StartUpdateOptions; use wicket_common::rack_update::UpdateSimulatedResult; use wicket_common::update_events::ComponentRegistrar; @@ -98,6 +97,7 @@ use wicket_common::update_events::UpdateEngine; use wicket_common::update_events::UpdateStepId; use wicket_common::update_events::UpdateTerminalError; use wicketd_api::GetArtifactsAndEventReportsResponse; +use wicketd_commission_types::update::ClearUpdateStateResponse; use wicketd_commission_types::update::UpdateTargets; #[derive(Debug)] diff --git a/wicketd/tests/integration_tests/updates.rs b/wicketd/tests/integration_tests/updates.rs index c356716f580..ee8d6a85d55 100644 --- a/wicketd/tests/integration_tests/updates.rs +++ b/wicketd/tests/integration_tests/updates.rs @@ -36,8 +36,7 @@ use wicket::OutputKind; use wicket_common::{ inventory::{SpIdentifier, SpType}, rack_update::{ - ClearUpdateStateResponse, ExitMessage, RackUpdateStatus, - StartUpdateOptions, UpdateState, + ExitMessage, RackUpdateStatus, StartUpdateOptions, UpdateState, }, update_events::{StepEventKind, UpdateComponent}, }; @@ -45,7 +44,9 @@ use wicketd::{RunningUpdateState, StartUpdateError}; use wicketd_client::types::{ GetInventoryParams, GetInventoryResponse, StartUpdateParams, }; -use wicketd_commission_types::update::UpdateTargets; +use wicketd_commission_types::update::{ + ClearUpdateStateResponse, UpdateTargets, +}; /// The list of zone file names defined in fake-non-semver.toml. static FAKE_NON_SEMVER_ZONE_FILE_NAMES: &[&str] = &[