Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add internal demo saga #6281

Merged
merged 6 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clients/nexus-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ progenitor::generate_api!(
OmicronPhysicalDisksConfig = nexus_types::disk::OmicronPhysicalDisksConfig,
RecoverySiloConfig = nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig,
TypedUuidForCollectionKind = omicron_uuid_kinds::CollectionUuid,
TypedUuidForDemoSagaKind = omicron_uuid_kinds::DemoSagaUuid,
TypedUuidForDownstairsKind = omicron_uuid_kinds::TypedUuid<omicron_uuid_kinds::DownstairsKind>,
TypedUuidForPropolisKind = omicron_uuid_kinds::TypedUuid<omicron_uuid_kinds::PropolisKind>,
TypedUuidForSledKind = omicron_uuid_kinds::TypedUuid<omicron_uuid_kinds::SledKind>,
Expand Down
147 changes: 147 additions & 0 deletions dev-tools/omdb/src/bin/omdb/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use nexus_client::types::BackgroundTasksActivateRequest;
use nexus_client::types::CurrentStatus;
use nexus_client::types::LastResult;
use nexus_client::types::PhysicalDiskPath;
use nexus_client::types::SagaState;
use nexus_client::types::SledSelector;
use nexus_client::types::UninitializedSledId;
use nexus_db_queries::db::lookup::LookupPath;
Expand All @@ -34,6 +35,7 @@ use nexus_types::internal_api::background::LookupRegionPortStatus;
use nexus_types::internal_api::background::RegionReplacementDriverStatus;
use nexus_types::inventory::BaseboardId;
use omicron_uuid_kinds::CollectionUuid;
use omicron_uuid_kinds::DemoSagaUuid;
use omicron_uuid_kinds::GenericUuid;
use omicron_uuid_kinds::PhysicalDiskUuid;
use omicron_uuid_kinds::SledUuid;
Expand Down Expand Up @@ -71,6 +73,8 @@ enum NexusCommands {
BackgroundTasks(BackgroundTasksArgs),
/// interact with blueprints
Blueprints(BlueprintsArgs),
/// view sagas, create and complete demo sagas
Sagas(SagasArgs),
/// interact with sleds
Sleds(SledsArgs),
}
Expand Down Expand Up @@ -244,6 +248,36 @@ struct BlueprintImportArgs {
input: Utf8PathBuf,
}

#[derive(Debug, Args)]
struct SagasArgs {
#[command(subcommand)]
command: SagasCommands,
}

#[derive(Debug, Subcommand)]
enum SagasCommands {
/// List sagas run by this Nexus
///
/// Note that this is reporting in-memory state about sagas run by *this*
/// Nexus instance. You'll get different answers if you ask different Nexus
/// instances.
hawkw marked this conversation as resolved.
Show resolved Hide resolved
List,

/// Create a "demo" saga
///
/// This saga will wait until it's explicitly completed using the
/// "demo-complete" subcommand.
DemoCreate,

/// Complete a demo saga started with "demo-create".
DemoComplete(DemoSagaIdArgs),
}

#[derive(Debug, Args)]
struct DemoSagaIdArgs {
demo_saga_id: DemoSagaUuid,
}

#[derive(Debug, Args)]
struct SledsArgs {
#[command(subcommand)]
Expand Down Expand Up @@ -402,6 +436,34 @@ impl NexusArgs {
cmd_nexus_blueprints_import(&client, token, args).await
}

NexusCommands::Sagas(SagasArgs { command }) => {
if self.nexus_internal_url.is_none() {
eprintln!(
"{}",
textwrap::wrap(
"WARNING: A Nexus instance was selected from DNS \
because a specific one was not specified. But \
the `omdb nexus sagas` commands usually only make \
sense when targeting a specific Nexus instance.",
80
)
.join("\n")
);
}
match command {
SagasCommands::List => cmd_nexus_sagas_list(&client).await,
SagasCommands::DemoCreate => {
let token = omdb.check_allow_destructive()?;
cmd_nexus_sagas_demo_create(&client, token).await
}
SagasCommands::DemoComplete(args) => {
let token = omdb.check_allow_destructive()?;
cmd_nexus_sagas_demo_complete(&client, args, token)
.await
}
}
}

NexusCommands::Sleds(SledsArgs {
command: SledsCommands::ListUninitialized,
}) => cmd_nexus_sleds_list_uninitialized(&client).await,
Expand Down Expand Up @@ -1550,6 +1612,91 @@ async fn cmd_nexus_blueprints_import(
Ok(())
}

/// Runs `omdb nexus sagas list`
async fn cmd_nexus_sagas_list(
client: &nexus_client::Client,
) -> Result<(), anyhow::Error> {
// We don't want users to confuse this with a general way to list all sagas.
// Such a command would read database state and it would go under "omdb db".
eprintln!(
"{}",
textwrap::wrap(
"NOTE: This command only reads in-memory state from the targeted \
Nexus instance. Sagas may be missing if they were run by a \
different Nexus instance or if they finished before this Nexus \
instance last started up.",
80
)
.join("\n")
);

let saga_stream = client.saga_list_stream(None, None);
let sagas =
saga_stream.try_collect::<Vec<_>>().await.context("listing sagas")?;

#[derive(Tabled)]
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
struct SagaRow {
saga_id: Uuid,
state: &'static str,
hawkw marked this conversation as resolved.
Show resolved Hide resolved
}
let rows = sagas.into_iter().map(|saga| SagaRow {
saga_id: saga.id,
state: match saga.state {
SagaState::Running => "running",
SagaState::Succeeded => "succeeded",
SagaState::Failed { .. } => "failed",
SagaState::Stuck { .. } => "stuck",
hawkw marked this conversation as resolved.
Show resolved Hide resolved
},
});
let table = tabled::Table::new(rows)
.with(tabled::settings::Style::empty())
.with(tabled::settings::Padding::new(0, 1, 0, 0))
.to_string();
println!("{}", table);
Ok(())
}

/// Runs `omdb nexus sagas demo-create`
async fn cmd_nexus_sagas_demo_create(
client: &nexus_client::Client,
_destruction_token: DestructiveOperationToken,
) -> Result<(), anyhow::Error> {
let demo_saga =
client.saga_demo_create().await.context("creating demo saga")?;
println!("saga id: {}", demo_saga.saga_id);
println!(
"demo saga id: {} (use this with `demo-complete`)",
demo_saga.demo_saga_id,
);
Ok(())
}

/// Runs `omdb nexus sagas demo-complete`
async fn cmd_nexus_sagas_demo_complete(
client: &nexus_client::Client,
args: &DemoSagaIdArgs,
_destruction_token: DestructiveOperationToken,
) -> Result<(), anyhow::Error> {
if let Err(error) = client
.saga_demo_complete(&args.demo_saga_id)
.await
.context("completing demo saga")
{
eprintln!("error: {:#}", error);
eprintln!(
"note: `demo-complete` must be run against the same Nexus \
instance that is currently running that saga."
);
eprintln!(
"note: Be sure that you're using the demo_saga_id, not the saga_id."
);
Err(error)
} else {
Ok(())
}
}

/// Runs `omdb nexus sleds list-uninitialized`
async fn cmd_nexus_sleds_list_uninitialized(
client: &nexus_client::Client,
Expand Down
16 changes: 16 additions & 0 deletions dev-tools/omdb/tests/env.out
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,22 @@ note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=d
note: database schema version matches expected (<redacted database version>)
note: listing all commissioned sleds (use -F to filter, e.g. -F in-service)
=============================================
EXECUTING COMMAND: omdb ["nexus", "sagas", "list"]
termination: Exited(0)
---------------------------------------------
stdout:
SAGA_ID STATE
---------------------------------------------
stderr:
note: Nexus URL not specified. Will pick one from DNS.
note: using Nexus URL http://[::ffff:127.0.0.1]:REDACTED_PORT
WARNING: A Nexus instance was selected from DNS because a specific one was not
specified. But the `omdb nexus sagas` commands usually only make sense when
targeting a specific Nexus instance.
NOTE: This command only reads in-memory state from the targeted Nexus instance.
Sagas may be missing if they were run by a different Nexus instance or if they
finished before this Nexus instance last started up.
=============================================
EXECUTING COMMAND: omdb ["oximeter", "--oximeter-url", "junk", "list-producers"]
termination: Exited(1)
---------------------------------------------
Expand Down
35 changes: 35 additions & 0 deletions dev-tools/omdb/tests/successes.out
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,41 @@ warning: unknown background task: "vpc_route_manager" (don't know how to interpr
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
=============================================
EXECUTING COMMAND: omdb ["nexus", "sagas", "list"]
termination: Exited(0)
---------------------------------------------
stdout:
SAGA_ID STATE
---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
NOTE: This command only reads in-memory state from the targeted Nexus instance.
Sagas may be missing if they were run by a different Nexus instance or if they
finished before this Nexus instance last started up.
=============================================
EXECUTING COMMAND: omdb ["--destructive", "nexus", "sagas", "demo-create"]
termination: Exited(0)
---------------------------------------------
stdout:
saga id: ..........<REDACTED_UUID>...........
demo saga id: ..........<REDACTED_UUID>........... (use this with `demo-complete`)
---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
=============================================
EXECUTING COMMAND: omdb ["nexus", "sagas", "list"]
termination: Exited(0)
---------------------------------------------
stdout:
SAGA_ID STATE
..........<REDACTED_UUID>........... running
---------------------------------------------
stderr:
note: using Nexus URL http://127.0.0.1:REDACTED_PORT/
NOTE: This command only reads in-memory state from the targeted Nexus instance.
Sagas may be missing if they were run by a different Nexus instance or if they
finished before this Nexus instance last started up.
=============================================
EXECUTING COMMAND: omdb ["--destructive", "nexus", "background-tasks", "activate", "inventory_collection"]
termination: Exited(0)
---------------------------------------------
Expand Down
23 changes: 23 additions & 0 deletions dev-tools/omdb/tests/test_all_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ async fn test_omdb_usage_errors() {
&["nexus"],
&["nexus", "background-tasks"],
&["nexus", "blueprints"],
&["nexus", "sagas"],
// Missing "--destructive" flag. The URL is bogus but just ensures that
// we get far enough to hit the error we care about.
&[
"nexus",
"--nexus-internal-url",
"http://[::1]:111",
"sagas",
"demo-create",
],
&["nexus", "sleds"],
&["sled-agent"],
&["sled-agent", "zones"],
Expand Down Expand Up @@ -134,6 +144,9 @@ async fn test_omdb_success_cases(cptestctx: &ControlPlaneTestContext) {
&["mgs", "inventory"],
&["nexus", "background-tasks", "doc"],
&["nexus", "background-tasks", "show"],
&["nexus", "sagas", "list"],
&["--destructive", "nexus", "sagas", "demo-create"],
&["nexus", "sagas", "list"],
&[
"--destructive",
"nexus",
Expand Down Expand Up @@ -326,6 +339,16 @@ async fn test_omdb_env_settings(cptestctx: &ControlPlaneTestContext) {
let args = &["--dns-server", &dns_sockaddr.to_string(), "db", "sleds"];
do_run(&mut output, move |exec| exec, &cmd_path, args).await;

// That said, the "sagas" command prints an extra warning in this case.
let args = &["nexus", "sagas", "list"];
do_run(
&mut output,
move |exec| exec.env("OMDB_DNS_SERVER", &dns_sockaddr.to_string()),
&cmd_path,
args,
)
.await;

// Case: specified in multiple places (command-line argument wins)
let args = &["oximeter", "--oximeter-url", "junk", "list-producers"];
let ox = ox_url.clone();
Expand Down
38 changes: 38 additions & 0 deletions dev-tools/omdb/tests/usage_errors.out
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ Usage: omdb nexus [OPTIONS] <COMMAND>
Commands:
background-tasks print information about background tasks
blueprints interact with blueprints
sagas view sagas, create and complete demo sagas
sleds interact with sleds
help Print this message or the help of the given subcommand(s)

Expand Down Expand Up @@ -518,6 +519,43 @@ Connection Options:
Safety Options:
-w, --destructive Allow potentially-destructive subcommands
=============================================
EXECUTING COMMAND: omdb ["nexus", "sagas"]
termination: Exited(2)
---------------------------------------------
stdout:
---------------------------------------------
stderr:
view sagas, create and complete demo sagas

Usage: omdb nexus sagas [OPTIONS] <COMMAND>

Commands:
list List sagas run by this Nexus
demo-create Create a "demo" saga
demo-complete Complete a demo saga started with "demo-create"
help Print this message or the help of the given subcommand(s)

Options:
--log-level <LOG_LEVEL> log level filter [env: LOG_LEVEL=] [default: warn]
-h, --help Print help

Connection Options:
--nexus-internal-url <NEXUS_INTERNAL_URL> URL of the Nexus internal API [env:
OMDB_NEXUS_URL=]
--dns-server <DNS_SERVER> [env: OMDB_DNS_SERVER=]

Safety Options:
-w, --destructive Allow potentially-destructive subcommands
=============================================
EXECUTING COMMAND: omdb ["nexus", "--nexus-internal-url", "http://[::1]:111", "sagas", "demo-create"]
termination: Exited(1)
---------------------------------------------
stdout:
---------------------------------------------
stderr:
note: using Nexus URL http://[::1]:111
Error: This command is potentially destructive. Pass the `-w` / `--destructive` flag to allow it.
=============================================
EXECUTING COMMAND: omdb ["nexus", "sleds"]
termination: Exited(2)
---------------------------------------------
Expand Down
Loading
Loading