Skip to content

Commit 2054471

Browse files
authored
[2/n] [reconfigurator-cli] support 'latest' and 'target' as blueprint specifiers (#8128)
Especially when issuing a long series of edit commands as I'd like to do in a subsequent PR, having to specify each blueprint by hand in these scripts is a bit of a manual process. This symbolic identifier makes it easier to write scripts. Depends on #8121.
1 parent 3f5c3f3 commit 2054471

File tree

7 files changed

+339
-93
lines changed

7 files changed

+339
-93
lines changed

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

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use nexus_reconfigurator_planning::blueprint_builder::BlueprintBuilder;
1919
use nexus_reconfigurator_planning::example::ExampleSystemBuilder;
2020
use nexus_reconfigurator_planning::planner::Planner;
2121
use nexus_reconfigurator_planning::system::{SledBuilder, SystemDescription};
22-
use nexus_reconfigurator_simulation::SimState;
2322
use nexus_reconfigurator_simulation::SimStateBuilder;
2423
use nexus_reconfigurator_simulation::Simulator;
24+
use nexus_reconfigurator_simulation::{BlueprintId, SimState};
2525
use nexus_types::deployment::OmicronZoneNic;
2626
use nexus_types::deployment::PlanningInput;
2727
use nexus_types::deployment::SledFilter;
@@ -53,6 +53,7 @@ use std::borrow::Cow;
5353
use std::collections::BTreeMap;
5454
use std::fmt::Write;
5555
use std::io::IsTerminal;
56+
use std::str::FromStr;
5657
use swrite::{SWrite, swriteln};
5758
use tabled::Tabled;
5859
use tufaceous_artifact::ArtifactHash;
@@ -333,8 +334,9 @@ struct InventoryArgs {
333334

334335
#[derive(Debug, Args)]
335336
struct BlueprintPlanArgs {
336-
/// id of the blueprint on which this one will be based
337-
parent_blueprint_id: BlueprintUuid,
337+
/// id of the blueprint on which this one will be based, "latest", or
338+
/// "target"
339+
parent_blueprint_id: BlueprintIdOpt,
338340
/// id of the inventory collection to use in planning
339341
///
340342
/// Must be provided unless there is only one collection in the loaded
@@ -344,8 +346,8 @@ struct BlueprintPlanArgs {
344346

345347
#[derive(Debug, Args)]
346348
struct BlueprintEditArgs {
347-
/// id of the blueprint to edit
348-
blueprint_id: BlueprintUuid,
349+
/// id of the blueprint to edit, "latest", or "target"
350+
blueprint_id: BlueprintIdOpt,
349351
/// "creator" field for the new blueprint
350352
#[arg(long)]
351353
creator: Option<String>,
@@ -393,6 +395,41 @@ enum BlueprintEditCommands {
393395
},
394396
}
395397

398+
#[derive(Clone, Debug)]
399+
enum BlueprintIdOpt {
400+
/// use the target blueprint
401+
Target,
402+
/// use the latest blueprint sorted by time created
403+
Latest,
404+
/// use a specific blueprint
405+
Id(BlueprintUuid),
406+
}
407+
408+
impl FromStr for BlueprintIdOpt {
409+
type Err = anyhow::Error;
410+
411+
fn from_str(s: &str) -> Result<Self, Self::Err> {
412+
match s {
413+
"latest" => Ok(BlueprintIdOpt::Latest),
414+
// These values match the ones supported in omdb.
415+
"current-target" | "current" | "target" => {
416+
Ok(BlueprintIdOpt::Target)
417+
}
418+
_ => Ok(BlueprintIdOpt::Id(s.parse()?)),
419+
}
420+
}
421+
}
422+
423+
impl From<BlueprintIdOpt> for BlueprintId {
424+
fn from(value: BlueprintIdOpt) -> Self {
425+
match value {
426+
BlueprintIdOpt::Latest => BlueprintId::Latest,
427+
BlueprintIdOpt::Target => BlueprintId::Target,
428+
BlueprintIdOpt::Id(id) => BlueprintId::Id(id),
429+
}
430+
}
431+
}
432+
396433
#[derive(Clone, Debug, Subcommand)]
397434
enum SpUpdateComponent {
398435
/// update the SP itself
@@ -442,8 +479,8 @@ fn parse_blueprint_zone_image_version(
442479

443480
#[derive(Debug, Args)]
444481
struct BlueprintArgs {
445-
/// id of the blueprint
446-
blueprint_id: BlueprintUuid,
482+
/// id of the blueprint, "latest", or "target"
483+
blueprint_id: BlueprintIdOpt,
447484
}
448485

449486
#[derive(Debug, Args)]
@@ -452,8 +489,8 @@ struct BlueprintDiffDnsArgs {
452489
dns_group: CliDnsGroup,
453490
/// DNS version to diff against
454491
dns_version: u32,
455-
/// id of the blueprint
456-
blueprint_id: BlueprintUuid,
492+
/// id of the blueprint, "latest", or "target"
493+
blueprint_id: BlueprintIdOpt,
457494
}
458495

459496
#[derive(Clone, Copy, Debug, ValueEnum)]
@@ -466,24 +503,24 @@ enum CliDnsGroup {
466503
struct BlueprintDiffInventoryArgs {
467504
/// id of the inventory collection
468505
collection_id: CollectionUuid,
469-
/// id of the blueprint
470-
blueprint_id: BlueprintUuid,
506+
/// id of the blueprint, "latest", or "target"
507+
blueprint_id: BlueprintIdOpt,
471508
}
472509

473510
#[derive(Debug, Args)]
474511
struct BlueprintSaveArgs {
475-
/// id of the blueprint
476-
blueprint_id: BlueprintUuid,
512+
/// id of the blueprint, "latest", or "target"
513+
blueprint_id: BlueprintIdOpt,
477514
/// output file
478515
filename: Utf8PathBuf,
479516
}
480517

481518
#[derive(Debug, Args)]
482519
struct BlueprintDiffArgs {
483-
/// id of the first blueprint
484-
blueprint1_id: BlueprintUuid,
485-
/// id of the second blueprint
486-
blueprint2_id: BlueprintUuid,
520+
/// id of the first blueprint, "latest", or "target"
521+
blueprint1_id: BlueprintIdOpt,
522+
/// id of the second blueprint, "latest", or "target"
523+
blueprint2_id: BlueprintIdOpt,
487524
}
488525

489526
#[derive(Debug, Subcommand)]
@@ -777,7 +814,9 @@ fn cmd_blueprint_blippy(
777814
args: BlueprintArgs,
778815
) -> anyhow::Result<Option<String>> {
779816
let state = sim.current_state();
780-
let blueprint = state.system().get_blueprint(args.blueprint_id)?;
817+
let resolved_id =
818+
state.system().resolve_blueprint_id(args.blueprint_id.into())?;
819+
let blueprint = state.system().get_blueprint(&resolved_id)?;
781820
let report =
782821
Blippy::new(&blueprint).into_report(BlippyReportSortKey::Severity);
783822
Ok(Some(format!("{}", report.display())))
@@ -791,9 +830,10 @@ fn cmd_blueprint_plan(
791830
let rng = state.rng_mut().next_planner_rng();
792831
let system = state.system_mut();
793832

794-
let parent_blueprint_id = args.parent_blueprint_id;
833+
let parent_blueprint_id =
834+
system.resolve_blueprint_id(args.parent_blueprint_id.into())?;
795835
let collection_id = args.collection_id;
796-
let parent_blueprint = system.get_blueprint(parent_blueprint_id)?;
836+
let parent_blueprint = system.get_blueprint(&parent_blueprint_id)?;
797837
let collection = match collection_id {
798838
Some(collection_id) => system.get_collection(collection_id)?,
799839
None => {
@@ -826,7 +866,7 @@ fn cmd_blueprint_plan(
826866
let blueprint = planner.plan().context("generating blueprint")?;
827867
let rv = format!(
828868
"generated blueprint {} based on parent blueprint {}",
829-
blueprint.id, parent_blueprint_id,
869+
blueprint.id, parent_blueprint.id,
830870
);
831871
system.add_blueprint(blueprint)?;
832872

@@ -843,8 +883,8 @@ fn cmd_blueprint_edit(
843883
let rng = state.rng_mut().next_planner_rng();
844884
let system = state.system_mut();
845885

846-
let blueprint_id = args.blueprint_id;
847-
let blueprint = system.get_blueprint(blueprint_id)?;
886+
let resolved_id = system.resolve_blueprint_id(args.blueprint_id.into())?;
887+
let blueprint = system.get_blueprint(&resolved_id)?;
848888
let creator = args.creator.as_deref().unwrap_or("reconfigurator-cli");
849889
let planning_input = sim
850890
.planning_input(blueprint)
@@ -972,8 +1012,8 @@ fn cmd_blueprint_edit(
9721012
.clone_from(&blueprint.cockroachdb_fingerprint);
9731013

9741014
let rv = format!(
975-
"blueprint {} created from blueprint {}: {}",
976-
new_blueprint.id, blueprint_id, label
1015+
"blueprint {} created from {}: {}",
1016+
new_blueprint.id, resolved_id, label
9771017
);
9781018
system.add_blueprint(new_blueprint)?;
9791019

@@ -1006,7 +1046,8 @@ fn cmd_blueprint_show(
10061046
args: BlueprintArgs,
10071047
) -> anyhow::Result<Option<String>> {
10081048
let state = sim.current_state();
1009-
let blueprint = state.system().get_blueprint(args.blueprint_id)?;
1049+
let blueprint =
1050+
state.system().resolve_and_get_blueprint(args.blueprint_id.into())?;
10101051
Ok(Some(format!("{}", blueprint.display())))
10111052
}
10121053

@@ -1019,8 +1060,10 @@ fn cmd_blueprint_diff(
10191060
let blueprint2_id = args.blueprint2_id;
10201061

10211062
let state = sim.current_state();
1022-
let blueprint1 = state.system().get_blueprint(blueprint1_id)?;
1023-
let blueprint2 = state.system().get_blueprint(blueprint2_id)?;
1063+
let blueprint1 =
1064+
state.system().resolve_and_get_blueprint(blueprint1_id.into())?;
1065+
let blueprint2 =
1066+
state.system().resolve_and_get_blueprint(blueprint2_id.into())?;
10241067

10251068
let sled_diff = blueprint2.diff_since_blueprint(&blueprint1);
10261069
swriteln!(rv, "{}", sled_diff.display());
@@ -1098,7 +1141,8 @@ fn cmd_blueprint_diff_dns(
10981141
let blueprint_id = args.blueprint_id;
10991142

11001143
let state = sim.current_state();
1101-
let blueprint = state.system().get_blueprint(blueprint_id)?;
1144+
let blueprint =
1145+
state.system().resolve_and_get_blueprint(blueprint_id.into())?;
11021146

11031147
let existing_dns_config = match dns_group {
11041148
CliDnsGroup::Internal => {
@@ -1140,7 +1184,8 @@ fn cmd_blueprint_diff_inventory(
11401184

11411185
let state = sim.current_state();
11421186
let _collection = state.system().get_collection(collection_id)?;
1143-
let _blueprint = state.system().get_blueprint(blueprint_id)?;
1187+
let _blueprint =
1188+
state.system().resolve_and_get_blueprint(blueprint_id.into())?;
11441189
// See https://github.com/oxidecomputer/omicron/issues/7242
11451190
// let diff = blueprint.diff_since_collection(&collection);
11461191
// Ok(Some(diff.display().to_string()))
@@ -1154,14 +1199,16 @@ fn cmd_blueprint_save(
11541199
let blueprint_id = args.blueprint_id;
11551200

11561201
let state = sim.current_state();
1157-
let blueprint = state.system().get_blueprint(blueprint_id)?;
1202+
let resolved_id =
1203+
state.system().resolve_blueprint_id(blueprint_id.into())?;
1204+
let blueprint = state.system().get_blueprint(&resolved_id)?;
11581205

11591206
let output_path = &args.filename;
11601207
let output_str = serde_json::to_string_pretty(&blueprint)
11611208
.context("serializing blueprint")?;
11621209
std::fs::write(&output_path, &output_str)
11631210
.with_context(|| format!("write {:?}", output_path))?;
1164-
Ok(Some(format!("saved blueprint {} to {:?}", blueprint_id, output_path)))
1211+
Ok(Some(format!("saved {} to {:?}", resolved_id, output_path)))
11651212
}
11661213

11671214
fn cmd_save(

dev-tools/reconfigurator-cli/tests/input/cmds-set-zone-images.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@ load-example --nsleds 1 --ndisks-per-sled 3 --seed test_set_zone_images
44
blueprint-show 971eeb12-1830-4fa0-a699-98ea0164505c
55

66
# Set a couple zones' image sources to specific artifacts.
7-
blueprint-edit 971eeb12-1830-4fa0-a699-98ea0164505c set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
8-
blueprint-edit 9766ca20-38d4-4380-b005-e7c43c797e7c set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 artifact valid-non-semver 7b4281a6bd5946bb96b332c62afe289bef275e58b9ba031085f8827655f434b3
7+
blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
8+
blueprint-edit latest set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 artifact valid-non-semver 7b4281a6bd5946bb96b332c62afe289bef275e58b9ba031085f8827655f434b3
99

1010
blueprint-show f714e6ea-e85a-4d7d-93c2-a018744fe176
1111
blueprint-diff 971eeb12-1830-4fa0-a699-98ea0164505c f714e6ea-e85a-4d7d-93c2-a018744fe176
1212

1313
# Set these zones' image sources back to the install dataset.
14-
blueprint-edit f714e6ea-e85a-4d7d-93c2-a018744fe176 set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
15-
blueprint-edit bb128f06-a2e1-44c1-8874-4f789d0ff896 set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 install-dataset
14+
blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
15+
blueprint-edit latest set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 install-dataset
1616
blueprint-show d9c572a1-a68c-4945-b1ec-5389bd588fe9
1717
blueprint-diff f714e6ea-e85a-4d7d-93c2-a018744fe176 d9c572a1-a68c-4945-b1ec-5389bd588fe9
1818

1919
# test help output
20-
blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 help
21-
blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image help
20+
blueprint-edit latest help
21+
blueprint-edit latest set-zone-image help
2222

2323
# test error case: no such zone id
24-
blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image 126f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
24+
blueprint-edit latest set-zone-image 126f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
2525
# test error case: invalid version identifier
26-
blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 🫡 5d835e8666bd5cf65be66efbca6847b24e41fd85993448a211ad7be5fea559ab
26+
blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 🫡 5d835e8666bd5cf65be66efbca6847b24e41fd85993448a211ad7be5fea559ab
2727
# test error case: bad hash
28-
blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 aaaa
28+
blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 aaaa

dev-tools/reconfigurator-cli/tests/output/cmds-set-mgs-updates-stdout

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,7 @@ Commands:
27702770
help Print this message or the help of the given subcommand(s)
27712771

27722772
Arguments:
2773-
<BLUEPRINT_ID> id of the blueprint to edit
2773+
<BLUEPRINT_ID> id of the blueprint to edit, "latest", or "target"
27742774

27752775
Options:
27762776
--creator <CREATOR> "creator" field for the new blueprint

dev-tools/reconfigurator-cli/tests/output/cmds-set-zone-images-stdout

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,12 @@ parent: 1b013011-2062-4b48-b544-a32b23bce83a
107107

108108
> # Set a couple zones' image sources to specific artifacts.
109109

110-
> blueprint-edit 971eeb12-1830-4fa0-a699-98ea0164505c set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
111-
blueprint 9766ca20-38d4-4380-b005-e7c43c797e7c created from blueprint 971eeb12-1830-4fa0-a699-98ea0164505c: set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 026f8db9-b966-45ec-bf89-fb7b41a04cc9 image source to artifact: version 1.2.3
110+
> blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
111+
blueprint 9766ca20-38d4-4380-b005-e7c43c797e7c created from latest blueprint (971eeb12-1830-4fa0-a699-98ea0164505c): set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 026f8db9-b966-45ec-bf89-fb7b41a04cc9 image source to artifact: version 1.2.3
112112
warn: no validation is done on the requested image source
113113

114-
> blueprint-edit 9766ca20-38d4-4380-b005-e7c43c797e7c set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 artifact valid-non-semver 7b4281a6bd5946bb96b332c62afe289bef275e58b9ba031085f8827655f434b3
115-
blueprint f714e6ea-e85a-4d7d-93c2-a018744fe176 created from blueprint 9766ca20-38d4-4380-b005-e7c43c797e7c: set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 72d12821-646d-4296-a081-ef5fb3a0ca24 image source to artifact: version valid-non-semver
114+
> blueprint-edit latest set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 artifact valid-non-semver 7b4281a6bd5946bb96b332c62afe289bef275e58b9ba031085f8827655f434b3
115+
blueprint f714e6ea-e85a-4d7d-93c2-a018744fe176 created from latest blueprint (9766ca20-38d4-4380-b005-e7c43c797e7c): set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 72d12821-646d-4296-a081-ef5fb3a0ca24 image source to artifact: version valid-non-semver
116116
warn: no validation is done on the requested image source
117117

118118
>
@@ -392,12 +392,12 @@ external DNS:
392392

393393
> # Set these zones' image sources back to the install dataset.
394394

395-
> blueprint-edit f714e6ea-e85a-4d7d-93c2-a018744fe176 set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
396-
blueprint bb128f06-a2e1-44c1-8874-4f789d0ff896 created from blueprint f714e6ea-e85a-4d7d-93c2-a018744fe176: set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 026f8db9-b966-45ec-bf89-fb7b41a04cc9 image source to install dataset
395+
> blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
396+
blueprint bb128f06-a2e1-44c1-8874-4f789d0ff896 created from latest blueprint (f714e6ea-e85a-4d7d-93c2-a018744fe176): set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 026f8db9-b966-45ec-bf89-fb7b41a04cc9 image source to install dataset
397397
warn: no validation is done on the requested image source
398398

399-
> blueprint-edit bb128f06-a2e1-44c1-8874-4f789d0ff896 set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 install-dataset
400-
blueprint d9c572a1-a68c-4945-b1ec-5389bd588fe9 created from blueprint bb128f06-a2e1-44c1-8874-4f789d0ff896: set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 72d12821-646d-4296-a081-ef5fb3a0ca24 image source to install dataset
399+
> blueprint-edit latest set-zone-image 72d12821-646d-4296-a081-ef5fb3a0ca24 install-dataset
400+
blueprint d9c572a1-a68c-4945-b1ec-5389bd588fe9 created from latest blueprint (bb128f06-a2e1-44c1-8874-4f789d0ff896): set sled 868d5b02-7792-4fc0-b6a9-654afcae9ea0 zone 72d12821-646d-4296-a081-ef5fb3a0ca24 image source to install dataset
401401
warn: no validation is done on the requested image source
402402

403403
> blueprint-show d9c572a1-a68c-4945-b1ec-5389bd588fe9
@@ -675,7 +675,7 @@ external DNS:
675675

676676
> # test help output
677677

678-
> blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 help
678+
> blueprint-edit latest help
679679
edit contents of a blueprint directly
680680

681681
Usage: blueprint-edit [OPTIONS] <BLUEPRINT_ID> <COMMAND>
@@ -690,14 +690,14 @@ Commands:
690690
help Print this message or the help of the given subcommand(s)
691691

692692
Arguments:
693-
<BLUEPRINT_ID> id of the blueprint to edit
693+
<BLUEPRINT_ID> id of the blueprint to edit, "latest", or "target"
694694

695695
Options:
696696
--creator <CREATOR> "creator" field for the new blueprint
697697
--comment <COMMENT> "comment" field for the new blueprint
698698
-h, --help Print help
699699

700-
> blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image help
700+
> blueprint-edit latest set-zone-image help
701701
set the image source for a zone
702702

703703
Usage: blueprint-edit <BLUEPRINT_ID> set-zone-image <ZONE_ID> <COMMAND>
@@ -717,14 +717,14 @@ Options:
717717

718718
> # test error case: no such zone id
719719

720-
> blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image 126f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
720+
> blueprint-edit latest set-zone-image 126f8db9-b966-45ec-bf89-fb7b41a04cc9 install-dataset
721721
error: could not find parent sled for zone 126f8db9-b966-45ec-bf89-fb7b41a04cc9
722722

723723
> # test error case: invalid version identifier
724724

725-
> blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 🫡 5d835e8666bd5cf65be66efbca6847b24e41fd85993448a211ad7be5fea559ab
725+
> blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 🫡 5d835e8666bd5cf65be66efbca6847b24e41fd85993448a211ad7be5fea559ab
726726

727727
> # test error case: bad hash
728728

729-
> blueprint-edit d9c572a1-a68c-4945-b1ec-5389bd588fe9 set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 aaaa
729+
> blueprint-edit latest set-zone-image 026f8db9-b966-45ec-bf89-fb7b41a04cc9 artifact 1.2.3 aaaa
730730

0 commit comments

Comments
 (0)