Skip to content

Commit 31c8803

Browse files
authored
internal API for fetching MgsUpdateDriver status does not work (#8475)
1 parent f73ab8e commit 31c8803

File tree

7 files changed

+177
-38
lines changed

7 files changed

+177
-38
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nexus/mgs-updates/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ chrono.workspace = true
1111
futures.workspace = true
1212
gateway-client.workspace = true
1313
gateway-types.workspace = true
14+
iddqd.workspace = true
1415
id-map.workspace = true
1516
internal-dns-resolver.workspace = true
1617
internal-dns-types.workspace = true

nexus/mgs-updates/src/driver.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use futures::future::BoxFuture;
1818
use futures::stream::FuturesUnordered;
1919
use id_map::IdMap;
2020
use id_map::IdMappable;
21+
use iddqd::IdOrdMap;
2122
use nexus_types::deployment::PendingMgsUpdate;
2223
use nexus_types::deployment::PendingMgsUpdates;
2324
use nexus_types::internal_api::views::CompletedAttempt;
@@ -31,7 +32,6 @@ use omicron_uuid_kinds::SpUpdateUuid;
3132
use qorb::resolver::AllBackends;
3233
use slog::{error, info, o, warn};
3334
use slog_error_chain::InlineErrorChain;
34-
use std::collections::BTreeMap;
3535
use std::collections::VecDeque;
3636
use std::sync::Arc;
3737
use std::time::Duration;
@@ -117,8 +117,8 @@ impl MgsUpdateDriver {
117117
) -> MgsUpdateDriver {
118118
let (status_tx, _) = watch::channel(MgsUpdateDriverStatus {
119119
recent: VecDeque::with_capacity(N_RECENT_COMPLETIONS),
120-
in_progress: BTreeMap::new(),
121-
waiting: BTreeMap::new(),
120+
in_progress: IdOrdMap::new(),
121+
waiting: IdOrdMap::new(),
122122
});
123123

124124
MgsUpdateDriver {
@@ -346,14 +346,15 @@ impl MgsUpdateDriver {
346346
// Update status. We do this before starting the future because it will
347347
// update this status and expects to find it.
348348
self.status_tx.send_modify(|driver_status| {
349-
driver_status.in_progress.insert(
350-
baseboard_id.clone(),
351-
InProgressUpdateStatus {
349+
driver_status
350+
.in_progress
351+
.insert_unique(InProgressUpdateStatus {
352+
baseboard_id: baseboard_id.clone(),
352353
time_started: in_progress.time_started,
353354
status: UpdateAttemptStatus::NotStarted,
354355
nattempts_done,
355-
},
356-
);
356+
})
357+
.expect("no previous update for this baseboard");
357358
});
358359

359360
let status_updater = UpdateAttemptStatusUpdater::new(
@@ -444,13 +445,14 @@ impl MgsUpdateDriver {
444445
assert!(found.is_some());
445446

446447
// Add this item to the list of requests waiting to be retried.
447-
driver_status.waiting.insert(
448-
baseboard_id.clone(),
449-
WaitingStatus {
448+
driver_status
449+
.waiting
450+
.insert_unique(WaitingStatus {
451+
baseboard_id: baseboard_id.clone(),
450452
next_attempt_time: status_time_next,
451453
nattempts_done,
452-
},
453-
);
454+
})
455+
.expect("no previous waiting update for this baseboard");
454456

455457
// Report this recently-completed attempt.
456458
// This is a ringbuffer of recent attempts. Make space if we're
@@ -577,7 +579,7 @@ impl UpdateAttemptStatusUpdater {
577579
// the future that owns it. The status entry for this future lives
578580
// in the `in_progress` struct until it completes. Thus, we should
579581
// always have a value here.
580-
let my_status =
582+
let mut my_status =
581583
driver_status.in_progress.get_mut(&self.baseboard_id).unwrap();
582584
my_status.status = new_status;
583585
});

nexus/mgs-updates/src/test_util/updates.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,15 @@ impl UpdateDescription<'_> {
190190
let (status_tx, status_rx) =
191191
watch::channel(MgsUpdateDriverStatus::default());
192192
status_tx.send_modify(|status| {
193-
status.in_progress.insert(
194-
baseboard_id.clone(),
195-
InProgressUpdateStatus {
193+
status
194+
.in_progress
195+
.insert_unique(InProgressUpdateStatus {
196+
baseboard_id: baseboard_id.clone(),
196197
time_started: chrono::Utc::now(),
197198
status: UpdateAttemptStatus::NotStarted,
198199
nattempts_done: 0,
199-
},
200-
);
200+
})
201+
.expect("no value present in object we just created");
201202
});
202203
let status_updater =
203204
UpdateAttemptStatusUpdater::new(status_tx.clone(), baseboard_id);
@@ -316,8 +317,7 @@ impl InProgressAttempt {
316317
// future itself to finish and then return indicating that it's
317318
// done.
318319
let overall_status = self.status_rx.borrow();
319-
let maybe_current_status =
320-
overall_status.in_progress.values().next();
320+
let maybe_current_status = overall_status.in_progress.iter().next();
321321
if let Some(current_status) = maybe_current_status {
322322
if current_status.status == status {
323323
debug!(

nexus/types/src/internal_api/views.rs

Lines changed: 113 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use chrono::SecondsFormat;
99
use chrono::Utc;
1010
use futures::future::ready;
1111
use futures::stream::StreamExt;
12+
use iddqd::IdOrdItem;
13+
use iddqd::IdOrdMap;
14+
use iddqd::id_upcast;
1215
use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory;
1316
use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult;
1417
use nexus_sled_agent_shared::inventory::OmicronZoneImageSource;
@@ -343,11 +346,13 @@ pub struct Ipv4NatEntryView {
343346

344347
/// Status of ongoing update attempts, recently completed attempts, and update
345348
/// requests that are waiting for retry.
346-
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)]
349+
#[derive(
350+
Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, JsonSchema,
351+
)]
347352
pub struct MgsUpdateDriverStatus {
348353
pub recent: VecDeque<CompletedAttempt>,
349-
pub in_progress: BTreeMap<Arc<BaseboardId>, InProgressUpdateStatus>,
350-
pub waiting: BTreeMap<Arc<BaseboardId>, WaitingStatus>,
354+
pub in_progress: IdOrdMap<InProgressUpdateStatus>,
355+
pub waiting: IdOrdMap<WaitingStatus>,
351356
}
352357

353358
impl MgsUpdateDriverStatus {
@@ -384,7 +389,7 @@ impl Display for MgsUpdateDriverStatusDisplay<'_> {
384389
}
385390

386391
writeln!(f, "\ncurrently in progress:")?;
387-
for (baseboard_id, status) in &status.in_progress {
392+
for status in &status.in_progress {
388393
// Ignore units smaller than a millisecond.
389394
let elapsed = Duration::from_millis(
390395
u64::try_from(
@@ -398,19 +403,19 @@ impl Display for MgsUpdateDriverStatusDisplay<'_> {
398403
status
399404
.time_started
400405
.to_rfc3339_opts(SecondsFormat::Millis, true),
401-
baseboard_id.serial_number,
406+
status.baseboard_id.serial_number,
402407
status.status,
403408
status.nattempts_done + 1,
404409
humantime::format_duration(elapsed),
405410
)?;
406411
}
407412

408413
writeln!(f, "\nwaiting for retry:")?;
409-
for (baseboard_id, wait_info) in &status.waiting {
414+
for wait_info in &status.waiting {
410415
writeln!(
411416
f,
412417
" serial {}: will try again at {} (attempt {})",
413-
baseboard_id.serial_number,
418+
wait_info.baseboard_id.serial_number,
414419
wait_info.next_attempt_time,
415420
wait_info.nattempts_done + 1,
416421
)?;
@@ -421,13 +426,13 @@ impl Display for MgsUpdateDriverStatusDisplay<'_> {
421426
}
422427

423428
/// externally-exposed status for a completed attempt
424-
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
429+
#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Serialize, JsonSchema)]
425430
pub struct CompletedAttempt {
426431
pub time_started: DateTime<Utc>,
427432
pub time_done: DateTime<Utc>,
428433
pub elapsed: Duration,
429434
pub request: PendingMgsUpdate,
430-
#[serde(serialize_with = "snake_case_result::serialize")]
435+
#[serde(with = "snake_case_result")]
431436
#[schemars(
432437
schema_with = "SnakeCaseResult::<UpdateCompletedHow, String>::json_schema"
433438
)]
@@ -447,13 +452,24 @@ pub enum UpdateCompletedHow {
447452
}
448453

449454
/// externally-exposed status for each in-progress update
450-
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
455+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, JsonSchema)]
451456
pub struct InProgressUpdateStatus {
457+
pub baseboard_id: Arc<BaseboardId>,
452458
pub time_started: DateTime<Utc>,
453459
pub status: UpdateAttemptStatus,
454460
pub nattempts_done: u32,
455461
}
456462

463+
impl IdOrdItem for InProgressUpdateStatus {
464+
type Key<'a> = &'a Arc<BaseboardId>;
465+
466+
fn key(&self) -> Self::Key<'_> {
467+
&self.baseboard_id
468+
}
469+
470+
id_upcast!();
471+
}
472+
457473
/// status of a single update attempt
458474
#[derive(
459475
Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, JsonSchema,
@@ -471,12 +487,23 @@ pub enum UpdateAttemptStatus {
471487
}
472488

473489
/// externally-exposed status for waiting updates
474-
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
490+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, JsonSchema)]
475491
pub struct WaitingStatus {
492+
pub baseboard_id: Arc<BaseboardId>,
476493
pub next_attempt_time: DateTime<Utc>,
477494
pub nattempts_done: u32,
478495
}
479496

497+
impl IdOrdItem for WaitingStatus {
498+
type Key<'a> = &'a Arc<BaseboardId>;
499+
500+
fn key(&self) -> Self::Key<'_> {
501+
&self.baseboard_id
502+
}
503+
504+
id_upcast!();
505+
}
506+
480507
#[derive(
481508
Debug,
482509
Clone,
@@ -594,3 +621,78 @@ impl UpdateStatus {
594621
ZoneStatusVersion::Unknown
595622
}
596623
}
624+
625+
#[cfg(test)]
626+
mod test {
627+
use super::CompletedAttempt;
628+
use super::InProgressUpdateStatus;
629+
use super::MgsUpdateDriverStatus;
630+
use super::UpdateCompletedHow;
631+
use super::WaitingStatus;
632+
use crate::deployment::ExpectedVersion;
633+
use crate::deployment::PendingMgsUpdate;
634+
use crate::deployment::PendingMgsUpdateDetails;
635+
use crate::internal_api::views::UpdateAttemptStatus;
636+
use crate::inventory::BaseboardId;
637+
use chrono::Utc;
638+
use gateway_client::types::SpType;
639+
use std::collections::VecDeque;
640+
use std::sync::Arc;
641+
use std::time::Instant;
642+
use tufaceous_artifact::ArtifactHash;
643+
644+
#[test]
645+
fn test_can_serialize_mgs_updates() {
646+
let start = Instant::now();
647+
let artifact_hash: ArtifactHash =
648+
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
649+
.parse()
650+
.unwrap();
651+
let baseboard_id = Arc::new(BaseboardId {
652+
part_number: String::from("a_port"),
653+
serial_number: String::from("a_serial"),
654+
});
655+
let waiting = WaitingStatus {
656+
baseboard_id: baseboard_id.clone(),
657+
next_attempt_time: Utc::now(),
658+
nattempts_done: 2,
659+
};
660+
let in_progress = InProgressUpdateStatus {
661+
baseboard_id: baseboard_id.clone(),
662+
time_started: Utc::now(),
663+
status: UpdateAttemptStatus::Updating,
664+
nattempts_done: 3,
665+
};
666+
let completed = CompletedAttempt {
667+
time_started: Utc::now(),
668+
time_done: Utc::now(),
669+
elapsed: start.elapsed(),
670+
request: PendingMgsUpdate {
671+
baseboard_id: baseboard_id.clone(),
672+
sp_type: SpType::Sled,
673+
slot_id: 12,
674+
details: PendingMgsUpdateDetails::Sp {
675+
expected_active_version: "1.0.0".parse().unwrap(),
676+
expected_inactive_version: ExpectedVersion::NoValidVersion,
677+
},
678+
artifact_hash,
679+
artifact_version: "2.0.0".parse().unwrap(),
680+
},
681+
result: Ok(UpdateCompletedHow::FoundNoChangesNeeded),
682+
nattempts_done: 8,
683+
};
684+
685+
let status = MgsUpdateDriverStatus {
686+
recent: VecDeque::from([completed]),
687+
in_progress: std::iter::once(in_progress).collect(),
688+
waiting: std::iter::once(waiting).collect(),
689+
};
690+
691+
let serialized =
692+
serde_json::to_string(&status).expect("failed to serialize value");
693+
let deserialized: MgsUpdateDriverStatus =
694+
serde_json::from_str(&serialized)
695+
.expect("failed to deserialize value");
696+
assert_eq!(deserialized, status);
697+
}
698+
}

nexus/types/src/inventory.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ impl Collection {
231231
Diffable,
232232
Ord,
233233
Eq,
234+
Hash,
234235
PartialOrd,
235236
PartialEq,
236237
Deserialize,

0 commit comments

Comments
 (0)