Skip to content

Commit

Permalink
display it
Browse files Browse the repository at this point in the history
Created using spr 1.3.6-beta.1
  • Loading branch information
sunshowers committed Aug 27, 2024
2 parents 19354d3 + 88e0da5 commit 937893a
Show file tree
Hide file tree
Showing 14 changed files with 526 additions and 32 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dev-tools/omdb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ tabled.workspace = true
textwrap.workspace = true
tokio = { workspace = true, features = [ "full" ] }
unicode-width.workspace = true
update-engine.workspace = true
url.workspace = true
uuid.workspace = true
ipnetwork.workspace = true
Expand Down
242 changes: 242 additions & 0 deletions dev-tools/omdb/src/bin/omdb/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,18 @@ use slog_error_chain::InlineErrorChain;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::str::FromStr;
use tabled::settings::object::Columns;
use tabled::settings::Padding;
use tabled::Tabled;
use tokio::sync::OnceCell;
use update_engine::display::StepIndexDisplay;
use update_engine::events::EventReport;
use update_engine::EventBuffer;
use update_engine::ExecutionStatus;
use update_engine::ExecutionTerminalInfo;
use update_engine::NestedError;
use update_engine::NestedSpec;
use update_engine::TerminalKind;
use uuid::Uuid;

/// Arguments to the "omdb nexus" subcommand
Expand Down Expand Up @@ -1556,6 +1566,100 @@ fn print_task_details(bgtask: &BackgroundTask, details: &serde_json::Value) {
}
}
}
} else if name == "blueprint_loader" {
#[derive(Deserialize)]
struct BlueprintLoaderStatus {
target_id: Uuid,
time_created: DateTime<Utc>,
status: String,
enabled: bool,
}

match serde_json::from_value::<BlueprintLoaderStatus>(details.clone()) {
Err(error) => eprintln!(
"warning: failed to interpret task details: {:?}: {:?}",
error, details
),
Ok(status) => {
println!(" target blueprint: {}", status.target_id);
println!(
" execution: {}",
if status.enabled { "enabled" } else { "disabled" }
);
println!(
" created at: {}",
humantime::format_rfc3339_millis(
status.time_created.into()
)
);
println!(" status: {}", status.status);
}
}
} else if name == "blueprint_executor" {
let mut value = details.clone();
// Extract and remove the event report. (If we don't do this, the
// `Debug` output can be quite large.)
//
// TODO: show more of the event buffer.
let event_buffer = extract_event_buffer(&mut value);

#[derive(Deserialize)]
struct BlueprintExecutorStatus {
target_id: Uuid,
enabled: bool,
execution_error: Option<NestedError>,
}

match serde_json::from_value::<BlueprintExecutorStatus>(value) {
Err(error) => eprintln!(
"warning: failed to interpret task details: {:?}: {:?}",
error, details
),
Ok(status) => {
// TODO: switch the other outputs to tabled as well.
let mut builder = tabled::builder::Builder::default();
builder.push_record([
"target blueprint:".to_string(),
status.target_id.to_string(),
]);
builder.push_record([
"execution:".to_string(),
if status.enabled {
"enabled".to_string()
} else {
"disabled".to_string()
},
]);

push_event_buffer_summary(event_buffer, &mut builder);

match status.execution_error {
Some(error) => {
builder.push_record([
"error:".to_string(),
error.to_string(),
]);

for source in error.sources() {
builder.push_record([
" caused by:".to_string(),
source.to_string(),
]);
}
}
None => {
builder.push_record([
"error:".to_string(),
"(none)".to_string(),
]);
}
}

let mut table = builder.build();
bgtask_apply_kv_style(&mut table);
println!("{}", table);
}
}
} else {
println!(
"warning: unknown background task: {:?} \
Expand All @@ -1576,6 +1680,144 @@ fn reason_str(reason: &ActivationReason) -> &'static str {
}
}

fn bgtask_apply_kv_style(table: &mut tabled::Table) {
let style = tabled::settings::Style::blank();
table.with(style).with(
tabled::settings::Modify::new(Columns::first())
// Background task tables are offset by 4 characters.
.with(Padding::new(4, 0, 0, 0)),
);
}

// Extract and remove the event report. (If we don't do this, the `Debug`
// output can be quite large.)
fn extract_event_buffer(
value: &mut serde_json::Value,
) -> anyhow::Result<EventBuffer<NestedSpec>> {
let Some(obj) = value.as_object_mut() else {
bail!("expected value to be an object")
};
let Some(event_report) = obj.remove("event_report") else {
bail!("expected 'event_report' field in value")
};

// Try deserializing the event report generically. We could deserialize to
// a more explicit spec, e.g. `ReconfiguratorExecutionSpec`, but that's
// unnecessary for omdb's purposes.
let value: Result<EventReport<NestedSpec>, NestedError> =
serde_json::from_value(event_report)
.context("failed to deserialize event report")?;
let event_report = value.context(
"event report stored as Err rather than Ok (did receiver task panic?)",
)?;

let mut event_buffer = EventBuffer::default();
event_buffer.add_event_report(event_report);
Ok(event_buffer)
}

