From 31fd1e49acd83f73471965a3e70c6c651c57b96f Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sat, 8 Jan 2022 01:20:36 -0800 Subject: [PATCH] Move nix::{NixResult, NixError} to error::{ColmenaResult, ColmenaError} --- src/command/apply.rs | 5 +- src/command/apply_local.rs | 5 +- src/command/eval.rs | 4 +- src/command/exec.rs | 5 +- src/command/nix_info.rs | 5 +- src/command/test_progress.rs | 7 ++- src/error.rs | 103 +++++++++++++++++++++++++++++++++ src/job.rs | 55 +++++++++--------- src/main.rs | 1 + src/nix/deployment/mod.rs | 34 +++++------ src/nix/flake.rs | 10 ++-- src/nix/hive/mod.rs | 32 +++++------ src/nix/host/key_uploader.rs | 5 +- src/nix/host/local.rs | 21 +++---- src/nix/host/mod.rs | 24 ++++---- src/nix/host/ssh.rs | 25 ++++---- src/nix/info.rs | 6 +- src/nix/mod.rs | 107 ++--------------------------------- src/nix/node_filter.rs | 14 ++--- src/nix/profile.rs | 20 +++---- src/nix/store.rs | 23 ++++---- src/progress/mod.rs | 6 +- src/progress/plain.rs | 4 +- src/progress/spinner.rs | 4 +- src/troubleshooter.rs | 8 +-- src/util.rs | 37 ++++++------ 26 files changed, 295 insertions(+), 275 deletions(-) create mode 100644 src/error.rs diff --git a/src/command/apply.rs b/src/command/apply.rs index 2dd385f..9d72739 100644 --- a/src/command/apply.rs +++ b/src/command/apply.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use clap::{Arg, App, ArgMatches, ArgSettings}; +use crate::error::ColmenaError; use crate::nix::deployment::{ Deployment, Goal, @@ -11,7 +12,7 @@ use crate::nix::deployment::{ ParallelismLimit, }; use crate::progress::SimpleProgressOutput; -use crate::nix::{NixError, NodeFilter}; +use crate::nix::NodeFilter; use crate::util; pub fn register_deploy_args(command: App) -> App { @@ -126,7 +127,7 @@ pub fn subcommand() -> App<'static> { util::register_selector_args(command) } -pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), NixError> { +pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> { let hive = util::hive_from_args(local_args).await?; let ssh_config = env::var("SSH_CONFIG_FILE") diff --git a/src/command/apply_local.rs b/src/command/apply_local.rs index 70c1f58..c375560 100644 --- a/src/command/apply_local.rs +++ b/src/command/apply_local.rs @@ -5,13 +5,14 @@ use clap::{Arg, App, ArgMatches}; use tokio::fs; use tokio::process::Command; +use crate::error::ColmenaError; use crate::nix::deployment::{ Deployment, Goal, TargetNode, Options, }; -use crate::nix::{NixError, NodeName, host}; +use crate::nix::{NodeName, host}; use crate::progress::SimpleProgressOutput; use crate::util; @@ -57,7 +58,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) -> Result<(), NixError> { +pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> { // 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") { diff --git a/src/command/eval.rs b/src/command/eval.rs index 4ebb8b0..c7abf89 100644 --- a/src/command/eval.rs +++ b/src/command/eval.rs @@ -2,8 +2,8 @@ use std::path::PathBuf; use clap::{Arg, App, AppSettings, ArgMatches}; +use crate::error::ColmenaError; use crate::util; -use crate::nix::NixError; pub fn subcommand() -> App<'static> { subcommand_gen("eval") @@ -41,7 +41,7 @@ For example, to retrieve the configuration of one node, you may write something .takes_value(false)) } -pub async fn run(global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), NixError> { +pub async fn run(global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> { if let Some("introspect") = global_args.subcommand_name() { log::warn!("`colmena introspect` has been renamed to `colmena eval`. Please update your scripts."); } diff --git a/src/command/exec.rs b/src/command/exec.rs index 80427d5..b086b9e 100644 --- a/src/command/exec.rs +++ b/src/command/exec.rs @@ -6,8 +6,9 @@ use clap::{Arg, App, AppSettings, ArgMatches}; use futures::future::join_all; use tokio::sync::Semaphore; -use crate::nix::{NixError, NodeFilter}; +use crate::error::ColmenaError; use crate::job::{JobMonitor, JobState, JobType}; +use crate::nix::NodeFilter; use crate::progress::SimpleProgressOutput; use crate::util; @@ -54,7 +55,7 @@ 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) -> Result<(), NixError> { +pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> { let hive = util::hive_from_args(local_args).await?; let ssh_config = env::var("SSH_CONFIG_FILE") .ok().map(PathBuf::from); diff --git a/src/command/nix_info.rs b/src/command/nix_info.rs index bffb600..d447b5a 100644 --- a/src/command/nix_info.rs +++ b/src/command/nix_info.rs @@ -1,13 +1,14 @@ use clap::{App, ArgMatches}; -use crate::nix::{NixCheck, NixError}; +use crate::error::ColmenaError; +use crate::nix::NixCheck; pub fn subcommand() -> App<'static> { App::new("nix-info") .about("Show information about the current Nix installation") } -pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), NixError> { +pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), ColmenaError> { let check = NixCheck::detect().await; check.print_version_info(); check.print_flakes_info(false); diff --git a/src/command/test_progress.rs b/src/command/test_progress.rs index 8ed29be..94d96f3 100644 --- a/src/command/test_progress.rs +++ b/src/command/test_progress.rs @@ -3,8 +3,9 @@ use std::time::Duration; use clap::{App, AppSettings, ArgMatches}; use tokio::time; +use crate::error::{ColmenaError, ColmenaResult}; use crate::job::{JobMonitor, JobType}; -use crate::nix::{NixError, NixResult, NodeName}; +use crate::nix::NodeName; use crate::progress::{ProgressOutput, spinner::SpinnerOutput}; macro_rules! node { @@ -19,7 +20,7 @@ pub fn subcommand() -> App<'static> { .setting(AppSettings::Hidden) } -pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), NixError> { +pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), ColmenaError> { let mut output = SpinnerOutput::new(); let (monitor, meta) = JobMonitor::new(output.get_sender()); @@ -52,7 +53,7 @@ pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result< let (_, _) = tokio::join!(eval, build); - Err(NixError::Unsupported) as NixResult<()> + Err(ColmenaError::Unsupported) as ColmenaResult<()> }); let (monitor, output, ret) = tokio::join!( diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..9758f9a --- /dev/null +++ b/src/error.rs @@ -0,0 +1,103 @@ +//! Custom error types. + +use std::os::unix::process::ExitStatusExt; +use std::process::ExitStatus; + +use snafu::Snafu; +use validator::ValidationErrors; + +use crate::nix::{key, StorePath}; + +pub type ColmenaResult = Result; + +#[non_exhaustive] +#[derive(Debug, Snafu)] +pub enum ColmenaError { + #[snafu(display("I/O Error: {}", error))] + IoError { error: std::io::Error }, + + #[snafu(display("Nix returned invalid response: {}", output))] + BadOutput { output: String }, + + #[snafu(display("Nix exited with error code: {}", exit_code))] + NixFailure { exit_code: i32 }, + + #[snafu(display("Nix was killed by signal {}", signal))] + NixKilled { signal: i32 }, + + #[snafu(display("This operation is not supported"))] + Unsupported, + + #[snafu(display("Invalid Nix store path"))] + InvalidStorePath, + + #[snafu(display("Validation error"))] + ValidationError { errors: ValidationErrors }, + + #[snafu(display("Failed to upload keys: {}", error))] + KeyError { error: key::KeyError }, + + #[snafu(display("Store path {:?} is not a derivation", store_path))] + NotADerivation { store_path: StorePath }, + + #[snafu(display("Invalid NixOS system profile"))] + InvalidProfile, + + #[snafu(display("Unknown active profile: {:?}", store_path))] + ActiveProfileUnknown { store_path: StorePath }, + + #[snafu(display("Could not determine current profile"))] + FailedToGetCurrentProfile, + + #[snafu(display("Current Nix version does not support Flakes"))] + NoFlakesSupport, + + #[snafu(display("Don't know how to connect to the node"))] + NoTargetHost, + + #[snafu(display("Node name cannot be empty"))] + EmptyNodeName, + + #[snafu(display("Filter rule cannot be empty"))] + EmptyFilterRule, + + #[snafu(display("Deployment already executed"))] + DeploymentAlreadyExecuted, + + #[snafu(display("Unknown error: {}", message))] + Unknown { message: String }, +} + +impl From for ColmenaError { + fn from(error: std::io::Error) -> Self { + Self::IoError { error } + } +} + +impl From for ColmenaError { + fn from(error: key::KeyError) -> Self { + Self::KeyError { error } + } +} + +impl From for ColmenaError { + fn from(errors: ValidationErrors) -> Self { + Self::ValidationError { errors } + } +} + +impl From for ColmenaError { + fn from(status: ExitStatus) -> Self { + match status.code() { + Some(exit_code) => Self::NixFailure { exit_code }, + None => Self::NixKilled { signal: status.signal().unwrap() }, + } + } +} + +impl ColmenaError { + pub fn unknown(error: Box) -> Self { + let message = error.to_string(); + Self::Unknown { message } + } +} diff --git a/src/job.rs b/src/job.rs index 4895359..a205bad 100644 --- a/src/job.rs +++ b/src/job.rs @@ -13,7 +13,8 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::time; use uuid::Uuid; -use crate::nix::{NixResult, NixError, NodeName}; +use crate::error::{ColmenaResult, ColmenaError}; +use crate::nix::NodeName; use crate::progress::{Sender as ProgressSender, Message as ProgressMessage, Line, LineStyle}; pub type Sender = UnboundedSender; @@ -192,7 +193,7 @@ pub enum EventPayload { /// The job failed. /// - /// We can't pass the NixError because the wrapper needs to + /// We can't pass the ColmenaError because the wrapper needs to /// be able to return it as-is. Failure(String), @@ -277,7 +278,7 @@ impl JobMonitor { } /// Starts the monitor. - pub async fn run_until_completion(mut self) -> NixResult { + pub async fn run_until_completion(mut self) -> ColmenaResult { if let Some(width) = self.label_width { if let Some(sender) = &self.progress { sender.send(ProgressMessage::HintLabelWidth(width)).unwrap(); @@ -465,7 +466,7 @@ impl JobMonitor { } /// Shows human-readable summary and performs cleanup. - async fn finish(mut self) -> NixResult { + async fn finish(mut self) -> ColmenaResult { if let Some(sender) = self.progress.take() { sender.send(ProgressMessage::Complete).unwrap(); } @@ -500,7 +501,7 @@ impl JobHandleInner { /// Creates a new job with a distinct ID. /// /// This sends out a Creation message with the metadata. - pub fn create_job(&self, job_type: JobType, nodes: Vec) -> NixResult { + pub fn create_job(&self, job_type: JobType, nodes: Vec) -> ColmenaResult { let job_id = JobId::new(); let creation = JobCreation { friendly_name: None, @@ -509,7 +510,7 @@ impl JobHandleInner { }; if job_type == JobType::Meta { - return Err(NixError::Unknown { message: "Cannot create a meta job!".to_string() }); + return Err(ColmenaError::Unknown { message: "Cannot create a meta job!".to_string() }); } let new_handle = Arc::new(Self { @@ -525,9 +526,9 @@ impl JobHandleInner { /// Runs a closure, automatically updating the job monitor based on the result. /// /// This immediately transitions the state to Running. - pub async fn run(self: Arc, f: U) -> NixResult + pub async fn run(self: Arc, f: U) -> ColmenaResult where U: FnOnce(Arc) -> F, - F: Future>, + F: Future>, { self.run_internal(f, true).await } @@ -535,52 +536,52 @@ impl JobHandleInner { /// Runs a closure, automatically updating the job monitor based on the result. /// /// This does not immediately transition the state to Running. - pub async fn run_waiting(self: Arc, f: U) -> NixResult + pub async fn run_waiting(self: Arc, f: U) -> ColmenaResult where U: FnOnce(Arc) -> F, - F: Future>, + F: Future>, { self.run_internal(f, false).await } /// Sends a line of child stdout to the job monitor. - pub fn stdout(&self, output: String) -> NixResult<()> { + pub fn stdout(&self, output: String) -> ColmenaResult<()> { self.send_payload(EventPayload::ChildStdout(output)) } /// Sends a line of child stderr to the job monitor. - pub fn stderr(&self, output: String) -> NixResult<()> { + pub fn stderr(&self, output: String) -> ColmenaResult<()> { self.send_payload(EventPayload::ChildStderr(output)) } /// Sends a human-readable message to the job monitor. - pub fn message(&self, message: String) -> NixResult<()> { + pub fn message(&self, message: String) -> ColmenaResult<()> { self.send_payload(EventPayload::Message(message)) } /// Transitions to a new job state. - pub fn state(&self, new_state: JobState) -> NixResult<()> { + pub fn state(&self, new_state: JobState) -> ColmenaResult<()> { self.send_payload(EventPayload::NewState(new_state)) } /// Marks the job as successful, with a custom message. - pub fn success_with_message(&self, message: String) -> NixResult<()> { + pub fn success_with_message(&self, message: String) -> ColmenaResult<()> { self.send_payload(EventPayload::SuccessWithMessage(message)) } /// Marks the job as noop. - pub fn noop(&self, message: String) -> NixResult<()> { + pub fn noop(&self, message: String) -> ColmenaResult<()> { self.send_payload(EventPayload::Noop(message)) } /// Marks the job as failed. - pub fn failure(&self, error: &NixError) -> NixResult<()> { + pub fn failure(&self, error: &ColmenaError) -> ColmenaResult<()> { self.send_payload(EventPayload::Failure(error.to_string())) } /// Runs a closure, automatically updating the job monitor based on the result. - async fn run_internal(self: Arc, f: U, report_running: bool) -> NixResult + async fn run_internal(self: Arc, f: U, report_running: bool) -> ColmenaResult where U: FnOnce(Arc) -> F, - F: Future>, + F: Future>, { if report_running { // Tell monitor we are starting @@ -603,7 +604,7 @@ impl JobHandleInner { } /// Sends an event to the job monitor. - fn send_payload(&self, payload: EventPayload) -> NixResult<()> { + fn send_payload(&self, payload: EventPayload) -> ColmenaResult<()> { if payload.privileged() { panic!("Tried to send privileged payload with JobHandle"); } @@ -611,7 +612,7 @@ impl JobHandleInner { let event = Event::new(self.job_id, payload); self.sender.send(event) - .map_err(|e| NixError::unknown(Box::new(e)))?; + .map_err(|e| ColmenaError::unknown(Box::new(e)))?; Ok(()) } @@ -619,9 +620,9 @@ impl JobHandleInner { impl MetaJobHandle { /// Runs a closure, automatically updating the job monitor based on the result. - pub async fn run(self, f: U) -> NixResult + pub async fn run(self, f: U) -> ColmenaResult where U: FnOnce(JobHandle) -> F, - F: Future>, + F: Future>, { let normal_handle = Arc::new(JobHandleInner { job_id: self.job_id, @@ -645,11 +646,11 @@ impl MetaJobHandle { } /// Sends an event to the job monitor. - fn send_payload(&self, payload: EventPayload) -> NixResult<()> { + fn send_payload(&self, payload: EventPayload) -> ColmenaResult<()> { let event = Event::new(self.job_id, payload); self.sender.send(event) - .map_err(|e| NixError::unknown(Box::new(e)))?; + .map_err(|e| ColmenaError::unknown(Box::new(e)))?; Ok(()) } @@ -874,7 +875,7 @@ mod tests { Ok(()) }).await?; - Err(NixError::Unsupported) as NixResult<()> + Err(ColmenaError::Unsupported) as ColmenaResult<()> }); // Run until completion @@ -884,7 +885,7 @@ mod tests { ); match ret { - Err(NixError::Unsupported) => (), + Err(ColmenaError::Unsupported) => (), _ => { panic!("Wrapper must return error as-is"); } diff --git a/src/main.rs b/src/main.rs index ae9ba73..be03b43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![deny(unused_must_use)] +mod error; mod nix; mod cli; mod command; diff --git a/src/nix/deployment/mod.rs b/src/nix/deployment/mod.rs index b098a55..e5db73a 100644 --- a/src/nix/deployment/mod.rs +++ b/src/nix/deployment/mod.rs @@ -26,8 +26,8 @@ use super::{ Host, NodeName, NodeConfig, - NixError, - NixResult, + ColmenaError, + ColmenaResult, Profile, ProfileDerivation, CopyDirection, @@ -120,9 +120,9 @@ impl Deployment { /// /// If a ProgressSender is supplied, then this should be run in parallel /// with its `run_until_completion()` future. - pub async fn execute(mut self) -> NixResult<()> { + pub async fn execute(mut self) -> ColmenaResult<()> { if self.executed { - return Err(NixError::DeploymentAlreadyExecuted); + return Err(ColmenaError::DeploymentAlreadyExecuted); } self.executed = true; @@ -148,7 +148,7 @@ impl Deployment { } join_all(futures).await - .into_iter().collect::>>()?; + .into_iter().collect::>>()?; Ok(()) }); @@ -173,7 +173,7 @@ impl Deployment { } join_all(futures).await - .into_iter().collect::>>()?; + .into_iter().collect::>>()?; Ok(()) }); @@ -219,7 +219,7 @@ impl Deployment { } /// Executes the deployment against a portion of nodes. - async fn execute_chunk(self: &DeploymentHandle, parent: JobHandle, mut chunk: TargetNodeMap) -> NixResult<()> { + async fn execute_chunk(self: &DeploymentHandle, parent: JobHandle, mut chunk: TargetNodeMap) -> ColmenaResult<()> { if self.goal == Goal::UploadKeys { unreachable!(); // some logic is screwed up } @@ -256,14 +256,14 @@ impl Deployment { } join_all(futures).await - .into_iter().collect::>>()?; + .into_iter().collect::>>()?; Ok(()) } /// Evaluates a set of nodes, returning their corresponding store derivations. async fn evaluate_nodes(self: &DeploymentHandle, parent: JobHandle, nodes: Vec) - -> NixResult> + -> ColmenaResult> { let job = parent.create_job(JobType::Evaluate, nodes.clone())?; @@ -280,12 +280,12 @@ impl Deployment { } /// Only uploads keys to a node. - async fn upload_keys_to_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode) -> NixResult<()> { + async fn upload_keys_to_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode) -> ColmenaResult<()> { let nodes = vec![target.name.clone()]; let job = parent.create_job(JobType::UploadKeys, nodes)?; job.run(|_| async move { if target.host.is_none() { - return Err(NixError::Unsupported); + return Err(ColmenaError::Unsupported); } let host = target.host.as_mut().unwrap(); @@ -297,7 +297,7 @@ impl Deployment { /// Builds a system profile directly on the node itself. async fn build_on_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile_drv: ProfileDerivation) - -> NixResult<(TargetNode, Profile)> + -> ColmenaResult<(TargetNode, Profile)> { let nodes = vec![target.name.clone()]; @@ -306,7 +306,7 @@ impl Deployment { let build_job = parent.create_job(JobType::Build, nodes.clone())?; let (target, profile) = build_job.run(|job| async move { if target.host.is_none() { - return Err(NixError::Unsupported); + return Err(ColmenaError::Unsupported); } let mut host = target.host.as_mut().unwrap(); @@ -331,7 +331,7 @@ impl Deployment { /// Builds and pushes a system profile on a node. async fn build_and_push_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile_drv: ProfileDerivation) - -> NixResult<(TargetNode, Profile)> + -> ColmenaResult<(TargetNode, Profile)> { let nodes = vec![target.name.clone()]; @@ -361,7 +361,7 @@ impl Deployment { let arc_self = self.clone(); let target = push_job.run(|job| async move { if target.host.is_none() { - return Err(NixError::Unsupported); + return Err(ColmenaError::Unsupported); } let host = target.host.as_mut().unwrap(); @@ -383,7 +383,7 @@ impl Deployment { /// /// This will also upload keys to the node. async fn activate_node(self: DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile: Profile) - -> NixResult<()> + -> ColmenaResult<()> { let nodes = vec![target.name.clone()]; let target_name = target.name.clone(); @@ -437,7 +437,7 @@ impl Deployment { if arc_self.options.force_replace_unknown_profiles { job.message("Warning: Remote profile is unknown, but unknown profiles are being ignored".to_string())?; } else { - return Err(NixError::ActiveProfileUnknown { + return Err(ColmenaError::ActiveProfileUnknown { store_path: profile, }); } diff --git a/src/nix/flake.rs b/src/nix/flake.rs index 2a2a269..b897e2a 100644 --- a/src/nix/flake.rs +++ b/src/nix/flake.rs @@ -7,7 +7,7 @@ use std::process::Stdio; use serde::Deserialize; use tokio::process::Command; -use super::{NixCheck, NixError, NixResult}; +use super::{NixCheck, ColmenaError, ColmenaResult}; /// A Nix Flake. #[derive(Debug)] @@ -24,7 +24,7 @@ impl Flake { /// /// This will try to retrieve the resolved URL of the local flake /// in the specified directory. - pub async fn from_dir>(dir: P) -> NixResult { + pub async fn from_dir>(dir: P) -> ColmenaResult { NixCheck::require_flake_support().await?; let flake = dir.as_ref().as_os_str().to_str() @@ -39,7 +39,7 @@ impl Flake { } /// Creates a flake from a Flake URI. - pub async fn from_uri(uri: String) -> NixResult { + pub async fn from_uri(uri: String) -> ColmenaResult { NixCheck::require_flake_support().await?; Ok(Self { @@ -69,7 +69,7 @@ struct FlakeMetadata { impl FlakeMetadata { /// Resolves a flake. - async fn resolve(flake: &str) -> NixResult { + async fn resolve(flake: &str) -> ColmenaResult { let child = Command::new("nix") .args(&["flake", "metadata", "--json"]) .args(&["--experimental-features", "nix-command flakes"]) @@ -86,7 +86,7 @@ impl FlakeMetadata { serde_json::from_slice::(&output.stdout) .map_err(|_| { let output = String::from_utf8_lossy(&output.stdout).to_string(); - NixError::BadOutput { output } + ColmenaError::BadOutput { output } }) } } diff --git a/src/nix/hive/mod.rs b/src/nix/hive/mod.rs index 46aa805..acab5ed 100644 --- a/src/nix/hive/mod.rs +++ b/src/nix/hive/mod.rs @@ -11,7 +11,7 @@ use validator::Validate; use super::{ Flake, - NixResult, + ColmenaResult, NodeName, NodeConfig, NodeFilter, @@ -39,7 +39,7 @@ pub enum HivePath { } impl HivePath { - pub async fn from_path>(path: P) -> NixResult { + pub async fn from_path>(path: P) -> ColmenaResult { let path = path.as_ref(); if let Some(osstr) = path.file_name() { @@ -87,7 +87,7 @@ pub struct Hive { } impl Hive { - pub fn new(path: HivePath) -> NixResult { + pub fn new(path: HivePath) -> ColmenaResult { let mut eval_nix = NamedTempFile::new()?; eval_nix.write_all(HIVE_EVAL).unwrap(); @@ -110,7 +110,7 @@ impl Hive { self.show_trace = value; } - pub async fn nix_options(&self) -> NixResult> { + pub async fn nix_options(&self) -> ColmenaResult> { let mut options = self.builder_args().await?; if self.show_trace { @@ -121,7 +121,7 @@ impl Hive { } /// Convenience wrapper to filter nodes for CLI actions. - pub async fn select_nodes(&self, filter: Option, ssh_config: Option, ssh_only: bool) -> NixResult> { + pub async fn select_nodes(&self, filter: Option, ssh_config: Option, ssh_only: bool) -> ColmenaResult> { let mut node_configs = None; log::info!("Enumerating nodes..."); @@ -197,13 +197,13 @@ impl Hive { } /// Returns a list of all node names. - pub async fn node_names(&self) -> NixResult> { + pub async fn node_names(&self) -> ColmenaResult> { self.nix_instantiate("attrNames hive.nodes").eval() .capture_json().await } /// Retrieve deployment info for all nodes. - pub async fn deployment_info(&self) -> NixResult> { + pub async fn deployment_info(&self) -> ColmenaResult> { let configs: HashMap = self.nix_instantiate("hive.deploymentConfig").eval_with_builders().await? .capture_json().await?; @@ -217,14 +217,14 @@ impl Hive { } /// Retrieve deployment info for a single node. - pub async fn deployment_info_single(&self, node: &NodeName) -> NixResult> { + pub async fn deployment_info_single(&self, node: &NodeName) -> ColmenaResult> { let expr = format!("hive.nodes.\"{}\".config.deployment or null", node.as_str()); self.nix_instantiate(&expr).eval_with_builders().await? .capture_json().await } /// Retrieve deployment info for a list of nodes. - pub async fn deployment_info_selected(&self, nodes: &[NodeName]) -> NixResult> { + pub async fn deployment_info_selected(&self, nodes: &[NodeName]) -> ColmenaResult> { let nodes_expr = SerializedNixExpresssion::new(nodes)?; let configs: HashMap = self.nix_instantiate(&format!("hive.deploymentConfigSelected {}", nodes_expr.expression())) @@ -246,7 +246,7 @@ impl Hive { /// Evaluation may take up a lot of memory, so we make it possible /// to split up the evaluation process into chunks and run them /// concurrently with other processes (e.g., build and apply). - pub async fn eval_selected(&self, nodes: &[NodeName], job: Option) -> NixResult> { + pub async fn eval_selected(&self, nodes: &[NodeName], job: Option) -> ColmenaResult> { let nodes_expr = SerializedNixExpresssion::new(nodes)?; let expr = format!("hive.evalSelected {}", nodes_expr.expression()); @@ -267,7 +267,7 @@ impl Hive { } /// Evaluates an expression using values from the configuration - pub async fn introspect(&self, expression: String, instantiate: bool) -> NixResult { + pub async fn introspect(&self, expression: String, instantiate: bool) -> ColmenaResult { if instantiate { let expression = format!("hive.introspect ({})", expression); self.nix_instantiate(&expression).instantiate_with_builders().await? @@ -280,7 +280,7 @@ impl Hive { } /// Retrieve machinesFile setting for the hive. - async fn machines_file(&self) -> NixResult> { + async fn machines_file(&self) -> ColmenaResult> { if let Some(builders_opt) = &*self.builders.read().await { return Ok(builders_opt.clone()); } @@ -296,7 +296,7 @@ impl Hive { } /// Returns Nix arguments to set builders. - async fn builder_args(&self) -> NixResult> { + async fn builder_args(&self) -> ColmenaResult> { let mut options = Vec::new(); if let Some(machines_file) = self.machines_file().await? { @@ -381,7 +381,7 @@ impl<'hive> NixInstantiate<'hive> { command } - async fn instantiate_with_builders(self) -> NixResult { + async fn instantiate_with_builders(self) -> ColmenaResult { let hive = self.hive; let mut command = self.instantiate(); @@ -391,7 +391,7 @@ impl<'hive> NixInstantiate<'hive> { Ok(command) } - async fn eval_with_builders(self) -> NixResult { + async fn eval_with_builders(self) -> ColmenaResult { let hive = self.hive; let mut command = self.eval(); @@ -413,7 +413,7 @@ struct SerializedNixExpresssion { } impl SerializedNixExpresssion { - pub fn new(data: T) -> NixResult where T: Serialize { + pub fn new(data: T) -> ColmenaResult where T: Serialize { let mut tmp = NamedTempFile::new()?; let json = serde_json::to_vec(&data).expect("Could not serialize data"); tmp.write_all(&json)?; diff --git a/src/nix/host/key_uploader.rs b/src/nix/host/key_uploader.rs index 28539eb..216e873 100644 --- a/src/nix/host/key_uploader.rs +++ b/src/nix/host/key_uploader.rs @@ -12,8 +12,9 @@ use shell_escape::unix::escape; use tokio::io::{AsyncWriteExt, BufReader}; use tokio::process::Child; +use crate::error::ColmenaResult; use crate::job::JobHandle; -use crate::nix::{Key, NixResult}; +use crate::nix::Key; use crate::util::capture_stream; const SCRIPT_TEMPLATE: &str = include_str!("./key_uploader.template.sh"); @@ -30,7 +31,7 @@ pub fn generate_script<'a>(key: &'a Key, destination: &'a Path, require_ownershi escape(key_script.into()) } -pub async fn feed_uploader(mut uploader: Child, key: &Key, job: Option) -> NixResult<()> { +pub async fn feed_uploader(mut uploader: Child, key: &Key, job: Option) -> ColmenaResult<()> { let mut reader = key.reader().await?; let mut stdin = uploader.stdin.take().unwrap(); diff --git a/src/nix/host/local.rs b/src/nix/host/local.rs index b4a8dfe..9ba73df 100644 --- a/src/nix/host/local.rs +++ b/src/nix/host/local.rs @@ -5,10 +5,11 @@ use std::process::Stdio; use async_trait::async_trait; use tokio::process::Command; -use super::{CopyDirection, CopyOptions, Host, key_uploader}; -use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; +use crate::error::{ColmenaResult, ColmenaError}; +use crate::nix::{StorePath, Profile, Goal, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; use crate::util::{CommandExecution, CommandExt}; use crate::job::JobHandle; +use super::{CopyDirection, CopyOptions, Host, key_uploader}; /// The local machine running Colmena. /// @@ -31,11 +32,11 @@ impl Local { #[async_trait] impl Host for Local { - async fn copy_closure(&mut self, _closure: &StorePath, _direction: CopyDirection, _options: CopyOptions) -> NixResult<()> { + async fn copy_closure(&mut self, _closure: &StorePath, _direction: CopyDirection, _options: CopyOptions) -> ColmenaResult<()> { Ok(()) } - async fn realize_remote(&mut self, derivation: &StorePath) -> NixResult> { + async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult> { let mut command = Command::new("nix-store"); command.args(self.nix_options.clone()); @@ -55,7 +56,7 @@ impl Host for Local { .map(|p| p.to_string().try_into()).collect() } - async fn upload_keys(&mut self, keys: &HashMap, require_ownership: bool) -> NixResult<()> { + async fn upload_keys(&mut self, keys: &HashMap, require_ownership: bool) -> ColmenaResult<()> { for (name, key) in keys { self.upload_key(name, key, require_ownership).await?; } @@ -63,9 +64,9 @@ impl Host for Local { Ok(()) } - async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> { + async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> { if !goal.requires_activation() { - return Err(NixError::Unsupported); + return Err(ColmenaError::Unsupported); } if goal.should_switch_profile() { @@ -91,14 +92,14 @@ impl Host for Local { result } - async fn get_main_system_profile(&mut self) -> NixResult { + async fn get_main_system_profile(&mut self) -> ColmenaResult { let paths = Command::new("sh") .args(&["-c", &format!("readlink -e {} || readlink -e {}", SYSTEM_PROFILE, CURRENT_PROFILE)]) .capture_output() .await?; let path = paths.lines().into_iter().next() - .ok_or(NixError::FailedToGetCurrentProfile)? + .ok_or(ColmenaError::FailedToGetCurrentProfile)? .to_string() .try_into()?; @@ -112,7 +113,7 @@ impl Host for Local { impl Local { /// "Uploads" a single key. - async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> NixResult<()> { + async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> ColmenaResult<()> { if let Some(job) = &self.job { job.message(format!("Deploying key {}", name))?; } diff --git a/src/nix/host/mod.rs b/src/nix/host/mod.rs index eb849fd..3695dfe 100644 --- a/src/nix/host/mod.rs +++ b/src/nix/host/mod.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use async_trait::async_trait; -use super::{StorePath, Profile, Goal, NixResult, NixError, Key}; +use super::{StorePath, Profile, Goal, ColmenaResult, ColmenaError, Key}; use crate::job::JobHandle; mod ssh; @@ -65,20 +65,20 @@ pub trait Host: Send + Sync + std::fmt::Debug { /// Sends or receives the specified closure to the host /// /// The StorePath and its dependent paths will then exist on this host. - async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> NixResult<()>; + async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> ColmenaResult<()>; /// Realizes the specified derivation on the host /// /// The derivation must already exist on the host. /// After realization, paths in the Vec will then /// exist on the host. - async fn realize_remote(&mut self, derivation: &StorePath) -> NixResult>; + async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult>; /// Provides a JobHandle to use during operations. fn set_job(&mut self, bar: Option); /// Realizes the specified local derivation on the host then retrieves the outputs. - async fn realize(&mut self, derivation: &StorePath) -> NixResult> { + async fn realize(&mut self, derivation: &StorePath) -> ColmenaResult> { let options = CopyOptions::default() .include_outputs(true); @@ -90,7 +90,7 @@ pub trait Host: Send + Sync + std::fmt::Debug { } /// Pushes and optionally activates a profile to the host. - async fn deploy(&mut self, profile: &Profile, goal: Goal, copy_options: CopyOptions) -> NixResult<()> { + async fn deploy(&mut self, profile: &Profile, goal: Goal, copy_options: CopyOptions) -> ColmenaResult<()> { self.copy_closure(profile.as_store_path(), CopyDirection::ToRemote, copy_options).await?; if goal.requires_activation() { @@ -106,8 +106,8 @@ pub trait Host: Send + Sync + std::fmt::Debug { /// will not be applied if the specified user/group does not /// exist. #[allow(unused_variables)] - async fn upload_keys(&mut self, keys: &HashMap, require_ownership: bool) -> NixResult<()> { - Err(NixError::Unsupported) + async fn upload_keys(&mut self, keys: &HashMap, require_ownership: bool) -> ColmenaResult<()> { + Err(ColmenaError::Unsupported) } /// Returns the main system profile on the host. @@ -115,19 +115,19 @@ pub trait Host: Send + Sync + std::fmt::Debug { /// This may _not_ be the system profile that's currently activated! /// It will first try `/nix/var/nix/profiles/system`, falling back /// to `/run/current-system` if it doesn't exist. - async fn get_main_system_profile(&mut self) -> NixResult; + async fn get_main_system_profile(&mut self) -> ColmenaResult; /// Activates a system profile on the host, if it runs NixOS. /// /// The profile must already exist on the host. You should probably use deploy instead. #[allow(unused_variables)] - async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> { - Err(NixError::Unsupported) + async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> { + Err(ColmenaError::Unsupported) } /// Runs an arbitrary command on the host. #[allow(unused_variables)] - async fn run_command(&mut self, command: &[&str]) -> NixResult<()> { - Err(NixError::Unsupported) + async fn run_command(&mut self, command: &[&str]) -> ColmenaResult<()> { + Err(ColmenaError::Unsupported) } } diff --git a/src/nix/host/ssh.rs b/src/nix/host/ssh.rs index 0e9d921..006a694 100644 --- a/src/nix/host/ssh.rs +++ b/src/nix/host/ssh.rs @@ -6,10 +6,11 @@ use std::process::Stdio; use async_trait::async_trait; use tokio::process::Command; -use super::{CopyDirection, CopyOptions, Host, key_uploader}; -use crate::nix::{StorePath, Profile, Goal, NixResult, NixError, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; +use crate::error::{ColmenaResult, ColmenaError}; +use crate::nix::{StorePath, Profile, Goal, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; use crate::util::{CommandExecution, CommandExt}; use crate::job::JobHandle; +use super::{CopyDirection, CopyOptions, Host, key_uploader}; /// A remote machine connected over SSH. #[derive(Debug)] @@ -35,11 +36,11 @@ pub struct Ssh { #[async_trait] impl Host for Ssh { - async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> NixResult<()> { + async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> ColmenaResult<()> { let command = self.nix_copy_closure(closure, direction, options); self.run_command(command).await } - async fn realize_remote(&mut self, derivation: &StorePath) -> NixResult> { + async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult> { let command = self.ssh(&["nix-store", "--no-gc-warning", "--realise", derivation.as_path().to_str().unwrap()]); let mut execution = CommandExecution::new(command); @@ -51,16 +52,16 @@ impl Host for Ssh { paths.lines().map(|p| p.to_string().try_into()).collect() } - async fn upload_keys(&mut self, keys: &HashMap, require_ownership: bool) -> NixResult<()> { + async fn upload_keys(&mut self, keys: &HashMap, require_ownership: bool) -> ColmenaResult<()> { for (name, key) in keys { self.upload_key(name, key, require_ownership).await?; } Ok(()) } - async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> { + async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> { if !goal.requires_activation() { - return Err(NixError::Unsupported); + return Err(ColmenaError::Unsupported); } if goal.should_switch_profile() { @@ -74,11 +75,11 @@ impl Host for Ssh { let command = self.ssh(&v); self.run_command(command).await } - async fn run_command(&mut self, command: &[&str]) -> NixResult<()> { + async fn run_command(&mut self, command: &[&str]) -> ColmenaResult<()> { let command = self.ssh(command); self.run_command(command).await } - async fn get_main_system_profile(&mut self) -> NixResult { + async fn get_main_system_profile(&mut self) -> ColmenaResult { let command = format!("\"readlink -e {} || readlink -e {}\"", SYSTEM_PROFILE, CURRENT_PROFILE); let paths = self.ssh(&["sh", "-c", &command]) @@ -86,7 +87,7 @@ impl Host for Ssh { .await?; let path = paths.lines().into_iter().next() - .ok_or(NixError::FailedToGetCurrentProfile)? + .ok_or(ColmenaError::FailedToGetCurrentProfile)? .to_string() .try_into()?; @@ -150,7 +151,7 @@ impl Ssh { cmd } - async fn run_command(&mut self, command: Command) -> NixResult<()> { + async fn run_command(&mut self, command: Command) -> ColmenaResult<()> { let mut execution = CommandExecution::new(command); execution.set_job(self.job.clone()); @@ -216,7 +217,7 @@ impl Ssh { } /// Uploads a single key. - async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> NixResult<()> { + async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> ColmenaResult<()> { if let Some(job) = &self.job { job.message(format!("Uploading key {}", name))?; } diff --git a/src/nix/info.rs b/src/nix/info.rs index 271d2a2..24e28e8 100644 --- a/src/nix/info.rs +++ b/src/nix/info.rs @@ -5,7 +5,7 @@ use log::Level; use regex::Regex; use tokio::process::Command; -use super::{NixError, NixResult}; +use super::{ColmenaError, ColmenaResult}; struct NixVersion { major: usize, @@ -89,12 +89,12 @@ impl NixCheck { } } - pub async fn require_flake_support() -> NixResult<()> { + pub async fn require_flake_support() -> ColmenaResult<()> { let check = Self::detect().await; if !check.flakes_supported() { check.print_flakes_info(true); - Err(NixError::NoFlakesSupport) + Err(ColmenaError::NoFlakesSupport) } else { Ok(()) } diff --git a/src/nix/mod.rs b/src/nix/mod.rs index a732281..515979d 100644 --- a/src/nix/mod.rs +++ b/src/nix/mod.rs @@ -1,15 +1,14 @@ use std::collections::HashMap; use std::hash::Hash; use std::ops::Deref; -use std::os::unix::process::ExitStatusExt; use std::path::Path; -use std::process::ExitStatus; use serde::de; use serde::{Deserialize, Deserializer, Serialize}; -use snafu::Snafu; use users::get_current_username; -use validator::{Validate, ValidationErrors, ValidationError as ValidationErrorType}; +use validator::{Validate, ValidationError as ValidationErrorType}; + +use crate::error::{ColmenaResult, ColmenaError}; pub mod host; pub use host::{Host, CopyDirection, CopyOptions}; @@ -45,100 +44,6 @@ pub const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system"; /// Path to the system profile that's currently active. pub const CURRENT_PROFILE: &str = "/run/current-system"; -pub type NixResult = Result; - -#[non_exhaustive] -#[derive(Debug, Snafu)] -pub enum NixError { - #[snafu(display("I/O Error: {}", error))] - IoError { error: std::io::Error }, - - #[snafu(display("Nix returned invalid response: {}", output))] - BadOutput { output: String }, - - #[snafu(display("Nix exited with error code: {}", exit_code))] - NixFailure { exit_code: i32 }, - - #[snafu(display("Nix was killed by signal {}", signal))] - NixKilled { signal: i32 }, - - #[snafu(display("This operation is not supported"))] - Unsupported, - - #[snafu(display("Invalid Nix store path"))] - InvalidStorePath, - - #[snafu(display("Validation error"))] - ValidationError { errors: ValidationErrors }, - - #[snafu(display("Failed to upload keys: {}", error))] - KeyError { error: key::KeyError }, - - #[snafu(display("Store path {:?} is not a derivation", store_path))] - NotADerivation { store_path: StorePath }, - - #[snafu(display("Invalid NixOS system profile"))] - InvalidProfile, - - #[snafu(display("Unknown active profile: {:?}", store_path))] - ActiveProfileUnknown { store_path: StorePath }, - - #[snafu(display("Could not determine current profile"))] - FailedToGetCurrentProfile, - - #[snafu(display("Current Nix version does not support Flakes"))] - NoFlakesSupport, - - #[snafu(display("Don't know how to connect to the node"))] - NoTargetHost, - - #[snafu(display("Node name cannot be empty"))] - EmptyNodeName, - - #[snafu(display("Filter rule cannot be empty"))] - EmptyFilterRule, - - #[snafu(display("Deployment already executed"))] - DeploymentAlreadyExecuted, - - #[snafu(display("Unknown error: {}", message))] - Unknown { message: String }, -} - -impl From for NixError { - fn from(error: std::io::Error) -> Self { - Self::IoError { error } - } -} - -impl From for NixError { - fn from(error: key::KeyError) -> Self { - Self::KeyError { error } - } -} - -impl From for NixError { - fn from(errors: ValidationErrors) -> Self { - Self::ValidationError { errors } - } -} - -impl From for NixError { - fn from(status: ExitStatus) -> Self { - match status.code() { - Some(exit_code) => Self::NixFailure { exit_code }, - None => Self::NixKilled { signal: status.signal().unwrap() }, - } - } -} - -impl NixError { - pub fn unknown(error: Box) -> Self { - let message = error.to_string(); - Self::Unknown { message } - } -} - /// A node's attribute name. #[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)] #[serde(transparent)] @@ -183,7 +88,7 @@ impl NodeName { } /// Creates a NodeName from a String. - pub fn new(name: String) -> NixResult { + pub fn new(name: String) -> ColmenaResult { let validated = Self::validate(name)?; Ok(Self(validated)) } @@ -199,10 +104,10 @@ impl NodeName { }) } - fn validate(s: String) -> NixResult { + fn validate(s: String) -> ColmenaResult { // FIXME: Elaborate if s.is_empty() { - return Err(NixError::EmptyNodeName); + return Err(ColmenaError::EmptyNodeName); } Ok(s) diff --git a/src/nix/node_filter.rs b/src/nix/node_filter.rs index 2a55062..71c3a18 100644 --- a/src/nix/node_filter.rs +++ b/src/nix/node_filter.rs @@ -6,7 +6,7 @@ use std::iter::{Iterator, FromIterator}; use glob::Pattern as GlobPattern; -use super::{NixError, NixResult, NodeName, NodeConfig}; +use super::{ColmenaError, ColmenaResult, NodeName, NodeConfig}; /// A node filter containing a list of rules. pub struct NodeFilter { @@ -27,7 +27,7 @@ enum Rule { impl NodeFilter { /// Creates a new filter using an expression passed using `--on`. - pub fn new>(filter: S) -> NixResult { + pub fn new>(filter: S) -> ColmenaResult { let filter = filter.as_ref(); let trimmed = filter.trim(); @@ -43,7 +43,7 @@ impl NodeFilter { let pattern = pattern.trim(); if pattern.is_empty() { - return Err(NixError::EmptyFilterRule); + return Err(ColmenaError::EmptyFilterRule); } if let Some(tag_pattern) = pattern.strip_prefix('@') { @@ -51,7 +51,7 @@ impl NodeFilter { } else { Ok(Rule::MatchName(GlobPattern::new(pattern).unwrap())) } - }).collect::>>(); + }).collect::>>(); let rules = Result::from_iter(rules)?; @@ -100,8 +100,8 @@ impl NodeFilter { } /// Runs the filter against a set of node names and returns the matched ones. - pub fn filter_node_names(&self, nodes: &[NodeName]) -> NixResult> { - nodes.iter().filter_map(|name| -> Option> { + pub fn filter_node_names(&self, nodes: &[NodeName]) -> ColmenaResult> { + nodes.iter().filter_map(|name| -> Option> { for rule in self.rules.iter() { match rule { Rule::MatchName(pat) => { @@ -110,7 +110,7 @@ impl NodeFilter { } } _ => { - return Some(Err(NixError::Unknown { + return Some(Err(ColmenaError::Unknown { message: format!("Not enough information to run rule {:?} - We only have node names", rule), })); } diff --git a/src/nix/profile.rs b/src/nix/profile.rs index bd186ec..f0ed0d4 100644 --- a/src/nix/profile.rs +++ b/src/nix/profile.rs @@ -6,8 +6,8 @@ use tokio::process::Command; use super::{ Goal, - NixResult, - NixError, + ColmenaResult, + ColmenaError, StorePath, StoreDerivation, BuildResult, @@ -20,16 +20,16 @@ pub type ProfileDerivation = StoreDerivation; pub struct Profile(StorePath); impl Profile { - pub fn from_store_path(path: StorePath) -> NixResult { + pub fn from_store_path(path: StorePath) -> ColmenaResult { if !path.is_dir() || !path.join("bin/switch-to-configuration").exists() { - return Err(NixError::InvalidProfile); + return Err(ColmenaError::InvalidProfile); } if path.to_str().is_none() { - Err(NixError::InvalidProfile) + Err(ColmenaError::InvalidProfile) } else { Ok(Self(path)) } @@ -63,7 +63,7 @@ impl Profile { } /// Create a GC root for this profile. - pub async fn create_gc_root(&self, path: &Path) -> NixResult<()> { + pub async fn create_gc_root(&self, path: &Path) -> ColmenaResult<()> { let mut command = Command::new("nix-store"); command.args(&["--no-build-output", "--indirect", "--add-root", path.to_str().unwrap()]); command.args(&["--realise", self.as_path().to_str().unwrap()]); @@ -83,19 +83,19 @@ impl Profile { } impl TryFrom> for Profile { - type Error = NixError; + type Error = ColmenaError; - fn try_from(result: BuildResult) -> NixResult { + fn try_from(result: BuildResult) -> ColmenaResult { let paths = result.paths(); if paths.is_empty() { - return Err(NixError::BadOutput { + return Err(ColmenaError::BadOutput { output: String::from("There is no store path"), }); } if paths.len() > 1 { - return Err(NixError::BadOutput { + return Err(ColmenaError::BadOutput { output: String::from("Build resulted in more than 1 store path"), }); } diff --git a/src/nix/store.rs b/src/nix/store.rs index 280efd2..4dcfaf9 100644 --- a/src/nix/store.rs +++ b/src/nix/store.rs @@ -7,8 +7,9 @@ use std::fmt; use serde::{Serialize, Deserialize}; use tokio::process::Command; +use crate::error::{ColmenaError, ColmenaResult}; use crate::util::CommandExt; -use super::{Host, NixResult, NixError}; +use super::Host; /// A Nix store path. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -43,7 +44,7 @@ impl StorePath { } /// Returns the immediate dependencies of the store path. - pub async fn references(&self) -> NixResult> { + pub async fn references(&self) -> ColmenaResult> { let references = Command::new("nix-store") .args(&["--query", "--references"]) .arg(&self.0) @@ -55,11 +56,11 @@ impl StorePath { } /// Converts the store path into a store derivation. - pub fn into_derivation>>(self) -> NixResult> { + pub fn into_derivation>>(self) -> ColmenaResult> { if self.is_derivation() { Ok(StoreDerivation::::from_store_path_unchecked(self)) } else { - Err(NixError::NotADerivation { store_path: self }) + Err(ColmenaError::NotADerivation { store_path: self }) } } } @@ -73,13 +74,13 @@ impl Deref for StorePath { } impl TryFrom for StorePath { - type Error = NixError; + type Error = ColmenaError; - fn try_from(s: String) -> NixResult { + fn try_from(s: String) -> ColmenaResult { if s.starts_with("/nix/store/") { Ok(Self(s.into())) } else { - Err(NixError::InvalidStorePath) + Err(ColmenaError::InvalidStorePath) } } } @@ -113,9 +114,9 @@ impl>> StoreDerivation { } } -impl, Error=NixError>> StoreDerivation { +impl, Error=ColmenaError>> StoreDerivation { /// Builds the store derivation on a host, resulting in a T. - pub async fn realize(&self, host: &mut Box) -> NixResult { + pub async fn realize(&self, host: &mut Box) -> ColmenaResult { let paths: Vec = host.realize(&self.path).await?; let result = BuildResult { @@ -126,7 +127,7 @@ impl, Error=NixError>> StoreDerivation { } /// Builds the store derivation on a host without copying the results back. - pub async fn realize_remote(&self, host: &mut Box) -> NixResult { + pub async fn realize_remote(&self, host: &mut Box) -> ColmenaResult { let paths: Vec = host.realize_remote(&self.path).await?; let result = BuildResult { @@ -143,7 +144,7 @@ impl>> fmt::Display for StoreDerivation { } } -impl, Error=NixError>> BuildResult { +impl, Error=ColmenaError>> BuildResult { pub fn paths(&self) -> &[StorePath] { self.results.as_slice() } diff --git a/src/progress/mod.rs b/src/progress/mod.rs index e6da509..bbf9768 100644 --- a/src/progress/mod.rs +++ b/src/progress/mod.rs @@ -13,8 +13,8 @@ use tokio::sync::mpsc::{self, UnboundedSender as TokioSender, }; +use crate::error::ColmenaResult; use crate::job::JobId; -use crate::nix::NixResult; pub use plain::PlainOutput; pub use spinner::SpinnerOutput; @@ -33,7 +33,7 @@ pub enum SimpleProgressOutput { #[async_trait] pub trait ProgressOutput : Sized { /// Runs until a Message::Complete is received. - async fn run_until_completion(self) -> NixResult; + async fn run_until_completion(self) -> ColmenaResult; /// Returns a sender. /// @@ -109,7 +109,7 @@ impl SimpleProgressOutput { } } - pub async fn run_until_completion(self) -> NixResult { + pub async fn run_until_completion(self) -> ColmenaResult { match self { Self::Plain(o) => { o.run_until_completion().await diff --git a/src/progress/plain.rs b/src/progress/plain.rs index 2f440c2..1a9b321 100644 --- a/src/progress/plain.rs +++ b/src/progress/plain.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use console::Style as ConsoleStyle; -use crate::nix::NixResult; +use crate::error::ColmenaResult; use super::{ DEFAULT_LABEL_WIDTH, ProgressOutput, @@ -81,7 +81,7 @@ impl PlainOutput { #[async_trait] impl ProgressOutput for PlainOutput { - async fn run_until_completion(mut self) -> NixResult { + async fn run_until_completion(mut self) -> ColmenaResult { loop { let message = self.receiver.recv().await; diff --git a/src/progress/spinner.rs b/src/progress/spinner.rs index 9ee13e1..98e36f7 100644 --- a/src/progress/spinner.rs +++ b/src/progress/spinner.rs @@ -6,8 +6,8 @@ use std::time::Instant; use async_trait::async_trait; use indicatif::{MultiProgress, ProgressStyle, ProgressBar}; +use crate::error::ColmenaResult; use crate::job::JobId; -use crate::nix::NixResult; use super::{ DEFAULT_LABEL_WIDTH, ProgressOutput, @@ -171,7 +171,7 @@ impl SpinnerOutput { #[async_trait] impl ProgressOutput for SpinnerOutput { - async fn run_until_completion(mut self) -> NixResult { + async fn run_until_completion(mut self) -> ColmenaResult { let meta_bar = self.multi.add(self.meta_bar.clone()); meta_bar.enable_steady_tick(100); diff --git a/src/troubleshooter.rs b/src/troubleshooter.rs index 2bc92c2..80d7b5b 100644 --- a/src/troubleshooter.rs +++ b/src/troubleshooter.rs @@ -7,12 +7,12 @@ use std::future::Future; use clap::ArgMatches; -use crate::nix::NixError; +use crate::error::ColmenaError; /// 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, local_args: &'a ArgMatches, f: U) -> T where U: FnOnce(&'a ArgMatches, &'a ArgMatches) -> F, - F: Future>, + F: Future>, { match f(global_args, local_args).await { Ok(r) => r, @@ -30,8 +30,8 @@ pub async fn run_wrapped<'a, F, U, T>(global_args: &'a ArgMatches, local_args: & } } -fn troubleshoot(global_args: &ArgMatches, _local_args: &ArgMatches, error: &NixError) -> Result<(), NixError> { - if let NixError::NoFlakesSupport = error { +fn troubleshoot(global_args: &ArgMatches, _local_args: &ArgMatches, error: &ColmenaError) -> Result<(), ColmenaError> { + if let ColmenaError::NoFlakesSupport = error { // 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. diff --git a/src/util.rs b/src/util.rs index 2edbd03..d74bcf7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,7 +9,8 @@ use serde::de::DeserializeOwned; use tokio::io::{AsyncRead, AsyncBufReadExt, BufReader}; use tokio::process::Command; -use super::nix::{Flake, Hive, HivePath, NixResult, NixError, StorePath}; +use super::error::{ColmenaResult, ColmenaError}; +use super::nix::{Flake, Hive, HivePath, StorePath}; use super::nix::deployment::TargetNodeMap; use super::job::JobHandle; @@ -26,16 +27,16 @@ pub struct CommandExecution { #[async_trait] pub trait CommandExt { /// Runs the command with stdout and stderr passed through to the user. - async fn passthrough(&mut self) -> NixResult<()>; + async fn passthrough(&mut self) -> ColmenaResult<()>; /// Runs the command, capturing the output as a String. - async fn capture_output(&mut self) -> NixResult; + async fn capture_output(&mut self) -> ColmenaResult; /// Runs the command, capturing deserialized output from JSON. - async fn capture_json(&mut self) -> NixResult where T: DeserializeOwned; + async fn capture_json(&mut self) -> ColmenaResult where T: DeserializeOwned; /// Runs the command, capturing a single store path. - async fn capture_store_path(&mut self) -> NixResult; + async fn capture_store_path(&mut self) -> ColmenaResult; } impl CommandExecution { @@ -65,7 +66,7 @@ impl CommandExecution { } /// Runs the command. - pub async fn run(&mut self) -> NixResult<()> { + pub async fn run(&mut self) -> ColmenaResult<()> { self.command.stdin(Stdio::null()); self.command.stdout(Stdio::piped()); self.command.stderr(Stdio::piped()); @@ -103,7 +104,7 @@ impl CommandExecution { #[async_trait] impl CommandExt for Command { /// Runs the command with stdout and stderr passed through to the user. - async fn passthrough(&mut self) -> NixResult<()> { + async fn passthrough(&mut self) -> ColmenaResult<()> { let exit = self .spawn()? .wait() @@ -117,7 +118,7 @@ impl CommandExt for Command { } /// Captures output as a String. - async fn capture_output(&mut self) -> NixResult { + async fn capture_output(&mut self) -> ColmenaResult { // We want the user to see the raw errors let output = self .stdout(Stdio::piped()) @@ -135,15 +136,15 @@ impl CommandExt for Command { } /// Captures deserialized output from JSON. - async fn capture_json(&mut self) -> NixResult where T: DeserializeOwned { + async fn capture_json(&mut self) -> ColmenaResult where T: DeserializeOwned { let output = self.capture_output().await?; - serde_json::from_str(&output).map_err(|_| NixError::BadOutput { + serde_json::from_str(&output).map_err(|_| ColmenaError::BadOutput { output: output.clone() }) } /// Captures a single store path. - async fn capture_store_path(&mut self) -> NixResult { + async fn capture_store_path(&mut self) -> ColmenaResult { let output = self.capture_output().await?; let path = output.trim_end().to_owned(); StorePath::try_from(path) @@ -152,12 +153,12 @@ impl CommandExt for Command { #[async_trait] impl CommandExt for CommandExecution { - async fn passthrough(&mut self) -> NixResult<()> { + async fn passthrough(&mut self) -> ColmenaResult<()> { self.run().await } /// Captures output as a String. - async fn capture_output(&mut self) -> NixResult { + async fn capture_output(&mut self) -> ColmenaResult { self.run().await?; let (stdout, _) = self.get_logs(); @@ -165,22 +166,22 @@ impl CommandExt for CommandExecution { } /// Captures deserialized output from JSON. - async fn capture_json(&mut self) -> NixResult where T: DeserializeOwned { + async fn capture_json(&mut self) -> ColmenaResult where T: DeserializeOwned { let output = self.capture_output().await?; - serde_json::from_str(&output).map_err(|_| NixError::BadOutput { + serde_json::from_str(&output).map_err(|_| ColmenaError::BadOutput { output: output.clone() }) } /// Captures a single store path. - async fn capture_store_path(&mut self) -> NixResult { + async fn capture_store_path(&mut self) -> ColmenaResult { let output = self.capture_output().await?; let path = output.trim_end().to_owned(); StorePath::try_from(path) } } -pub async fn hive_from_args(args: &ArgMatches) -> NixResult { +pub async fn hive_from_args(args: &ArgMatches) -> ColmenaResult { let path = match args.occurrences_of("config") { 0 => { // traverse upwards until we find hive.nix @@ -281,7 +282,7 @@ fn canonicalize_cli_path(path: &str) -> PathBuf { } } -pub async fn capture_stream(mut stream: BufReader, job: Option, stderr: bool) -> NixResult +pub async fn capture_stream(mut stream: BufReader, job: Option, stderr: bool) -> ColmenaResult where R: AsyncRead + Unpin { let mut log = String::new();