Skip to content

Commit 374ac00

Browse files
authored
[fm] add opt-in ANSI terminal colors to displayers (#10862)
1 parent 9932e36 commit 374ac00

10 files changed

Lines changed: 1024 additions & 293 deletions

File tree

Cargo.lock

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

dev-tools/omdb/src/bin/omdb/db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,7 @@ impl DbArgs {
14461446
args.exec(&omdb, &opctx, &datastore).await
14471447
}
14481448
DbCommands::Sitrep(args) => {
1449-
sitrep::cmd_db_sitrep(&opctx, &datastore, &fetch_opts, args).await
1449+
sitrep::cmd_db_sitrep(&omdb, &opctx, &datastore, &fetch_opts, args).await
14501450
}
14511451
DbCommands::Sitreps(args) => {
14521452
sitrep::cmd_db_sitrep_history(&opctx, &datastore, &fetch_opts, args).await

dev-tools/omdb/src/bin/omdb/db/sitrep.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
//! `omdb db sitrep` subcommands
66
7+
use crate::Omdb;
78
use crate::db::DbFetchOptions;
89
use crate::db::check_limit;
910
use crate::helpers::const_max_len;
1011
use crate::helpers::datetime_opt_rfc3339_concise;
12+
use crate::helpers::should_colorize;
1113
use anyhow::Context;
1214
use async_bb8_diesel::AsyncRunQueryDsl;
1315
use chrono::{DateTime, Utc};
@@ -239,17 +241,23 @@ impl SitrepSelector {
239241
}
240242

241243
pub(super) async fn cmd_db_sitrep(
244+
omdb: &Omdb,
242245
opctx: &OpContext,
243246
datastore: &DataStore,
244247
fetch_opts: &DbFetchOptions,
245248
args: &SitrepArgs,
246249
) -> anyhow::Result<()> {
250+
let colored =
251+
should_colorize(omdb.output.color, supports_color::Stream::Stdout);
247252
match args.command {
248253
Commands::History(ref args) => {
249254
cmd_db_sitrep_history(opctx, datastore, fetch_opts, args).await
250255
}
251256
Commands::Info { sitrep, opts: ref args } => {
252-
cmd_db_sitrep_show(opctx, datastore, fetch_opts, args, sitrep).await
257+
cmd_db_sitrep_show(
258+
opctx, datastore, fetch_opts, args, sitrep, colored,
259+
)
260+
.await
253261
}
254262
Commands::Current(ref args) => {
255263
cmd_db_sitrep_show(
@@ -258,12 +266,15 @@ pub(super) async fn cmd_db_sitrep(
258266
fetch_opts,
259267
args,
260268
SitrepSelector::Current,
269+
colored,
261270
)
262271
.await
263272
}
264273
Commands::AnalysisReport(ref args) => {
265-
cmd_db_sitrep_analysis_report(opctx, datastore, fetch_opts, args)
266-
.await
274+
cmd_db_sitrep_analysis_report(
275+
opctx, datastore, fetch_opts, args, colored,
276+
)
277+
.await
267278
}
268279
}
269280
}
@@ -347,6 +358,7 @@ async fn cmd_db_sitrep_show(
347358
_fetch_opts: &DbFetchOptions,
348359
opts: &ShowOptions,
349360
sitrep_selector: SitrepSelector,
361+
colored: bool,
350362
) -> anyhow::Result<()> {
351363
let current_version = datastore
352364
.fm_current_sitrep_version(&opctx)
@@ -499,7 +511,7 @@ async fn cmd_db_sitrep_show(
499511
if !cases.is_empty() {
500512
println!("\n{:=<80}\n", "== CASES ");
501513
for case in cases {
502-
println!("{}", case.display_indented(4, Some(id)));
514+
println!("{}", case.display_indented(4, Some(id)).colored(colored));
503515
}
504516
}
505517

@@ -511,6 +523,7 @@ async fn cmd_db_sitrep_analysis_report(
511523
datastore: &DataStore,
512524
_fetch_opts: &DbFetchOptions,
513525
args: &AnalysisReportArgs,
526+
colored: bool,
514527
) -> anyhow::Result<()> {
515528
let &AnalysisReportArgs { sitrep, ref opts } = args;
516529
let (_, id) = sitrep.resolve(&datastore, &opctx).await?;
@@ -519,14 +532,15 @@ async fn cmd_db_sitrep_analysis_report(
519532
load_analysis_report(datastore, id).await.with_context(|| {
520533
format!("failed to load analysis report for {err_ctx}",)
521534
})?;
522-
print_analysis_report(&report, opts.json)?;
535+
print_analysis_report(&report, opts.json, colored)?;
523536

524537
Ok(())
525538
}
526539

527540
fn print_analysis_report(
528541
report: &model::fm::SitrepAnalysisReport,
529542
json: bool,
543+
colored: bool,
530544
) -> anyhow::Result<()> {
531545
use nexus_types::fm::analysis_reports::{AnalysisReport, InputReport};
532546

@@ -566,27 +580,33 @@ fn print_analysis_report(
566580

567581
println!("\n{:=<80}", "== ANALYSIS INPUT REPORT ");
568582
match serde_json::from_value::<InputReport>(input_report.clone()) {
569-
Ok(report) => println!("{}", report.display_multiline(0)),
583+
Ok(report) => {
584+
println!("{}", report.display_multiline(0).colored(colored))
585+
}
570586
Err(e) => {
571587
eprintln!(
572588
"WARNING: failed to parse input report; falling back to \
573589
less structured output: {e}"
574590
);
575-
let displayer = nexus_types::fm::display::Json::new(&input_report);
591+
let displayer = nexus_types::fm::display::Json::new(&input_report)
592+
.colored(colored);
576593
println!("{displayer}");
577594
}
578595
}
579596

580597
println!("\n{:=<80}", "== ANALYSIS REPORT ");
581598
match serde_json::from_value::<AnalysisReport>(analysis_report.clone()) {
582-
Ok(report) => println!("{}", report.display_multiline(0)),
599+
Ok(report) => {
600+
println!("{}", report.display_multiline(0).colored(colored))
601+
}
583602
Err(e) => {
584603
eprintln!(
585604
"WARNING: failed to parse analysis report; falling back to \
586605
less structured output: {e}"
587606
);
588607
let displayer =
589-
nexus_types::fm::display::Json::new(&analysis_report);
608+
nexus_types::fm::display::Json::new(&analysis_report)
609+
.colored(colored);
590610
println!("{displayer}");
591611
}
592612
}

dev-tools/omdb/src/bin/omdb/nexus.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,14 @@ impl NexusArgs {
722722
}) => cmd_nexus_background_tasks_list(&client).await,
723723
NexusCommands::BackgroundTasks(BackgroundTasksArgs {
724724
command: BackgroundTasksCommands::Show(args),
725-
}) => cmd_nexus_background_tasks_show(&client, args).await,
725+
}) => {
726+
cmd_nexus_background_tasks_show(
727+
&client,
728+
args,
729+
omdb.output.color,
730+
)
731+
.await
732+
}
726733
NexusCommands::BackgroundTasks(BackgroundTasksArgs {
727734
command: BackgroundTasksCommands::PrintReport(args),
728735
}) => {
@@ -990,6 +997,7 @@ async fn cmd_nexus_background_tasks_list(
990997
async fn cmd_nexus_background_tasks_show(
991998
client: &nexus_lockstep_client::Client,
992999
args: &BackgroundTasksShowArgs,
1000+
color: ColorChoice,
9931001
) -> Result<(), anyhow::Error> {
9941002
let response =
9951003
client.bgtask_list().await.context("listing background tasks")?;
@@ -1039,6 +1047,7 @@ async fn cmd_nexus_background_tasks_show(
10391047

10401048
let opts = BackgroundTasksPrintOpts {
10411049
show_executing_info: !args.no_executing_info,
1050+
colored: should_colorize(color, supports_color::Stream::Stdout),
10421051
};
10431052

10441053
// Some tasks should be grouped and printed together in a certain order,
@@ -1137,6 +1146,8 @@ async fn cmd_nexus_background_tasks_activate(
11371146
#[derive(Clone, Debug)]
11381147
struct BackgroundTasksPrintOpts {
11391148
show_executing_info: bool,
1149+
/// Whether to style output with ANSI terminal colors.
1150+
colored: bool,
11401151
}
11411152

11421153
fn print_task(bgtask: &BackgroundTask, opts: &BackgroundTasksPrintOpts) {
@@ -1185,7 +1196,7 @@ fn print_task(bgtask: &BackgroundTask, opts: &BackgroundTasksPrintOpts) {
11851196
// unstable -- it gets exposed by background tasks as unstructured
11861197
// (schemaless) data. We make a best effort to interpret it.
11871198
if let LastResult::Completed(completed) = &bgtask.last {
1188-
print_task_details(&bgtask, &completed.details);
1199+
print_task_details(&bgtask, &completed.details, opts.colored);
11891200
}
11901201
}
11911202

@@ -1229,7 +1240,11 @@ fn print_start_end_time(
12291240
/// undocumented and unstable (subject to change). That does make this code
12301241
/// both ugly and brittle. It's not a fatal error to fail to parse these, but
12311242
/// we do warn the user if that happens.
1232-
fn print_task_details(bgtask: &BackgroundTask, details: &serde_json::Value) {
1243+
fn print_task_details(
1244+
bgtask: &BackgroundTask,
1245+
details: &serde_json::Value,
1246+
colored: bool,
1247+
) {
12331248
// All tasks might produce an "error" property. If we find one, print that
12341249
// out and stop.
12351250
#[derive(Deserialize)]
@@ -1362,7 +1377,7 @@ fn print_task_details(bgtask: &BackgroundTask, details: &serde_json::Value) {
13621377
print_task_webhook_deliverator(details);
13631378
}
13641379
"fm_analysis" => {
1365-
print_task_fm_analysis(details);
1380+
print_task_fm_analysis(details, colored);
13661381
}
13671382
"fm_sitrep_loader" => {
13681383
print_task_fm_sitrep_loader(details);
@@ -3490,7 +3505,7 @@ mod ereporter_status_fields {
34903505
pub const NUM_WIDTH: usize = 4;
34913506
}
34923507

3493-
fn print_task_fm_analysis(details: &serde_json::Value) {
3508+
fn print_task_fm_analysis(details: &serde_json::Value, colored: bool) {
34943509
use nexus_types::internal_api::background::fm_analysis::{
34953510
AnalysisOutcome, AnalysisStatus, Outcome, PreparationStatus,
34963511
};
@@ -3629,7 +3644,7 @@ fn print_task_fm_analysis(details: &serde_json::Value) {
36293644

36303645
let PreparationStatus { warnings, report: prep_report } = prep_status;
36313646
println!(" preparation report:");
3632-
print!("{}", prep_report.display_multiline(6));
3647+
print!("{}", prep_report.display_multiline(6).colored(colored));
36333648
if !warnings.is_empty() {
36343649
println!("{ERRICON} non-fatal errors preparing analysis inputs:");
36353650
for error in warnings {
@@ -3639,7 +3654,7 @@ fn print_task_fm_analysis(details: &serde_json::Value) {
36393654

36403655
println!();
36413656
println!(" analysis report:");
3642-
print!("{}", analysis_report.display_multiline(6));
3657+
print!("{}", analysis_report.display_multiline(6).colored(colored));
36433658
print_start_end_time(start_time, end_time, 4);
36443659
}
36453660

nexus/types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ itertools.workspace = true
3535
newtype_derive.workspace = true
3636
omicron-uuid-kinds.workspace = true
3737
openssl.workspace = true
38+
owo-colors.workspace = true
3839
oximeter-db.workspace = true
3940
oxnet.workspace = true
4041
oxql-types.workspace = true
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// an example analysis report
2+
sitrep ID: ea7affb0-36eb-4a9a-b9bd-22e00f1bcc04
3+
analysis log:
4+
* began analysis
5+
cases (1 with activity):
6+
* case b0d36461-e3e7-4a53-9162-82414fb30088
7+
// PSU 0 faulted
8+
diagnosis engine: power_shelf
9+
opened in sitrep: ea7affb0-36eb-4a9a-b9bd-22e00f1bcc04 <-- this sitrep
10+
activity in this analysis:
11+
* opened case
12+
* /!\ WARNING: something suspicious:
13+
// hmmm...
14+
* key: value

0 commit comments

Comments
 (0)