// Make a short summary of the current state of an execution based on an event
// buffer, and add it to the table.
fn push_event_buffer_summary(
event_buffer: anyhow::Result<EventBuffer<NestedSpec>>,
builder: &mut tabled::builder::Builder,
) {
match event_buffer {
Ok(buffer) => {
let Some(summary) = buffer.root_execution_summary() else {
builder.push_record(["status:", "(no information found)"]);
return;
};

match summary.execution_status {
ExecutionStatus::NotStarted => {
builder.push_record(["status:", "not started"]);
}
ExecutionStatus::Running { step_key, .. } => {
let step_data = buffer.get(&step_key).expect("step exists");
builder.push_record([
"status:".to_string(),
format!(
"running: {} (step {})",
step_data.step_info().description,
StepIndexDisplay::new(
step_key.index + 1,
summary.total_steps
),
),
]);
}
ExecutionStatus::Terminal(info) => {
push_event_buffer_terminal_info(
&info,
summary.total_steps,
&buffer,
builder,
);
}
}
}
Err(error) => {
builder.push_record([
"event report error:".to_string(),
error.to_string(),
]);
for source in error.chain() {
builder.push_record([
" caused by:".to_string(),
source.to_string(),
]);
}
}
}
}

fn push_event_buffer_terminal_info(
info: &ExecutionTerminalInfo,
total_steps: usize,
buffer: &EventBuffer<NestedSpec>,
builder: &mut tabled::builder::Builder,
) {
let step_data = buffer.get(&info.step_key).expect("step exists");

match info.kind {
TerminalKind::Completed => {
let v = format!("completed ({} steps)", total_steps);
builder.push_record(["status:".to_string(), v]);
}
TerminalKind::Failed => {
let v = format!(
"failed at: {} (step {})",
step_data.step_info().description,
StepIndexDisplay::new(info.step_key.index, total_steps)
);
builder.push_record(["status:".to_string(), v]);

// Don't show the error here, because it's duplicated in another
// field that's already shown.
}
TerminalKind::Aborted => {
let v = format!(
"aborted at: {} (step {})",
step_data.step_info().description,
StepIndexDisplay::new(info.step_key.index, total_steps)
);
builder.push_record(["status:".to_string(), v]);

let Some(reason) = step_data.step_status().abort_reason() else {
builder.push_record([" reason:", "(no reason found)"]);
return;
};

builder.push_record([
" reason:".to_string(),
reason.message_display(&buffer).to_string(),
]);
// TODO: show last progress event
}
}
}

/// Used for printing background task status as a table
#[derive(Tabled)]
struct BackgroundTaskStatusRow {
Expand Down
6 changes: 5 additions & 1 deletion nexus/reconfigurator/execution/src/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,11 @@ mod test {

// If we execute it again, we should see no more changes.
_ = realize_blueprint_and_expect(
&opctx, datastore, resolver, &blueprint, &overrides,
&opctx,
datastore,
resolver,
&blueprint2,
&overrides,
)
.await;
verify_dns_unchanged(
Expand Down
11 changes: 3 additions & 8 deletions nexus/reconfigurator/execution/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,7 @@ fn register_zone_external_networking_step<'a>(
&opctx, blueprint,
)
.await
.map_err(|err| {
// TODO: this is really a list of errors, and the
// update-engine should grow the ability to represent
// that.
anyhow!(err)
})?;
.map_err(|err| anyhow!(err))?;

StepSuccess::new(()).into()
},
Expand Down Expand Up @@ -608,8 +603,8 @@ fn register_cockroachdb_settings_step<'a>(
.register()
}

fn register_finalize_step<'a>(
registrar: &ComponentRegistrar<'_, 'a>,
fn register_finalize_step(
registrar: &ComponentRegistrar<'_, '_>,
reassign_saga_output: StepHandle<ReassignSagaOutput>,
register_cockroach_output: StepHandle<Option<anyhow::Error>>,
) -> StepHandle<RealizeBlueprintOutput> {
Expand Down
17 changes: 14 additions & 3 deletions nexus/src/app/background/tasks/blueprint_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl BlueprintExecutor {
"target_id" => %blueprint.id);
return json!({
"target_id": blueprint.id.to_string(),
"error": "blueprint disabled"
"enabled": false,
});
}

Expand Down Expand Up @@ -133,14 +133,23 @@ impl BlueprintExecutor {

json!({
"target_id": blueprint.id.to_string(),
"enabled": true,
// Note: The field "error" is treated as special by omdb,
// and if that field is present then nothing else is
// displayed.
"execution_error": null,
"needs_saga_recovery": needs_saga_recovery,
"event_report": event_report,
})
}
Err(error) => {
json!({
"target_id": blueprint.id.to_string(),
"error": NestedError::new(error.as_ref()),
"enabled": true,
// Note: The field "error" is treated as special by omdb,
// and if that field is present then nothing else is
// displayed.
"execution_error": NestedError::new(error.as_ref()),
"event_report": event_report,
})
}
Expand Down Expand Up @@ -346,6 +355,7 @@ mod test {
value,
json!({
"target_id": blueprint_id,
"enabled": true,
"needs_saga_recovery": false,
})
);
Expand Down Expand Up @@ -444,6 +454,7 @@ mod test {
value,
json!({
"target_id": blueprint.1.id.to_string(),
"enabled": true,
"needs_saga_recovery": false,
})
);
Expand All @@ -463,7 +474,7 @@ mod test {
assert_eq!(
value,
json!({
"error": "blueprint disabled",
"enabled": false,
"target_id": blueprint.1.id.to_string()
})
);
Expand Down
Loading

0 comments on commit 937893a

Please sign in to comment.