Skip to content

Commit 8b44547

Browse files
authored
reconfigurator-cli could use blueprint-history command (#9207)
1 parent a180243 commit 8b44547

File tree

4 files changed

+929
-0
lines changed

4 files changed

+929
-0
lines changed

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

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ use omicron_uuid_kinds::SledUuid;
6464
use omicron_uuid_kinds::VnicUuid;
6565
use omicron_uuid_kinds::{BlueprintUuid, MupdateOverrideUuid};
6666
use omicron_uuid_kinds::{CollectionUuid, MupdateUuid};
67+
use slog_error_chain::InlineErrorChain;
6768
use std::borrow::Cow;
6869
use std::collections::BTreeSet;
6970
use std::convert::Infallible;
@@ -312,6 +313,7 @@ fn process_command(
312313
Commands::BlueprintShow(args) => cmd_blueprint_show(sim, args),
313314
Commands::BlueprintDiff(args) => cmd_blueprint_diff(sim, args),
314315
Commands::BlueprintDiffDns(args) => cmd_blueprint_diff_dns(sim, args),
316+
Commands::BlueprintHistory(args) => cmd_blueprint_history(sim, args),
315317
Commands::BlueprintSave(args) => cmd_blueprint_save(sim, args),
316318
Commands::Show => cmd_show(sim),
317319
Commands::Set(args) => cmd_set(sim, args),
@@ -392,6 +394,12 @@ enum Commands {
392394
BlueprintDiff(BlueprintDiffArgs),
393395
/// show differences between a blueprint and a particular DNS version
394396
BlueprintDiffDns(BlueprintDiffDnsArgs),
397+
/// print a summary of the history of blueprints
398+
///
399+
/// This is similar to `omdb reconfigurator history` in a live system, but
400+
/// it walks blueprints directly via their parent blueprint id rather than
401+
/// walking the `bp_target` table.
402+
BlueprintHistory(BlueprintHistoryArgs),
395403
/// write one blueprint to a file
396404
BlueprintSave(BlueprintSaveArgs),
397405

@@ -936,6 +944,16 @@ impl FromStr for BlueprintIdOpt {
936944
}
937945
}
938946

947+
impl fmt::Display for BlueprintIdOpt {
948+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
949+
match self {
950+
BlueprintIdOpt::Target => f.write_str("target"),
951+
BlueprintIdOpt::Latest => f.write_str("latest"),
952+
BlueprintIdOpt::Id(id) => id.fmt(f),
953+
}
954+
}
955+
}
956+
939957
impl From<BlueprintIdOpt> for BlueprintId {
940958
fn from(value: BlueprintIdOpt) -> Self {
941959
match value {
@@ -1200,6 +1218,21 @@ enum CliDnsGroup {
12001218
External,
12011219
}
12021220

1221+
#[derive(Debug, Args)]
1222+
struct BlueprintHistoryArgs {
1223+
/// how many blueprints worth of history to report
1224+
#[clap(long, default_value_t = 128)]
1225+
limit: usize,
1226+
1227+
/// also attempt to diff each blueprint
1228+
#[clap(long, default_value_t = false)]
1229+
diff: bool,
1230+
1231+
/// id of the blueprint to start history from
1232+
#[clap(default_value_t = BlueprintIdOpt::Target)]
1233+
blueprint_id: BlueprintIdOpt,
1234+
}
1235+
12031236
#[derive(Debug, Args)]
12041237
struct BlueprintSaveArgs {
12051238
/// id of the blueprint, "latest", or "target"
@@ -2609,6 +2642,92 @@ fn cmd_blueprint_diff_dns(
26092642
Ok(Some(dns_diff.to_string()))
26102643
}
26112644

2645+
// This command looks a lot like `omdb reconfigurator history`, but it differs
2646+
// in some ways that often don't matter but are worth knowing about:
2647+
//
2648+
// 1. It's reading the history of blueprints based on their parent ids. `omdb
2649+
// reconfigurator history` reads the `bp_target` table. These are generally
2650+
// equivalent, except that the `omdb` command sees entries for blueprints
2651+
// being enabled and disabled and the times that that happened. This command
2652+
// doesn't know that.
2653+
//
2654+
// 2. Relatedly, this command prints the creation time of the blueprint, not
2655+
// when it was made the target. These are generally very close in time but
2656+
// they're not the same thing.
2657+
fn cmd_blueprint_history(
2658+
sim: &mut ReconfiguratorSim,
2659+
args: BlueprintHistoryArgs,
2660+
) -> anyhow::Result<Option<String>> {
2661+
let BlueprintHistoryArgs { limit, diff, blueprint_id } = args;
2662+
2663+
let state = sim.current_state();
2664+
let system = state.system();
2665+
let resolved_id = system.resolve_blueprint_id(blueprint_id.into())?;
2666+
let mut blueprint = system.get_blueprint(&resolved_id)?;
2667+
2668+
// We want to print the output in logical order, but in order to construct
2669+
// the output, we need to walk in reverse-logical order. To do this, we'll
2670+
// assemble the output as we go and then print it in reverse order.
2671+
//
2672+
// Although we're sort of printing a table, we use strings rather than a
2673+
// table in case we're in `diff` mode. In that case, we want to print
2674+
// details with each "row" that don't go in the table itself.
2675+
let mut entries = Vec::new();
2676+
2677+
while entries.len() < limit {
2678+
let mut entry = String::new();
2679+
swriteln!(
2680+
entry,
2681+
"{} {} {}",
2682+
humantime::format_rfc3339_millis(blueprint.time_created.into()),
2683+
blueprint.id,
2684+
blueprint.comment
2685+
);
2686+
let new_blueprint = blueprint;
2687+
let Some(parent_blueprint_id) = &blueprint.parent_blueprint_id else {
2688+
// We reached the initial blueprint.
2689+
entries.push(entry);
2690+
break;
2691+
};
2692+
2693+
blueprint = match system
2694+
.resolve_and_get_blueprint(BlueprintId::Id(*parent_blueprint_id))
2695+
{
2696+
Ok(b) => b,
2697+
Err(error) => {
2698+
swriteln!(
2699+
entry,
2700+
"error walking back from blueprint {} to parent {}: {}",
2701+
blueprint.id,
2702+
parent_blueprint_id,
2703+
InlineErrorChain::new(&error)
2704+
);
2705+
entries.push(entry);
2706+
break;
2707+
}
2708+
};
2709+
2710+
if diff {
2711+
let diff = new_blueprint.diff_since_blueprint(&blueprint);
2712+
swriteln!(entry, "{}", diff.display());
2713+
}
2714+
2715+
entries.push(entry);
2716+
}
2717+
2718+
if entries.len() == limit {
2719+
entries.push(String::from("... (earlier history omitted)\n"));
2720+
}
2721+
2722+
let mut output = String::new();
2723+
swriteln!(output, "{:24} {:36}", "TIME", "BLUEPRINT");
2724+
for entry in entries.iter().rev() {
2725+
swrite!(output, "{entry}");
2726+
}
2727+
2728+
Ok(Some(output))
2729+
}
2730+
26122731
fn cmd_blueprint_save(
26132732
sim: &mut ReconfiguratorSim,
26142733
args: BlueprintSaveArgs,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Start with an example system.
2+
load-example
3+
4+
# Create a chain of blueprints.
5+
blueprint-edit dbcbd3d6-41ff-48ae-ac0b-1becc9b2fd21 expunge-zones 2bf59188-be61-4c64-9abf-2e336d82f2e6
6+
blueprint-edit 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1 add-nexus d81c6a84-79b8-4958-ae41-ea46c9b19763 2
7+
blueprint-edit 58d5e830-0884-47d8-a7cd-b2b3751adeb4 bump-nexus-generation
8+
blueprint-edit af934083-59b5-4bf6-8966-6fb5292c29e1 expunge-zones 7d0b456b-da4e-414c-8df8-31412a5bd0fe
9+
10+
# By default, `blueprint-history` shows the history from the current target,
11+
# which does not include any of those blueprints.
12+
blueprint-history
13+
14+
# You can give it a specific blueprint.
15+
blueprint-history 8da82a8e-bf97-4fbd-8ddd-9f6462732cf1
16+
17+
# Running it from the latest blueprint should report all of them.
18+
blueprint-history latest
19+
20+
# Show what happens when we reach the limit.
21+
blueprint-history --limit 2 latest
22+
23+
# Show diffs, too.
24+
blueprint-history --diff latest

dev-tools/reconfigurator-cli/tests/output/cmds-blueprint-history-stderr

Whitespace-only changes.

0 commit comments

Comments
 (0)