Skip to content

Commit 8fe7c9a

Browse files
committed
A couple of review fixes
1 parent d22da66 commit 8fe7c9a

File tree

1 file changed

+98
-11
lines changed

1 file changed

+98
-11
lines changed

nexus/db-queries/src/db/datastore/trust_quorum.rs

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use nexus_db_model::DbTypedUuid;
2020
use nexus_db_model::HwBaseboardId;
2121
use nexus_db_model::TrustQuorumConfiguration as DbTrustQuorumConfiguration;
2222
use nexus_db_model::TrustQuorumMember as DbTrustQuorumMember;
23+
use nexus_types::trust_quorum::TrustQuorumConfigState;
2324
use nexus_types::trust_quorum::{TrustQuorumConfig, TrustQuorumMemberData};
2425
use omicron_common::api::external::Error;
2526
use omicron_common::api::external::ListResultVec;
@@ -75,7 +76,7 @@ impl DataStore {
7576
pub async fn tq_get_all_rack_id_and_latest_epoch(
7677
&self,
7778
opctx: &OpContext,
78-
) -> ListResultVec<(RackUuid, Epoch)> {
79+
) -> Result<BTreeMap<RackUuid, Epoch>, Error> {
7980
opctx.authorize(authz::Action::ListChildren, &authz::FLEET).await?;
8081
let conn = &*self.pool_connection_authorized(opctx).await?;
8182

@@ -92,10 +93,9 @@ impl DataStore {
9293
public_error_from_diesel(e, ErrorHandler::Server)
9394
})?;
9495

95-
let mut output = Vec::with_capacity(values.len());
96-
96+
let mut output = BTreeMap::new();
9797
for (rack_id, epoch) in values {
98-
output.push((rack_id.into(), i64_to_epoch(epoch)?));
98+
output.insert(rack_id.into(), i64_to_epoch(epoch)?);
9999
}
100100

101101
Ok(output)
@@ -274,14 +274,71 @@ impl DataStore {
274274
.await
275275
.map_err(|txn_error| txn_error.into_diesel(&err))?;
276276

277+
// Some light sanity checking
278+
match config.state {
279+
TrustQuorumConfigState::Preparing
280+
| TrustQuorumConfigState::PreparingLrtqUpgrade => {}
281+
TrustQuorumConfigState::Committing
282+
| TrustQuorumConfigState::Committed
283+
| TrustQuorumConfigState::Aborted => {
284+
let state = config.state;
285+
bail_txn!(
286+
err,
287+
"Cannot insert configuration in state={:?}",
288+
state
289+
);
290+
}
291+
}
292+
277293
let is_insertable = if let Some(epoch) = current {
278294
// Only insert if what is in the DB is immediately prior to
279295
// this configuration.
280296
Some(epoch) == config.epoch.previous()
281297
} else {
282-
// Unconditional update is fine here, since a config
283-
// doesn't exist TODO: Should we ensure that epoch == 1
284-
// || epoch == 2 ?
298+
// We perform an unconditional insert here since
299+
// no existing configuration exists. However, the
300+
// configuration to be inserted is still subject to
301+
// some constraints.
302+
//
303+
// If there is no existing configuration, then the epoch
304+
// to be inserted must be either 1 or 2. It will be 1 if
305+
// this is a new initialization and 2 if this is an LRTQ
306+
// upgrade. Let's check both conditions here and return
307+
// an error if unmet.
308+
match config.state {
309+
TrustQuorumConfigState::Preparing => {
310+
let actual = config.epoch;
311+
let expected = Epoch(1);
312+
if actual != expected {
313+
bail_txn!(
314+
err,
315+
"Failed to insert first TQ
316+
configuration: invalid epoch for \
317+
state=preparing: Expected {}, Got {}",
318+
expected,
319+
actual
320+
);
321+
}
322+
}
323+
TrustQuorumConfigState::PreparingLrtqUpgrade => {
324+
let actual = config.epoch;
325+
let expected = Epoch(2);
326+
if actual != expected {
327+
bail_txn!(
328+
err,
329+
"Failed to insert first TQ
330+
configuration: invalid epoch for \
331+
state=preparing-lrtq-upgrade: \
332+
Expected {}, Got {}",
333+
expected,
334+
actual
335+
);
336+
}
337+
}
338+
_ => {
339+
// Already checked abbove
340+
}
341+
}
285342
true
286343
};
287344

@@ -1127,6 +1184,40 @@ mod tests {
11271184
.collect(),
11281185
};
11291186

1187+
// Create a couple of invalid configs andd try to insert them.
1188+
// They should return distinct errors.
1189+
let bad_config =
1190+
TrustQuorumConfig { epoch: Epoch(2), ..config.clone() };
1191+
let e1 = datastore
1192+
.tq_insert_latest_config(opctx, bad_config)
1193+
.await
1194+
.unwrap_err();
1195+
1196+
let bad_config = TrustQuorumConfig {
1197+
epoch: Epoch(3),
1198+
state: TrustQuorumConfigState::PreparingLrtqUpgrade,
1199+
..config.clone()
1200+
};
1201+
let e2 = datastore
1202+
.tq_insert_latest_config(opctx, bad_config)
1203+
.await
1204+
.unwrap_err();
1205+
1206+
let bad_config = TrustQuorumConfig {
1207+
state: TrustQuorumConfigState::Committing,
1208+
..config.clone()
1209+
};
1210+
let e3 = datastore
1211+
.tq_insert_latest_config(opctx, bad_config)
1212+
.await
1213+
.unwrap_err();
1214+
1215+
assert_ne!(e1, e2);
1216+
assert_ne!(e1, e3);
1217+
assert_ne!(e2, e3);
1218+
1219+
// Insert a valid config and watch it succeed
1220+
11301221
datastore.tq_insert_latest_config(opctx, config.clone()).await.unwrap();
11311222

11321223
let read_config = datastore
@@ -1612,10 +1703,6 @@ mod tests {
16121703

16131704
// We should have retrieved one epoch per rack_id
16141705
assert_eq!(values.len(), 3);
1615-
// Ensure all rack_ids are unique
1616-
let rack_ids: BTreeSet<_> =
1617-
values.iter().map(|(rack_id, _)| rack_id).collect();
1618-
assert_eq!(rack_ids.len(), 3);
16191706

16201707
// The epoch should be the latest that exists
16211708
for (rack_id, epoch) in values {

0 commit comments

Comments
 (0)