Improve error reporting

This commit is contained in:
Zhaofeng Li 2021-11-18 13:15:20 -08:00
parent fae58994e4
commit c271780b63
9 changed files with 93 additions and 17 deletions

View file

@ -56,13 +56,19 @@ macro_rules! register_command {
macro_rules! handle_command {
($module:ident, $matches:ident) => {
if let Some(sub_matches) = $matches.subcommand_matches(stringify!($module)) {
command::$module::run(&$matches, &sub_matches).await;
crate::troubleshooter::run_wrapped(
&$matches, &sub_matches,
command::$module::run,
).await;
return;
}
};
($name:expr, $module:ident, $matches:ident) => {
if let Some(sub_matches) = $matches.subcommand_matches($name) {
command::$module::run(&$matches, &sub_matches).await;
crate::troubleshooter::run_wrapped(
&$matches, &sub_matches,
command::$module::run,
).await;
return;
}
};

View file

@ -13,6 +13,7 @@ use crate::nix::deployment::{
EvaluationNodeLimit,
ParallelismLimit,
};
use crate::nix::NixError;
use crate::nix::host::local as localhost;
use crate::util;
@ -114,13 +115,13 @@ pub fn subcommand() -> App<'static, 'static> {
util::register_selector_args(command)
}
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
let hive = util::hive_from_args(local_args).await.unwrap();
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) -> Result<(), NixError> {
let hive = util::hive_from_args(local_args).await?;
log::info!("Enumerating nodes...");
let all_nodes = hive.deployment_info().await.unwrap();
let all_nodes = hive.deployment_info().await?;
let nix_options = hive.nix_options().await.unwrap();
let nix_options = hive.nix_options().await?;
let selected_nodes = match local_args.value_of("on") {
Some(filter) => {
@ -241,4 +242,6 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
if !success {
quit::with_code(10);
}
Ok(())
}

View file

@ -12,7 +12,7 @@ use crate::nix::deployment::{
Target,
DeploymentOptions,
};
use crate::nix::host;
use crate::nix::{NixError, host};
use crate::util;
pub fn subcommand() -> App<'static, 'static> {
@ -57,7 +57,7 @@ By default, Colmena will deploy keys set in `deployment.keys` before activating
.takes_value(false))
}
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) -> Result<(), NixError> {
// Sanity check: Are we running NixOS?
if let Ok(os_release) = fs::read_to_string("/etc/os-release").await {
if !os_release.contains("ID=nixos\n") {
@ -131,6 +131,8 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
if !success {
quit::with_code(10);
}
Ok(())
}
async fn escalate(sudo: &str) -> ! {

View file

@ -3,6 +3,7 @@ use std::path::PathBuf;
use clap::{Arg, App, AppSettings, SubCommand, ArgMatches};
use crate::util;
use crate::nix::NixError;
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("eval")
@ -37,12 +38,12 @@ pub fn deprecated_alias() -> App<'static, 'static> {
.setting(AppSettings::Hidden)
}
pub async fn run(global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
pub async fn run(global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) -> Result<(), NixError> {
if let Some("introspect") = global_args.subcommand_name() {
log::warn!("`colmena introspect` has been renamed to `colmena eval`. Please update your scripts.");
}
let hive = util::hive_from_args(local_args).await.unwrap();
let hive = util::hive_from_args(local_args).await?;
if !(local_args.is_present("expression") ^ local_args.is_present("expression_file")) {
log::error!("Either an expression (-E) or a .nix file containing an expression should be specified, not both.");
@ -57,11 +58,13 @@ pub async fn run(global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
};
let instantiate = local_args.is_present("instantiate");
let result = hive.introspect(expression, instantiate).await.unwrap();
let result = hive.introspect(expression, instantiate).await?;
if instantiate {
print!("{}", result);
} else {
println!("{}", result);
}
Ok(())
}

View file

@ -54,11 +54,11 @@ It's recommended to use -- to separate Colmena options from the command to run.
util::register_selector_args(command)
}
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
let hive = util::hive_from_args(local_args).await.unwrap();
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) -> Result<(), NixError> {
let hive = util::hive_from_args(local_args).await?;
log::info!("Enumerating nodes...");
let all_nodes = hive.deployment_info().await.unwrap();
let all_nodes = hive.deployment_info().await?;
let selected_nodes = match local_args.value_of("on") {
Some(filter) => {
@ -167,4 +167,6 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
join_all(futures).await;
}).await;
Ok(())
}

View file

@ -1,14 +1,16 @@
use clap::{App, SubCommand, ArgMatches};
use crate::nix::NixCheck;
use crate::nix::{NixCheck, NixError};
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("nix-info")
.about("Show information about the current Nix installation")
}
pub async fn run(_global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>) {
pub async fn run(_global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>) -> Result<(), NixError> {
let check = NixCheck::detect().await;
check.print_version_info();
check.print_flakes_info(false);
Ok(())
}

View file

@ -3,6 +3,7 @@ use std::time::Duration;
use clap::{App, AppSettings, SubCommand, ArgMatches};
use tokio::time;
use crate::nix::NixError;
use crate::progress::{Progress, OutputStyle};
pub fn subcommand() -> App<'static, 'static> {
@ -11,7 +12,7 @@ pub fn subcommand() -> App<'static, 'static> {
.setting(AppSettings::Hidden)
}
pub async fn run(_global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>) {
pub async fn run(_global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>) -> Result<(), NixError> {
let progress = Progress::with_style(OutputStyle::Condensed);
let mut task = progress.create_task_progress(String::from("test"));
@ -21,4 +22,6 @@ pub async fn run(_global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>) {
}
task.success("Completed");
Ok(())
}

View file

@ -4,6 +4,7 @@ mod nix;
mod cli;
mod command;
mod progress;
mod troubleshooter;
mod util;
#[tokio::main]

54
src/troubleshooter.rs Normal file
View file

@ -0,0 +1,54 @@
//! Automatic troubleshooter.
//!
//! Tries to provide some useful hints when things go wrong.
use std::env;
use std::future::Future;
use clap::ArgMatches;
use crate::nix::NixError;
/// Runs a closure and tries to troubleshoot if it returns an error.
pub async fn run_wrapped<'a, F, U, T>(global_args: &'a ArgMatches<'a>, local_args: &'a ArgMatches<'a>, f: U) -> T
where U: FnOnce(&'a ArgMatches<'a>, &'a ArgMatches<'a>) -> F,
F: Future<Output = Result<T, NixError>>,
{
match f(global_args, local_args).await {
Ok(r) => r,
Err(error) => {
log::error!("-----");
log::error!("Operation failed with error: {}", error);
if let Err(own_error) = troubleshoot(global_args, local_args, &error) {
log::error!("Error occurred while trying to troubleshoot another error: {}", own_error);
}
// Ensure we exit with a code
quit::with_code(1);
},
}
}
fn troubleshoot(global_args: &ArgMatches<'_>, _local_args: &ArgMatches<'_>, error: &NixError) -> Result<(), NixError> {
match error {
NixError::NoFlakesSupport => {
// People following the tutorial might put hive.nix directly
// in their Colmena checkout, and encounter NoFlakesSupport
// because Colmena always prefers flake.nix when it exists.
if global_args.occurrences_of("config") == 0 {
let cwd = env::current_dir()?;
if cwd.join("flake.nix").is_file() && cwd.join("hive.nix").is_file() {
eprintln!("Hint: You have both flake.nix and hive.nix in the current directory, and");
eprintln!(" Colmena will always prefer flake.nix if it exists.");
eprintln!();
eprintln!(" Try passing `-f hive.nix` explicitly if this is what you want.");
}
};
}
_ => {},
}
Ok(())
}