diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2021-09-06 19:17:55 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2021-09-06 19:52:44 +0200 |
commit | 2e357b78c76129f92c04c5888d2af35872f51c14 (patch) | |
tree | fb945782f09bc66e62340dfc9ac4995801d0222b | |
parent | b31e12a488f6395f161cfdc2153455f8175b83b4 (diff) | |
download | rebel-2e357b78c76129f92c04c5888d2af35872f51c14.tar rebel-2e357b78c76129f92c04c5888d2af35872f51c14.zip |
Introduce new contextualizable error type
The new error type is inspired by the anyhow library, but it can be
serialized/desirialized and is much simpler.
-rw-r--r-- | src/executor.rs | 10 | ||||
-rw-r--r-- | src/runner.rs | 6 | ||||
-rw-r--r-- | src/runner/runc.rs | 15 | ||||
-rw-r--r-- | src/runner/runc/init.rs | 43 | ||||
-rw-r--r-- | src/runner/runc/run.rs | 31 | ||||
-rw-r--r-- | src/util.rs | 1 | ||||
-rw-r--r-- | src/util/error.rs | 105 | ||||
-rw-r--r-- | src/util/ipc.rs | 14 |
8 files changed, 141 insertions, 84 deletions
diff --git a/src/executor.rs b/src/executor.rs index ceb9780..d0cc536 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::{ paths, runner, types::*, - util::{self, cjson}, + util::{self, cjson, error::*}, }; fn save_output(output: &TaskOutput) -> io::Result<()> { @@ -137,11 +137,7 @@ impl<'a> Executor<'a> { .collect() } - fn run_one( - &self, - task_ref: &TaskRef, - runner: &impl runner::Runner, - ) -> runner::Result<TaskOutput> { + fn run_one(&self, task_ref: &TaskRef, runner: &impl runner::Runner) -> Result<TaskOutput> { let task_def = self.tasks.get(&task_ref.id).expect("Invalid TaskRef"); let task_deps = self.task_deps_with_inputs(&task_def); @@ -208,7 +204,7 @@ impl<'a> Executor<'a> { } } - pub fn run(&mut self, runner: &impl runner::Runner) -> runner::Result<()> { + pub fn run(&mut self, runner: &impl runner::Runner) -> Result<()> { while let Some(task_ref) = self.tasks_runnable.pop() { let output = self.run_one(&task_ref, runner)?; self.tasks_done.insert(task_ref.clone(), output); diff --git a/src/runner.rs b/src/runner.rs index f36f65d..4710552 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,9 +1,9 @@ pub mod runc; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, io}; +use std::collections::HashMap; -use crate::types::*; +use crate::{types::*, util::error::*}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Task { @@ -15,8 +15,6 @@ pub struct Task { pub env: HashMap<String, String>, } -pub type Result<T> = io::Result<T>; - pub trait Runner { fn run(&self, task: &Task) -> Result<OutputHash>; } diff --git a/src/runner/runc.rs b/src/runner/runc.rs index 4c8fb10..756fc28 100644 --- a/src/runner/runc.rs +++ b/src/runner/runc.rs @@ -2,7 +2,7 @@ mod init; mod run; mod spec; -use std::{io, process}; +use std::process; use ipc_channel::ipc; use nix::{ @@ -12,10 +12,15 @@ use nix::{ }; use serde::{Deserialize, Serialize}; -use crate::{clone, runner, types::*, unshare, util::ipc::CheckDisconnect}; +use crate::{ + clone, runner, + types::*, + unshare, + util::{error::*, ipc::CheckDisconnect}, +}; #[derive(Debug, Deserialize, Serialize)] -struct Request(runner::Task, ipc::IpcSender<Result<OutputHash, run::Error>>); +struct Request(runner::Task, ipc::IpcSender<Result<OutputHash>>); fn runner(idmap_finished: ipc::IpcReceiver<()>, channel: ipc::IpcReceiver<Request>) -> ! { idmap_finished @@ -58,7 +63,7 @@ impl RuncRunner { /// Creates a new Runc runner /// /// Unsafe: Do not call in multithreaded processes - pub unsafe fn new() -> io::Result<Self> { + pub unsafe fn new() -> Result<Self> { init::runc_preinit()?; let (tx, rx) = ipc::channel().expect("IPC channel creation failed"); @@ -90,7 +95,7 @@ impl RuncRunner { } impl super::Runner for RuncRunner { - fn run(&self, task: &runner::Task) -> super::Result<OutputHash> { + fn run(&self, task: &runner::Task) -> Result<OutputHash> { let (reply_tx, reply_rx) = ipc::channel().expect("IPC channel creation failed"); self.channel diff --git a/src/runner/runc/init.rs b/src/runner/runc/init.rs index 32eac6b..9fb5e57 100644 --- a/src/runner/runc/init.rs +++ b/src/runner/runc/init.rs @@ -1,17 +1,18 @@ use std::{ ffi::CString, fs::{DirBuilder, File}, - io, os::unix::prelude::OsStringExt, }; use nix::mount::{self, MsFlags}; -use serde::{Deserialize, Serialize}; use walkdir::WalkDir; -use crate::{paths, unshare, util}; +use crate::{ + paths, unshare, + util::{self, error::*}, +}; -fn prepare_buildtmp() -> io::Result<()> { +fn prepare_buildtmp() -> Result<()> { mount::mount::<_, _, _, str>( Some("buildtmp"), paths::TMP_DIR, @@ -23,8 +24,8 @@ fn prepare_buildtmp() -> io::Result<()> { util::tar::unpack(File::open(paths::ROOTFS_ARCHIVE)?, paths::ROOTFS_DIR)?; for entry in WalkDir::new(paths::ROOTFS_DIR) { - let path = CString::new(entry?.into_path().into_os_string().into_vec()) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let path = + CString::new(entry?.into_path().into_os_string().into_vec()).map_err(Error::new)?; if unsafe { libc::lchown( path.as_ptr(), @@ -33,38 +34,14 @@ fn prepare_buildtmp() -> io::Result<()> { ) } < 0 { - return Err(io::Error::last_os_error()); + return Err(Error::last_os_error()); } } Ok(()) } -#[derive(Debug, Deserialize, Serialize)] -pub enum Error { - Code(i32), - String(String), -} - -impl From<io::Error> for Error { - fn from(error: io::Error) -> Self { - match error.raw_os_error() { - Some(code) => Error::Code(code), - None => Error::String(error.to_string()), - } - } -} - -impl From<Error> for io::Error { - fn from(error: Error) -> Self { - match error { - Error::Code(code) => io::Error::from_raw_os_error(code), - Error::String(string) => io::Error::new(io::ErrorKind::Other, string), - } - } -} - -pub fn runc_preinit() -> Result<(), Error> { +pub fn runc_preinit() -> Result<()> { let mut dir = DirBuilder::new(); dir.recursive(true); dir.create(paths::OUTPUT_STATE_DIR)?; @@ -72,7 +49,7 @@ pub fn runc_preinit() -> Result<(), Error> { Ok(()) } -pub fn runc_init() -> Result<(), Error> { +pub fn runc_init() -> Result<()> { prepare_buildtmp()?; Ok(()) } diff --git a/src/runner/runc/run.rs b/src/runner/runc/run.rs index 9fd88f7..c0d9aa1 100644 --- a/src/runner/runc/run.rs +++ b/src/runner/runc/run.rs @@ -4,42 +4,17 @@ use std::{ }; use nix::mount::{self, MsFlags}; -use serde::{Deserialize, Serialize}; use sha2::Digest; use tee_readwrite::TeeWriter; use crate::{ paths, runner, types::*, - util::{self, tar}, + util::{self, error::*, tar}, }; use super::spec; -#[derive(Debug, Deserialize, Serialize)] -pub enum Error { - Code(i32), - String(String), -} - -impl From<io::Error> for Error { - fn from(error: io::Error) -> Self { - match error.raw_os_error() { - Some(code) => Error::Code(code), - None => Error::String(error.to_string()), - } - } -} - -impl From<Error> for io::Error { - fn from(error: Error) -> Self { - match error { - Error::Code(code) => io::Error::from_raw_os_error(code), - Error::String(string) => io::Error::new(io::ErrorKind::Other, string), - } - } -} - fn init_task(task: &runner::Task) -> io::Result<()> { let mut dir = fs::DirBuilder::new(); dir.recursive(true); @@ -160,7 +135,7 @@ fn collect_output(task: runner::Task) -> io::Result<OutputHash> { Ok(hash) } -pub fn handle_task(task: runner::Task) -> Result<OutputHash, Error> { +pub fn handle_task(task: runner::Task) -> Result<OutputHash> { init_task(&task)?; unpack_dependencies(&task)?; @@ -180,7 +155,7 @@ pub fn handle_task(task: runner::Task) -> Result<OutputHash, Error> { .status()?; if !status.success() { - return Err(Error::String("Task failed".to_string())); + return Err(Error::new("Task failed")); } Ok(collect_output(task)?) diff --git a/src/util.rs b/src/util.rs index 50a3534..eaf2438 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ pub mod cjson; +pub mod error; pub mod ipc; pub mod tar; diff --git a/src/util/error.rs b/src/util/error.rs new file mode 100644 index 0000000..a60d39b --- /dev/null +++ b/src/util/error.rs @@ -0,0 +1,105 @@ +//! Serializable errors with context + +use std::{fmt::Display, io, result}; + +use serde::{Deserialize, Serialize}; + +pub trait Contextualizable: Sized { + type Output; + + fn with_context<C: Display, F: FnOnce() -> C>(self, f: F) -> Self::Output; + + fn context<C: Display>(self, c: C) -> Self::Output { + self.with_context(|| c) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ErrorCause { + Code(i32), + String(String), +} + +impl Display for ErrorCause { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorCause::Code(code) => io::Error::from_raw_os_error(*code).fmt(f), + ErrorCause::String(string) => f.write_str(string), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct Error { + pub cause: ErrorCause, + pub context: Vec<String>, +} + +impl Error { + pub fn new<D: Display>(cause: D) -> Self { + Error { + cause: ErrorCause::String(cause.to_string()), + context: Vec::new(), + } + } + + pub fn last_os_error() -> Self { + io::Error::last_os_error().into() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Error: ")?; + + let mut it = self.context.iter().rev(); + if let Some(ctx) = it.next() { + write!(f, "{}\n\nCaused by:\n ", ctx)?; + + for ctx in it { + write!(f, "{}\n ", ctx)?; + } + } + + self.cause.fmt(f) + } +} + +impl Contextualizable for Error { + type Output = Error; + fn with_context<C: Display, F: FnOnce() -> C>(self, f: F) -> Self::Output { + let Error { cause, mut context } = self; + context.push(f().to_string()); + Error { cause, context } + } +} + +impl<E> From<E> for Error +where + E: Into<io::Error>, +{ + fn from(err: E) -> Self { + let io_err = err.into(); + let cause = match io_err.raw_os_error() { + Some(code) => ErrorCause::Code(code), + None => ErrorCause::String(io_err.to_string()), + }; + Error { + cause, + context: Vec::new(), + } + } +} + +pub type Result<T> = result::Result<T, Error>; + +impl<T, E> Contextualizable for result::Result<T, E> +where + E: Into<Error>, +{ + type Output = Result<T>; + + fn with_context<C: Display, F: FnOnce() -> C>(self, f: F) -> Self::Output { + self.map_err(|err| err.into().with_context(f)) + } +} diff --git a/src/util/ipc.rs b/src/util/ipc.rs index 9fa6b9d..45fd823 100644 --- a/src/util/ipc.rs +++ b/src/util/ipc.rs @@ -1,23 +1,23 @@ -use std::{ - io::{self, Error, ErrorKind}, - result, -}; +use std::result; use ipc_channel::ipc; + +use crate::util::error::*; + pub trait CheckDisconnect { type Output; - fn check_disconnect(self) -> io::Result<Self::Output>; + fn check_disconnect(self) -> Result<Self::Output>; } impl<T> CheckDisconnect for result::Result<T, ipc::IpcError> { type Output = result::Result<(), T>; - fn check_disconnect(self) -> io::Result<Self::Output> { + fn check_disconnect(self) -> Result<Self::Output> { match self { Ok(v) => Ok(Err(v)), Err(ipc::IpcError::Disconnected) => Ok(Ok(())), - Err(error) => Err(Error::new(ErrorKind::Other, error)), + Err(error) => Err(Error::new(error)), } } } |