Skip to content

Commit 77342f5

Browse files
authored
reconfigurator-cli should support setting target release (#8283)
1 parent 7ee6ec6 commit 77342f5

File tree

15 files changed

+1595
-78
lines changed

15 files changed

+1595
-78
lines changed

Cargo.lock

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

dev-tools/reconfigurator-cli/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ slog-term.workspace = true
4040
slog.workspace = true
4141
swrite.workspace = true
4242
tabled.workspace = true
43+
tokio.workspace = true
4344
tufaceous-artifact.workspace = true
45+
update-common.workspace = true
4446
uuid.workspace = true
4547
omicron-workspace-hack.workspace = true
4648

@@ -50,11 +52,13 @@ omicron-workspace-hack.workspace = true
5052
# nexus/reconfigurator/cli-integration-tests.
5153
camino-tempfile.workspace = true
5254
datatest-stable.workspace = true
55+
dropshot.workspace = true
5356
expectorate.workspace = true
5457
omicron-test-utils.workspace = true
5558
serde.workspace = true
5659
subprocess.workspace = true
5760
tokio.workspace = true
61+
tufaceous.workspace = true
5862

5963
# Disable doc builds by default for our binaries to work around issue
6064
# rust-lang/cargo#8373. These docs would not be very useful anyway.

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

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use tabled::Tabled;
6060
use tufaceous_artifact::ArtifactHash;
6161
use tufaceous_artifact::ArtifactVersion;
6262
use tufaceous_artifact::ArtifactVersionError;
63+
use update_common::artifacts::{ArtifactsWithPlan, ControlPlaneZonesMode};
6364

6465
mod log_capture;
6566

@@ -727,6 +728,11 @@ enum SetArgs {
727728
NumNexus { num_nexus: u16 },
728729
/// system's external DNS zone name (suffix)
729730
ExternalDnsZoneName { zone_name: String },
731+
/// system target release
732+
TargetRelease {
733+
/// TUF repo containing release artifacts
734+
filename: Utf8PathBuf,
735+
},
730736
}
731737

732738
#[derive(Debug, Args)]
@@ -838,6 +844,7 @@ fn cmd_sled_list(
838844
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
839845
struct Sled {
840846
id: SledUuid,
847+
serial: String,
841848
nzpools: usize,
842849
subnet: String,
843850
}
@@ -849,11 +856,12 @@ fn cmd_sled_list(
849856
.to_planning_input_builder()
850857
.context("failed to generate planning input")?
851858
.build();
852-
let rows = planning_input.all_sled_resources(SledFilter::Commissioned).map(
853-
|(sled_id, sled_resources)| Sled {
859+
let rows = planning_input.all_sleds(SledFilter::Commissioned).map(
860+
|(sled_id, sled_details)| Sled {
854861
id: sled_id,
855-
subnet: sled_resources.subnet.net().to_string(),
856-
nzpools: sled_resources.zpools.len(),
862+
serial: sled_details.baseboard_id.serial_number.clone(),
863+
subnet: sled_details.resources.subnet.net().to_string(),
864+
nzpools: sled_details.resources.zpools.len(),
857865
},
858866
);
859867
let table = tabled::Table::new(rows)
@@ -1618,20 +1626,7 @@ fn cmd_wipe(
16181626
fn cmd_show(sim: &mut ReconfiguratorSim) -> anyhow::Result<Option<String>> {
16191627
let mut s = String::new();
16201628
let state = sim.current_state();
1621-
do_print_properties(&mut s, state);
1622-
swriteln!(
1623-
s,
1624-
"target number of Nexus instances: {}",
1625-
state
1626-
.config()
1627-
.num_nexus()
1628-
.map_or_else(|| "default".to_owned(), |n| n.to_string())
1629-
);
1630-
Ok(Some(s))
1631-
}
16321629

1633-
// TODO: consider moving this to a method on `SimState`.
1634-
fn do_print_properties(s: &mut String, state: &SimState) {
16351630
swriteln!(
16361631
s,
16371632
"configured external DNS zone name: {}",
@@ -1667,6 +1662,41 @@ fn do_print_properties(s: &mut String, state: &SimState) {
16671662
.collect::<Vec<_>>()
16681663
.join(", "),
16691664
);
1665+
swriteln!(
1666+
s,
1667+
"target number of Nexus instances: {}",
1668+
state
1669+
.config()
1670+
.num_nexus()
1671+
.map_or_else(|| "default".to_owned(), |n| n.to_string())
1672+
);
1673+
1674+
let target_release = state.system().description().target_release();
1675+
match target_release {
1676+
Some(tuf_desc) => {
1677+
swriteln!(
1678+
s,
1679+
"target release: {} ({})",
1680+
tuf_desc.repo.system_version,
1681+
tuf_desc.repo.file_name
1682+
);
1683+
for artifact in &tuf_desc.artifacts {
1684+
swriteln!(
1685+
s,
1686+
" artifact: {} {} ({} version {})",
1687+
artifact.hash,
1688+
artifact.id.kind,
1689+
artifact.id.name,
1690+
artifact.id.version
1691+
);
1692+
}
1693+
}
1694+
None => {
1695+
swriteln!(s, "target release: unset");
1696+
}
1697+
}
1698+
1699+
Ok(Some(s))
16701700
}
16711701

16721702
fn cmd_set(
@@ -1703,6 +1733,33 @@ fn cmd_set(
17031733
state.config_mut().set_external_dns_zone_name(zone_name);
17041734
rv
17051735
}
1736+
SetArgs::TargetRelease { filename } => {
1737+
let file = std::fs::File::open(&filename)
1738+
.with_context(|| format!("open {:?}", filename))?;
1739+
let buf = std::io::BufReader::new(file);
1740+
let rt = tokio::runtime::Runtime::new()
1741+
.context("creating tokio runtime")?;
1742+
// We're not using the repo hash here. Make one up.
1743+
let repo_hash = ArtifactHash([0; 32]);
1744+
let artifacts_with_plan = rt.block_on(async {
1745+
ArtifactsWithPlan::from_zip(
1746+
buf,
1747+
None,
1748+
repo_hash,
1749+
ControlPlaneZonesMode::Split,
1750+
&sim.log,
1751+
)
1752+
.await
1753+
.with_context(|| format!("unpacking {:?}", filename))
1754+
})?;
1755+
let description = artifacts_with_plan.description().clone();
1756+
drop(artifacts_with_plan);
1757+
state
1758+
.system_mut()
1759+
.description_mut()
1760+
.set_target_release(Some(description));
1761+
format!("set target release based on {}", filename)
1762+
}
17061763
};
17071764

17081765
sim.commit_and_bump(format!("reconfigurator-cli set: {}", rv), state);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
//! Common code used by integration tests
6+
7+
use camino::Utf8Path;
8+
use expectorate::assert_contents;
9+
use omicron_test_utils::dev::test_cmds::EXIT_SUCCESS;
10+
use omicron_test_utils::dev::test_cmds::Redactor;
11+
use omicron_test_utils::dev::test_cmds::assert_exit_code;
12+
use omicron_test_utils::dev::test_cmds::path_to_executable;
13+
use omicron_test_utils::dev::test_cmds::run_command;
14+
use std::path::PathBuf;
15+
use subprocess::{Exec, ExitStatus};
16+
17+
fn path_to_cli() -> PathBuf {
18+
path_to_executable(env!("CARGO_BIN_EXE_reconfigurator-cli"))
19+
}
20+
21+
fn run_cli(
22+
file: impl AsRef<Utf8Path>,
23+
args: &[&str],
24+
cwd: Option<&Utf8Path>,
25+
) -> (ExitStatus, String, String) {
26+
let file = file.as_ref();
27+
28+
// Turn the path into an absolute one, because we're going to set a custom
29+
// cwd for the subprocess.
30+
let file = file.canonicalize_utf8().expect("file canonicalized");
31+
eprintln!("using file: {file}");
32+
33+
// Create a temporary directory for the CLI to use -- that will let it
34+
// read and write files in its own sandbox.
35+
let tmpdir;
36+
let cwd = match cwd {
37+
None => {
38+
tmpdir =
39+
camino_tempfile::tempdir().expect("failed to create tmpdir");
40+
tmpdir.path()
41+
}
42+
Some(c) => c,
43+
};
44+
let exec = Exec::cmd(path_to_cli()).arg(file).args(args).cwd(cwd);
45+
run_command(exec)
46+
}
47+
48+
pub fn script_with_cwd(
49+
path: &Utf8Path,
50+
cwd: Option<&Utf8Path>,
51+
) -> datatest_stable::Result<()> {
52+
let (exit_status, stdout_text, stderr_text) =
53+
run_cli(path, &["--seed", "reconfigurator-cli-test"], cwd);
54+
assert_exit_code(exit_status, EXIT_SUCCESS, &stderr_text);
55+
56+
// Everything is deterministic, so we don't need to redact UUIDs.
57+
// However, it's necessary to redact paths from generated log entries.
58+
let stdout_text = Redactor::default()
59+
.uuids(false)
60+
.field("extracting uploaded archive to", ".*")
61+
.field("created directory to store extracted artifacts, path:", ".*")
62+
.do_redact(&stdout_text);
63+
64+
// This is the file name without the extension.
65+
let test_name = path.file_stem().unwrap();
66+
let stdout_file = format!("tests/output/{test_name}-stdout");
67+
let stderr_file = format!("tests/output/{test_name}-stderr");
68+
69+
assert_contents(&stdout_file, &stdout_text);
70+
assert_contents(&stderr_file, &stderr_text);
71+
72+
Ok(())
73+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Load example system
2+
load-example --nsleds 3 --ndisks-per-sled 3
3+
4+
# Print the default target release
5+
show
6+
7+
# Load a target release from a fake file.
8+
# This file was generated by the test runner in a temporary directory that this
9+
# invocation of `reconfigurator-cli` is running out of as its working directory.
10+
set target-release repo-1.0.0.zip
11+
12+
# Print the default target release again.
13+
show
14+
15+
# Test that this state survives a save/load operation.
16+
save saved.out
17+
wipe all
18+
# This should NOT show the target release.
19+
show
20+
load saved.out
21+
# This should show the target release.
22+
show
23+
24+
25+
# Great. Now, let's run through an upgrade!
26+
# First, print out what we've got.
27+
sled-list
28+
blueprint-list
29+
inventory-list
30+
31+
# First step: upgrade one SP.
32+
blueprint-plan dbcbd3d6-41ff-48ae-ac0b-1becc9b2fd21 f45ba181-4b56-42cc-a762-874d90184a43
33+
blueprint-diff dbcbd3d6-41ff-48ae-ac0b-1becc9b2fd21 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1
34+
35+
# If we generate another plan, there should be no change.
36+
blueprint-plan 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1 f45ba181-4b56-42cc-a762-874d90184a43
37+
blueprint-diff 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1 58d5e830-0884-47d8-a7cd-b2b3751adeb4
38+
39+
# Now, update the simulated SP to reflect that the update completed.
40+
# Collect inventory from it and use that collection for another planning step.
41+
# This should report that the update completed, remove that update, and add one
42+
# for another sled.
43+
sled-update-sp 98e6b7c2-2efa-41ca-b20a-0a4d61102fe6 --active 1.0.0
44+
inventory-generate
45+
blueprint-plan 58d5e830-0884-47d8-a7cd-b2b3751adeb4 eb0796d5-ab8a-4f7b-a884-b4aeacb8ab51
46+
blueprint-diff 58d5e830-0884-47d8-a7cd-b2b3751adeb4 af934083-59b5-4bf6-8966-6fb5292c29e1
47+
48+
# This time, make it more interesting. Change the inactive slot contents of
49+
# the simulated SP. This should make the configured update impossible and cause
50+
# the planner to fix it.
51+
sled-update-sp 2b8f0cb3-0295-4b3c-bc58-4fe88b57112c --inactive 0.5.0
52+
inventory-generate
53+
blueprint-plan af934083-59b5-4bf6-8966-6fb5292c29e1 61f451b3-2121-4ed6-91c7-a550054f6c21
54+
blueprint-diff af934083-59b5-4bf6-8966-6fb5292c29e1 df06bb57-ad42-4431-9206-abff322896c7
55+
56+
# Now simulate the update completing successfully.
57+
# Another planning step should try to update the last sled.
58+
sled-update-sp 2b8f0cb3-0295-4b3c-bc58-4fe88b57112c --active 1.0.0
59+
inventory-generate
60+
blueprint-plan df06bb57-ad42-4431-9206-abff322896c7 b1bda47d-2c19-4fba-96e3-d9df28db7436
61+
blueprint-diff df06bb57-ad42-4431-9206-abff322896c7 7f976e0d-d2a5-4eeb-9e82-c82bc2824aba
62+
63+
# Finish updating the last sled and do one more planning run.
64+
# There should be nothing left to do.
65+
sled-update-sp d81c6a84-79b8-4958-ae41-ea46c9b19763 --active 1.0.0
66+
inventory-generate
67+
blueprint-plan 7f976e0d-d2a5-4eeb-9e82-c82bc2824aba a71f7a73-35a6-45e8-acbe-f1c5925eed69
68+
blueprint-diff 7f976e0d-d2a5-4eeb-9e82-c82bc2824aba 9034c710-3e57-45f3-99e5-4316145e87ac

dev-tools/reconfigurator-cli/tests/output/cmds-example-stdout

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ configured silo names: example-silo
1616
internal DNS generations: 1
1717
external DNS generations: 1
1818
target number of Nexus instances: default
19+
target release: unset
1920

2021

2122

2223
> sled-list
23-
ID NZPOOLS SUBNET
24-
2eb69596-f081-4e2d-9425-9994926e0832 10 fd00:1122:3344:102::/64
25-
32d8d836-4d8a-4e54-8fa9-f31d79c42646 10 fd00:1122:3344:103::/64
26-
89d02b1b-478c-401a-8e28-7a26f74fa41b 10 fd00:1122:3344:101::/64
24+
ID SERIAL NZPOOLS SUBNET
25+
2eb69596-f081-4e2d-9425-9994926e0832 serial1 10 fd00:1122:3344:102::/64
26+
32d8d836-4d8a-4e54-8fa9-f31d79c42646 serial2 10 fd00:1122:3344:103::/64
27+
89d02b1b-478c-401a-8e28-7a26f74fa41b serial0 10 fd00:1122:3344:101::/64
2728

2829
> inventory-list
2930
ID NERRORS TIME_DONE
@@ -383,8 +384,8 @@ loaded example system with:
383384

384385

385386
> sled-list
386-
ID NZPOOLS SUBNET
387-
89d02b1b-478c-401a-8e28-7a26f74fa41b 4 fd00:1122:3344:101::/64
387+
ID SERIAL NZPOOLS SUBNET
388+
89d02b1b-478c-401a-8e28-7a26f74fa41b serial0 4 fd00:1122:3344:101::/64
388389

389390
> inventory-list
390391
ID NERRORS TIME_DONE

dev-tools/reconfigurator-cli/tests/output/cmds-stdout

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using provided RNG seed: reconfigurator-cli-test
55
new RNG seed: test_basic
66

77
> sled-list
8-
ID NZPOOLS SUBNET
8+
ID SERIAL NZPOOLS SUBNET
99

1010
> inventory-list
1111
ID NERRORS TIME_DONE
@@ -21,8 +21,8 @@ error: attempted to access sled dde1c0e2-b10d-4621-b420-f179f7a7a00a not found i
2121
added sled dde1c0e2-b10d-4621-b420-f179f7a7a00a
2222

2323
> sled-list
24-
ID NZPOOLS SUBNET
25-
dde1c0e2-b10d-4621-b420-f179f7a7a00a 10 fd00:1122:3344:101::/64
24+
ID SERIAL NZPOOLS SUBNET
25+
dde1c0e2-b10d-4621-b420-f179f7a7a00a serial0 10 fd00:1122:3344:101::/64
2626

2727
> sled-show dde1c0e2-b10d-4621-b420-f179f7a7a00a
2828
sled dde1c0e2-b10d-4621-b420-f179f7a7a00a
@@ -60,10 +60,10 @@ added sled 90c1102a-b9f5-4d88-92a2-60d54a2d98cc
6060
added sled 04ef3330-c682-4a08-8def-fcc4bef31bcd
6161

6262
> sled-list
63-
ID NZPOOLS SUBNET
64-
04ef3330-c682-4a08-8def-fcc4bef31bcd 10 fd00:1122:3344:103::/64
65-
90c1102a-b9f5-4d88-92a2-60d54a2d98cc 10 fd00:1122:3344:102::/64
66-
dde1c0e2-b10d-4621-b420-f179f7a7a00a 10 fd00:1122:3344:101::/64
63+
ID SERIAL NZPOOLS SUBNET
64+
04ef3330-c682-4a08-8def-fcc4bef31bcd serial2 10 fd00:1122:3344:103::/64
65+
90c1102a-b9f5-4d88-92a2-60d54a2d98cc serial1 10 fd00:1122:3344:102::/64
66+
dde1c0e2-b10d-4621-b420-f179f7a7a00a serial0 10 fd00:1122:3344:101::/64
6767

6868

6969
> sled-update-sp dde1c0e2-b10d-4621-b420-f179f7a7a00a

dev-tools/reconfigurator-cli/tests/output/target-release-stderr

Whitespace-only changes.

0 commit comments

Comments
 (0)