Skip to content

Commit 2561a7a

Browse files
authored
[27/n] [reconfigurator-cli] allow specifying "latest" for collection ID (#8536)
Also clean up code to diff blueprint versus inventory, an operation that makes less and less sense as time goes on.
1 parent 17e4c0e commit 2561a7a

File tree

5 files changed

+138
-47
lines changed

5 files changed

+138
-47
lines changed

dev-tools/reconfigurator-cli/src/lib.rs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use nexus_reconfigurator_planning::planner::Planner;
2222
use nexus_reconfigurator_planning::system::{
2323
SledBuilder, SledInventoryVisibility, SystemDescription,
2424
};
25-
use nexus_reconfigurator_simulation::{BlueprintId, SimState};
25+
use nexus_reconfigurator_simulation::{BlueprintId, CollectionId, SimState};
2626
use nexus_reconfigurator_simulation::{SimStateBuilder, SimTufRepoSource};
2727
use nexus_reconfigurator_simulation::{SimTufRepoDescription, Simulator};
2828
use nexus_sled_agent_shared::inventory::ZoneKind;
@@ -239,9 +239,6 @@ fn process_command(
239239
Commands::BlueprintShow(args) => cmd_blueprint_show(sim, args),
240240
Commands::BlueprintDiff(args) => cmd_blueprint_diff(sim, args),
241241
Commands::BlueprintDiffDns(args) => cmd_blueprint_diff_dns(sim, args),
242-
Commands::BlueprintDiffInventory(args) => {
243-
cmd_blueprint_diff_inventory(sim, args)
244-
}
245242
Commands::BlueprintSave(args) => cmd_blueprint_save(sim, args),
246243
Commands::Show => cmd_show(sim),
247244
Commands::Set(args) => cmd_set(sim, args),
@@ -312,8 +309,6 @@ enum Commands {
312309
BlueprintDiff(BlueprintDiffArgs),
313310
/// show differences between a blueprint and a particular DNS version
314311
BlueprintDiffDns(BlueprintDiffDnsArgs),
315-
/// show differences between a blueprint and an inventory collection
316-
BlueprintDiffInventory(BlueprintDiffInventoryArgs),
317312
/// write one blueprint to a file
318313
BlueprintSave(BlueprintSaveArgs),
319314

@@ -522,11 +517,11 @@ struct BlueprintPlanArgs {
522517
/// id of the blueprint on which this one will be based, "latest", or
523518
/// "target"
524519
parent_blueprint_id: BlueprintIdOpt,
525-
/// id of the inventory collection to use in planning
520+
/// id of the inventory collection to use in planning or "latest"
526521
///
527522
/// Must be provided unless there is only one collection in the loaded
528523
/// state.
529-
collection_id: Option<CollectionUuid>,
524+
collection_id: Option<CollectionIdOpt>,
530525
}
531526

532527
#[derive(Debug, Args)]
@@ -729,6 +724,34 @@ impl From<BlueprintIdOpt> for BlueprintId {
729724
}
730725
}
731726

727+
#[derive(Clone, Debug)]
728+
enum CollectionIdOpt {
729+
/// use the latest collection sorted by time created
730+
Latest,
731+
/// use a specific collection
732+
Id(CollectionUuid),
733+
}
734+
735+
impl FromStr for CollectionIdOpt {
736+
type Err = anyhow::Error;
737+
738+
fn from_str(s: &str) -> Result<Self, Self::Err> {
739+
match s {
740+
"latest" => Ok(CollectionIdOpt::Latest),
741+
_ => Ok(CollectionIdOpt::Id(s.parse()?)),
742+
}
743+
}
744+
}
745+
746+
impl From<CollectionIdOpt> for CollectionId {
747+
fn from(value: CollectionIdOpt) -> Self {
748+
match value {
749+
CollectionIdOpt::Latest => CollectionId::Latest,
750+
CollectionIdOpt::Id(id) => CollectionId::Id(id),
751+
}
752+
}
753+
}
754+
732755
/// Clap field for an optional mupdate override UUID.
733756
///
734757
/// This structure is similar to `Option`, but is specified separately to:
@@ -1467,10 +1490,13 @@ fn cmd_blueprint_plan(
14671490

14681491
let parent_blueprint_id =
14691492
system.resolve_blueprint_id(args.parent_blueprint_id.into())?;
1470-
let collection_id = args.collection_id;
14711493
let parent_blueprint = system.get_blueprint(&parent_blueprint_id)?;
1472-
let collection = match collection_id {
1473-
Some(collection_id) => system.get_collection(collection_id)?,
1494+
let collection = match args.collection_id {
1495+
Some(collection_id) => {
1496+
let resolved =
1497+
system.resolve_collection_id(collection_id.into())?;
1498+
system.get_collection(&resolved)?
1499+
}
14741500
None => {
14751501
let mut all_collections_iter = system.all_collections();
14761502
match all_collections_iter.len() {
@@ -1888,23 +1914,6 @@ fn cmd_blueprint_diff_dns(
18881914
Ok(Some(dns_diff.to_string()))
18891915
}
18901916

1891-
fn cmd_blueprint_diff_inventory(
1892-
sim: &mut ReconfiguratorSim,
1893-
args: BlueprintDiffInventoryArgs,
1894-
) -> anyhow::Result<Option<String>> {
1895-
let collection_id = args.collection_id;
1896-
let blueprint_id = args.blueprint_id;
1897-
1898-
let state = sim.current_state();
1899-
let _collection = state.system().get_collection(collection_id)?;
1900-
let _blueprint =
1901-
state.system().resolve_and_get_blueprint(blueprint_id.into())?;
1902-
// See https://github.com/oxidecomputer/omicron/issues/7242
1903-
// let diff = blueprint.diff_since_collection(&collection);
1904-
// Ok(Some(diff.display().to_string()))
1905-
bail!("Not Implemented")
1906-
}
1907-
19081917
fn cmd_blueprint_save(
19091918
sim: &mut ReconfiguratorSim,
19101919
args: BlueprintSaveArgs,

dev-tools/reconfigurator-cli/tests/input/cmds-noop-image-source.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@ sled-set serial6 inventory-hidden
5050

5151
# Generate an inventory and run a blueprint planning step.
5252
inventory-generate
53-
blueprint-plan latest eb0796d5-ab8a-4f7b-a884-b4aeacb8ab51
53+
blueprint-plan latest latest
5454

5555
# This diff should show expected changes to the blueprint.
56-
blueprint-diff 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1 latest
56+
blueprint-diff latest
5757

5858
# Bring the hidden sled back.
5959
sled-set serial6 inventory-visible
6060

6161
# Run another inventory and blueprint planning step.
6262
inventory-generate
63-
blueprint-plan latest 61f451b3-2121-4ed6-91c7-a550054f6c21
63+
blueprint-plan latest latest
6464

6565
# This diff should show changes to the sled that's back in inventory.
66-
blueprint-diff 58d5e830-0884-47d8-a7cd-b2b3751adeb4 latest
66+
blueprint-diff latest

dev-tools/reconfigurator-cli/tests/output/cmds-noop-image-source-stdout

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ set sled e96e226f-4ed9-4c01-91b9-69a9cd076c9e inventory visibility: visible -> h
154154
> inventory-generate
155155
generated inventory collection eb0796d5-ab8a-4f7b-a884-b4aeacb8ab51 from configured sleds
156156

157-
> blueprint-plan latest eb0796d5-ab8a-4f7b-a884-b4aeacb8ab51
157+
> blueprint-plan latest latest
158158
WARN skipping zones eligible for cleanup check (sled not present in latest inventory collection), sled_id: e96e226f-4ed9-4c01-91b9-69a9cd076c9e
159159
WARN skipping noop image source check since sled-agent encountered error retrieving zone manifest (this is abnormal), sled_id: 2b8f0cb3-0295-4b3c-bc58-4fe88b57112c, error: reconfigurator-sim simulated error: simulated error obtaining zone manifest
160160
INFO install dataset artifact hash matches TUF repo, switching out the zone image source to Artifact, sled_id: 98e6b7c2-2efa-41ca-b20a-0a4d61102fe6, tuf_artifact_id: nexus v1.0.0 (zone)
@@ -192,7 +192,7 @@ generated blueprint 58d5e830-0884-47d8-a7cd-b2b3751adeb4 based on parent bluepri
192192

193193

194194
> # This diff should show expected changes to the blueprint.
195-
> blueprint-diff 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1 latest
195+
> blueprint-diff latest
196196
from: blueprint 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1
197197
to: blueprint 58d5e830-0884-47d8-a7cd-b2b3751adeb4
198198

@@ -510,7 +510,7 @@ set sled e96e226f-4ed9-4c01-91b9-69a9cd076c9e inventory visibility: hidden -> vi
510510
> inventory-generate
511511
generated inventory collection 61f451b3-2121-4ed6-91c7-a550054f6c21 from configured sleds
512512

513-
> blueprint-plan latest 61f451b3-2121-4ed6-91c7-a550054f6c21
513+
> blueprint-plan latest latest
514514
WARN skipping noop image source check since sled-agent encountered error retrieving zone manifest (this is abnormal), sled_id: 2b8f0cb3-0295-4b3c-bc58-4fe88b57112c, error: reconfigurator-sim simulated error: simulated error obtaining zone manifest
515515
INFO noop converting 0/0 install-dataset zones to artifact store, sled_id: 98e6b7c2-2efa-41ca-b20a-0a4d61102fe6
516516
WARN zone manifest inventory indicated install dataset artifact is invalid, not using artifact (this is abnormal), sled_id: aff6c093-197d-42c5-ad80-9f10ba051a34, zone_id: e8fe709c-725f-4bb2-b714-ffcda13a9e54, kind: internal_ntp, file_name: ntp.tar.gz, error: reconfigurator-sim: simulated error validating zone image
@@ -537,7 +537,7 @@ generated blueprint af934083-59b5-4bf6-8966-6fb5292c29e1 based on parent bluepri
537537

538538

539539
> # This diff should show changes to the sled that's back in inventory.
540-
> blueprint-diff 58d5e830-0884-47d8-a7cd-b2b3751adeb4 latest
540+
> blueprint-diff latest
541541
from: blueprint 58d5e830-0884-47d8-a7cd-b2b3751adeb4
542542
to: blueprint af934083-59b5-4bf6-8966-6fb5292c29e1
543543

nexus/reconfigurator/simulation/src/errors.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use std::collections::BTreeSet;
66

77
use itertools::Itertools;
88
use omicron_common::api::external::{Generation, Name};
9-
use omicron_uuid_kinds::CollectionUuid;
109
use thiserror::Error;
1110

12-
use crate::{BlueprintId, ResolvedBlueprintId};
11+
use crate::{
12+
BlueprintId, CollectionId, ResolvedBlueprintId, ResolvedCollectionId,
13+
};
1314

1415
/// The caller attempted to insert a duplicate key.
1516
#[derive(Clone, Debug, Error)]
@@ -23,7 +24,7 @@ impl DuplicateError {
2324
&self.id
2425
}
2526

26-
pub(crate) fn collection(id: CollectionUuid) -> Self {
27+
pub(crate) fn collection(id: CollectionId) -> Self {
2728
Self { id: ObjectId::Collection(id) }
2829
}
2930

@@ -46,8 +47,9 @@ impl DuplicateError {
4647

4748
#[derive(Clone, Debug)]
4849
pub enum ObjectId {
49-
Collection(CollectionUuid),
50+
Collection(CollectionId),
5051
Blueprint(BlueprintId),
52+
ResolvedCollection(ResolvedCollectionId),
5153
ResolvedBlueprint(ResolvedBlueprintId),
5254
InternalDns(Generation),
5355
ExternalDns(Generation),
@@ -57,7 +59,10 @@ pub enum ObjectId {
5759
impl ObjectId {
5860
fn to_error_string(&self) -> String {
5961
match self {
60-
ObjectId::Collection(id) => {
62+
ObjectId::Collection(CollectionId::Latest) => {
63+
"no latest collection found".to_string()
64+
}
65+
ObjectId::Collection(CollectionId::Id(id)) => {
6166
format!("collection ID {id}")
6267
}
6368
ObjectId::Blueprint(BlueprintId::Latest) => {
@@ -69,6 +74,7 @@ impl ObjectId {
6974
ObjectId::Blueprint(BlueprintId::Id(id)) => {
7075
format!("blueprint ID {id}")
7176
}
77+
ObjectId::ResolvedCollection(id) => id.to_string(),
7278
ObjectId::ResolvedBlueprint(id) => id.to_string(),
7379
ObjectId::InternalDns(generation) => {
7480
format!("internal DNS at generation {generation}")
@@ -95,14 +101,18 @@ impl KeyError {
95101
&self.id
96102
}
97103

98-
pub(crate) fn collection(id: CollectionUuid) -> Self {
104+
pub(crate) fn collection(id: CollectionId) -> Self {
99105
Self { id: ObjectId::Collection(id) }
100106
}
101107

102108
pub(crate) fn blueprint(id: BlueprintId) -> Self {
103109
Self { id: ObjectId::Blueprint(id) }
104110
}
105111

112+
pub(crate) fn resolved_collection(id: ResolvedCollectionId) -> Self {
113+
Self { id: ObjectId::ResolvedCollection(id) }
114+
}
115+
106116
pub(crate) fn resolved_blueprint(id: ResolvedBlueprintId) -> Self {
107117
Self { id: ObjectId::ResolvedBlueprint(id) }
108118
}

nexus/reconfigurator/simulation/src/system.rs

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,32 @@ impl SimSystem {
121121
&self.description
122122
}
123123

124+
pub fn resolve_collection_id(
125+
&self,
126+
original: CollectionId,
127+
) -> Result<ResolvedCollectionId, KeyError> {
128+
let resolved = match original {
129+
CollectionId::Latest => {
130+
// The invariant of self.collections is that the last element is
131+
// the latest.
132+
let (id, _) = self
133+
.collections
134+
.last()
135+
.ok_or(KeyError::collection(original))?;
136+
*id
137+
}
138+
CollectionId::Id(id) => id,
139+
};
140+
Ok(ResolvedCollectionId { original, resolved })
141+
}
142+
124143
pub fn get_collection(
125144
&self,
126-
id: CollectionUuid,
145+
id: &ResolvedCollectionId,
127146
) -> Result<&Collection, KeyError> {
128-
match self.collections.get(&id) {
147+
match self.collections.get(&id.resolved()) {
129148
Some(c) => Ok(&**c),
130-
None => Err(KeyError::collection(id)),
149+
None => Err(KeyError::resolved_collection(id.clone())),
131150
}
132151
}
133152

@@ -261,10 +280,18 @@ impl SimSystemBuilder {
261280
&self.inner.system.description()
262281
}
263282

283+
#[inline]
284+
pub fn resolve_collection_id(
285+
&self,
286+
original: CollectionId,
287+
) -> Result<ResolvedCollectionId, KeyError> {
288+
self.inner.system.resolve_collection_id(original)
289+
}
290+
264291
#[inline]
265292
pub fn get_collection(
266293
&self,
267-
id: CollectionUuid,
294+
id: &ResolvedCollectionId,
268295
) -> Result<&Collection, KeyError> {
269296
self.inner.system.get_collection(id)
270297
}
@@ -489,6 +516,49 @@ impl fmt::Display for ResolvedBlueprintId {
489516
}
490517
}
491518

519+
/// An identifier for an inventory collection.
520+
#[derive(Clone, Copy, Debug)]
521+
pub enum CollectionId {
522+
/// The latest inventory collection added to the system.
523+
Latest,
524+
525+
/// The specified inventory collection.
526+
Id(CollectionUuid),
527+
}
528+
529+
/// An identifier for a blueprint after it has been resolved.
530+
#[derive(Clone, Debug)]
531+
pub struct ResolvedCollectionId {
532+
/// The original collection ID.
533+
original: CollectionId,
534+
535+
/// The resolved collection ID.
536+
resolved: CollectionUuid,
537+
}
538+
539+
impl ResolvedCollectionId {
540+
/// Return the original collection ID.
541+
pub fn original(&self) -> CollectionId {
542+
self.original
543+
}
544+
545+
/// Return the resolved collection ID.
546+
pub fn resolved(&self) -> CollectionUuid {
547+
self.resolved
548+
}
549+
}
550+
551+
impl fmt::Display for ResolvedCollectionId {
552+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553+
match self.original {
554+
CollectionId::Latest => {
555+
write!(f, "latest collection ({})", self.resolved)
556+
}
557+
CollectionId::Id(id) => write!(f, "collection {id}"),
558+
}
559+
}
560+
}
561+
492562
/// A log entry corresponding to an individual operation on a
493563
/// [`SimSystemBuilder`].
494564
#[derive(Clone, Debug)]
@@ -808,7 +878,9 @@ impl SimSystemBuilderInner {
808878
|_, other| other.time_started <= time_started,
809879
) {
810880
Ok(()) => Ok(()),
811-
Err(_) => Err(DuplicateError::collection(collection_id)),
881+
Err(_) => {
882+
Err(DuplicateError::collection(CollectionId::Id(collection_id)))
883+
}
812884
}
813885
}
814886

0 commit comments

Comments
 (0)