summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2021-09-06 19:17:55 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2021-09-06 19:52:44 +0200
commit2e357b78c76129f92c04c5888d2af35872f51c14 (patch)
treefb945782f09bc66e62340dfc9ac4995801d0222b
parentb31e12a488f6395f161cfdc2153455f8175b83b4 (diff)
downloadrebel-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.rs10
-rw-r--r--src/runner.rs6
-rw-r--r--src/runner/runc.rs15
-rw-r--r--src/runner/runc/init.rs43
-rw-r--r--src/runner/runc/run.rs31
-rw-r--r--src/util.rs1
-rw-r--r--src/util/error.rs105
-rw-r--r--src/util/ipc.rs14
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)),
}
}
}