Skip to content

Commit

Permalink
omdb: Accept current-target as a blueprint ID to show or diff (#5543)
Browse files Browse the repository at this point in the history
#5287 got rid of `omdb db services ...`, so I've found myself wanting to
see the current target blueprint. omdb can do that, but it required
listing the blueprints, visually finding the target, then calling `nexus
blueprints show <ID>`. With this PR, we can accept the string
`current-target` instead, and I figured that might also be useful in
diffs (e.g., `nexus blueprints diff current-target <SOME_ID>` to see
changes from the current target to some other blueprint).
  • Loading branch information
jgallagher authored Apr 17, 2024
1 parent 64fcc16 commit 47548c5
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 24 deletions.
102 changes: 78 additions & 24 deletions dev-tools/omdb/src/bin/omdb/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use chrono::Utc;
use clap::Args;
use clap::Subcommand;
use clap::ValueEnum;
use futures::future::try_join;
use futures::TryStreamExt;
use nexus_client::types::ActivationReason;
use nexus_client::types::BackgroundTask;
Expand All @@ -35,6 +36,7 @@ use reedline::Reedline;
use serde::Deserialize;
use slog_error_chain::InlineErrorChain;
use std::collections::BTreeMap;
use std::str::FromStr;
use tabled::Tabled;
use uuid::Uuid;

Expand Down Expand Up @@ -89,7 +91,7 @@ enum BlueprintsCommands {
List,
/// Show a blueprint
Show(BlueprintIdArgs),
/// Diff two blueprint
/// Diff two blueprints
Diff(BlueprintIdsArgs),
/// Delete a blueprint
Delete(BlueprintIdArgs),
Expand All @@ -103,18 +105,72 @@ enum BlueprintsCommands {
Import(BlueprintImportArgs),
}

#[derive(Debug, Args)]
#[derive(Debug, Clone, Copy)]
enum BlueprintIdOrCurrentTarget {
CurrentTarget,
BlueprintId(Uuid),
}

impl FromStr for BlueprintIdOrCurrentTarget {
type Err = uuid::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if matches!(s, "current-target" | "current" | "target") {
Ok(Self::CurrentTarget)
} else {
let id = s.parse()?;
Ok(Self::BlueprintId(id))
}
}
}

impl BlueprintIdOrCurrentTarget {
async fn resolve_to_id(
&self,
client: &nexus_client::Client,
) -> anyhow::Result<Uuid> {
match self {
Self::CurrentTarget => {
let target = client
.blueprint_target_view()
.await
.context("getting current blueprint target")?;
Ok(target.target_id)
}
Self::BlueprintId(id) => Ok(*id),
}
}

async fn resolve_to_blueprint(
&self,
client: &nexus_client::Client,
) -> anyhow::Result<Blueprint> {
let id = self.resolve_to_id(client).await?;
let response = client.blueprint_view(&id).await.with_context(|| {
let suffix = match self {
BlueprintIdOrCurrentTarget::CurrentTarget => {
" (current target)"
}
BlueprintIdOrCurrentTarget::BlueprintId(_) => "",
};
format!("fetching blueprint {id}{suffix}")
})?;
Ok(response.into_inner())
}
}

#[derive(Debug, Clone, Copy, Args)]
struct BlueprintIdArgs {
/// id of a blueprint
blueprint_id: Uuid,
/// id of blueprint (or `target` for the current target)
blueprint_id: BlueprintIdOrCurrentTarget,
}

#[derive(Debug, Args)]
struct BlueprintIdsArgs {
/// id of first blueprint
blueprint1_id: Uuid,
/// id of second blueprint
blueprint2_id: Uuid,
/// id of first blueprint (or `target` for the current target)
blueprint1_id: BlueprintIdOrCurrentTarget,
/// id of second blueprint (or `target` for the current target)
blueprint2_id: BlueprintIdOrCurrentTarget,
}

#[derive(Debug, Args)]
Expand Down Expand Up @@ -973,10 +1029,7 @@ async fn cmd_nexus_blueprints_show(
client: &nexus_client::Client,
args: &BlueprintIdArgs,
) -> Result<(), anyhow::Error> {
let blueprint = client
.blueprint_view(&args.blueprint_id)
.await
.with_context(|| format!("fetching blueprint {}", args.blueprint_id))?;
let blueprint = args.blueprint_id.resolve_to_blueprint(client).await?;
println!("{}", blueprint.display());
Ok(())
}
Expand All @@ -985,12 +1038,11 @@ async fn cmd_nexus_blueprints_diff(
client: &nexus_client::Client,
args: &BlueprintIdsArgs,
) -> Result<(), anyhow::Error> {
let b1 = client.blueprint_view(&args.blueprint1_id).await.with_context(
|| format!("fetching blueprint {}", args.blueprint1_id),
)?;
let b2 = client.blueprint_view(&args.blueprint2_id).await.with_context(
|| format!("fetching blueprint {}", args.blueprint2_id),
)?;
let (b1, b2) = try_join(
args.blueprint1_id.resolve_to_blueprint(client),
args.blueprint2_id.resolve_to_blueprint(client),
)
.await?;
let diff = b2.diff_since_blueprint(&b1).context("diffing blueprints")?;
println!("{}", diff.display());
Ok(())
Expand All @@ -1001,11 +1053,12 @@ async fn cmd_nexus_blueprints_delete(
args: &BlueprintIdArgs,
_destruction_token: DestructiveOperationToken,
) -> Result<(), anyhow::Error> {
let blueprint_id = args.blueprint_id.resolve_to_id(client).await?;
let _ = client
.blueprint_delete(&args.blueprint_id)
.blueprint_delete(&blueprint_id)
.await
.with_context(|| format!("deleting blueprint {}", args.blueprint_id))?;
println!("blueprint {} deleted", args.blueprint_id);
.with_context(|| format!("deleting blueprint {blueprint_id}"))?;
println!("blueprint {blueprint_id} deleted");
Ok(())
}

Expand Down Expand Up @@ -1064,19 +1117,20 @@ async fn cmd_nexus_blueprints_target_set_enabled(
enabled: bool,
_destruction_token: DestructiveOperationToken,
) -> Result<(), anyhow::Error> {
let blueprint_id = args.blueprint_id.resolve_to_id(client).await?;
let description = if enabled { "enabled" } else { "disabled" };
client
.blueprint_target_set_enabled(
&nexus_client::types::BlueprintTargetSet {
target_id: args.blueprint_id,
target_id: blueprint_id,
enabled,
},
)
.await
.with_context(|| {
format!("setting blueprint {} to {description}", args.blueprint_id)
format!("setting blueprint {blueprint_id} to {description}")
})?;
eprintln!("set target blueprint {} to {description}", args.blueprint_id);
eprintln!("set target blueprint {blueprint_id} to {description}");
Ok(())
}

Expand Down
62 changes: 62 additions & 0 deletions dev-tools/omdb/tests/successes.out
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,65 @@ METADATA:
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
=============================================
EXECUTING COMMAND: omdb ["nexus", "blueprints", "show", "current-target"]
termination: Exited(0)
---------------------------------------------
stdout:
blueprint ......<REDACTED_BLUEPRINT_ID>.......
parent: <none>

-----------------------------------------------------------------------------------------
zone type zone ID disposition underlay IP
-----------------------------------------------------------------------------------------

sled ..........<REDACTED_UUID>...........: zones at generation 2
(no zones)

sled ..........<REDACTED_UUID>...........: zones at generation 2
clickhouse ..........<REDACTED_UUID>........... in service ::1
cockroach_db ..........<REDACTED_UUID>........... in service ::1
crucible_pantry ..........<REDACTED_UUID>........... in service ::1
external_dns ..........<REDACTED_UUID>........... in service ::1
internal_dns ..........<REDACTED_UUID>........... in service ::1
nexus ..........<REDACTED_UUID>........... in service ::ffff:127.0.0.1

METADATA:
created by: nexus-test-utils
created at: <REDACTED TIMESTAMP>
comment: initial test blueprint
internal DNS version: 1
external DNS version: 2

---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
=============================================
EXECUTING COMMAND: omdb ["nexus", "blueprints", "diff", "......<REDACTED_BLUEPRINT_ID>.......", "current-target"]
termination: Exited(0)
---------------------------------------------
stdout:
from: blueprint ......<REDACTED_BLUEPRINT_ID>.......
to: blueprint ......<REDACTED_BLUEPRINT_ID>.......

---------------------------------------------------------------------------------------------------
zone type zone ID disposition underlay IP status
---------------------------------------------------------------------------------------------------

UNCHANGED SLEDS:

sled ..........<REDACTED_UUID>...........: zones at generation 2
clickhouse ..........<REDACTED_UUID>........... in service ::1
cockroach_db ..........<REDACTED_UUID>........... in service ::1
crucible_pantry ..........<REDACTED_UUID>........... in service ::1
external_dns ..........<REDACTED_UUID>........... in service ::1
internal_dns ..........<REDACTED_UUID>........... in service ::1
nexus ..........<REDACTED_UUID>........... in service ::ffff:127.0.0.1

METADATA:
internal DNS version: 1 (unchanged)
external DNS version: 2 (unchanged)

---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
=============================================
8 changes: 8 additions & 0 deletions dev-tools/omdb/tests/test_all_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ async fn test_omdb_success_cases(cptestctx: &ControlPlaneTestContext) {
&["nexus", "background-tasks", "show"],
&["nexus", "blueprints", "list"],
&["nexus", "blueprints", "show", &initial_blueprint_id],
&["nexus", "blueprints", "show", "current-target"],
&[
"nexus",
"blueprints",
"diff",
&initial_blueprint_id,
"current-target",
],
// We can't easily test the sled agent output because that's only
// provided by a real sled agent, which is not available in the
// ControlPlaneTestContext.
Expand Down

0 comments on commit 47548c5

Please sign in to comment.