Skip to content

Commit 5403e1d

Browse files
Merge pull request #22 from balena-io-experimental/error-handling
Refactor error handling
2 parents 9783ca4 + 287fec9 commit 5403e1d

19 files changed

Lines changed: 276 additions & 234 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ serde = { version = "1.0.197" }
1818
serde_json = "1.0.120"
1919
thiserror = "2"
2020
tokio = { version = "1.43.0", default-features = false, features = [
21+
"rt",
2122
"sync",
2223
"macros",
2324
] }

examples/composer/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ mod tests {
776776

777777
// Seeking the target must succeed
778778
let worker = worker.seek_target(target.clone()).await.unwrap();
779-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
779+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
780780

781781
// The alpine image must exist now
782782
let docker = Docker::connect_with_defaults().unwrap();
@@ -934,7 +934,7 @@ mod tests {
934934

935935
// Seeking the target must succeed
936936
let worker = worker.seek_target(target).await.unwrap();
937-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
937+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
938938

939939
// The alpine image must exist now
940940
let docker = Docker::connect_with_defaults().unwrap();
@@ -973,7 +973,7 @@ mod tests {
973973

974974
// Seeking the target must succeed
975975
let worker = worker.seek_target(target).await.unwrap();
976-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
976+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
977977

978978
// The container ids should match
979979
let container = docker
@@ -1000,7 +1000,7 @@ mod tests {
10001000

10011001
// Seeking the target must succeed
10021002
let worker = worker.seek_target(target).await.unwrap();
1003-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
1003+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
10041004

10051005
// The container ids should match
10061006
let container = docker
@@ -1021,7 +1021,7 @@ mod tests {
10211021

10221022
// Seeking the target must succeed
10231023
let worker = worker.seek_target(target).await.unwrap();
1024-
assert_eq!(worker.status(), &SeekStatus::TargetStateReached);
1024+
assert_eq!(worker.status(), &SeekStatus::TargetReached);
10251025

10261026
// The container should no longer exist
10271027
let container = docker

src/errors.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::ops::Deref;
2+
3+
use thiserror::Error;
4+
5+
#[derive(Debug, Error)]
6+
#[error("argument extraction failed: {0:?}")]
7+
/// Arguments to a task could not be extracted.
8+
/// This is likely to be an error with the task definition.
9+
pub struct ExtractionError(#[from] anyhow::Error);
10+
11+
#[derive(Debug, Error)]
12+
#[error("serialization error: {0:?}")]
13+
/// An error happened while serializing or deserializing an input type
14+
pub struct SerializationError(#[from] serde_json::Error);
15+
16+
#[derive(Debug, Error)]
17+
#[error("internal error, this may be a bug: {0:?}")]
18+
/// Some unexpected error happened during the worker operation, either
19+
/// planning or runtime. These errors should not happen, unless
20+
/// there is a bug in the implementation.
21+
pub struct InternalError(#[from] anyhow::Error);
22+
23+
#[derive(Debug, Error)]
24+
#[error("method expansion failed: {0:?}")]
25+
/// An error happened while trying to expand the method
26+
/// task into its sub-tasks. This is likely an issue with the
27+
/// method definition
28+
pub struct MethodError(Box<dyn std::error::Error + Send + Sync>);
29+
30+
impl MethodError {
31+
pub fn new<E: std::error::Error + Send + Sync + 'static>(err: E) -> Self {
32+
Self(Box::new(err))
33+
}
34+
}
35+
36+
#[derive(Debug, Error)]
37+
#[error(transparent)]
38+
/// An error happened while executing the task within the workflow.
39+
/// These errors only happen at runtime, never at the planning stage
40+
/// of the worker.
41+
pub struct IOError(Box<dyn std::error::Error + Send + Sync>);
42+
43+
impl IOError {
44+
pub fn new<E: std::error::Error + Send + Sync + 'static>(err: E) -> Self {
45+
Self(Box::new(err))
46+
}
47+
}
48+
49+
impl Deref for IOError {
50+
type Target = Box<dyn std::error::Error + Send + Sync>;
51+
52+
fn deref(&self) -> &Self::Target {
53+
&self.0
54+
}
55+
}

src/extract/args/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use anyhow::Context as AnyhowCtx;
22
use serde::de::DeserializeOwned;
33
use std::ops::Deref;
44

5+
use crate::errors::ExtractionError;
56
use crate::system::{FromSystem, System};
6-
use crate::task::{Context, FromContext, InputError};
7+
use crate::task::{Context, FromContext};
78

89
mod de;
910
mod error;
@@ -12,7 +13,7 @@ mod error;
1213
pub struct Args<T>(pub T);
1314

1415
impl<T: DeserializeOwned + Send> FromContext for Args<T> {
15-
type Error = InputError;
16+
type Error = ExtractionError;
1617

1718
fn from_context(context: &Context) -> Result<Self, Self::Error> {
1819
let args = &context.args;
@@ -28,7 +29,7 @@ impl<T: DeserializeOwned + Send> FromContext for Args<T> {
2829
}
2930

3031
impl<T: DeserializeOwned + Send> FromSystem for Args<T> {
31-
type Error = InputError;
32+
type Error = ExtractionError;
3233

3334
fn from_system(_: &System, context: &Context) -> Result<Self, Self::Error> {
3435
Self::from_context(context)

src/extract/path.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use std::fmt::{Display, Formatter};
22

3+
use crate::errors::ExtractionError;
34
use crate::system::{FromSystem, System};
4-
use crate::task::{Context, FromContext, InputError};
5+
use crate::task::{Context, FromContext};
56

67
pub struct Path(pub String);
78

89
impl FromContext for Path {
9-
type Error = InputError;
10+
type Error = ExtractionError;
1011

1112
fn from_context(context: &Context) -> Result<Self, Self::Error> {
1213
let path = context.path.to_str();
@@ -22,7 +23,7 @@ impl Display for Path {
2223
}
2324

2425
impl FromSystem for Path {
25-
type Error = InputError;
26+
type Error = ExtractionError;
2627

2728
fn from_system(_: &System, context: &Context) -> Result<Self, Self::Error> {
2829
Self::from_context(context)

src/extract/res.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use anyhow::Context as AnyhowCxt;
22
use std::ops::Deref;
33
use std::sync::Arc;
44

5+
use crate::errors::ExtractionError;
56
use crate::system::{FromSystem, System};
6-
use crate::task::{Context, InputError};
7+
use crate::task::Context;
78

89
pub struct Res<R>(Arc<R>);
910

1011
impl<R: Send + Sync + 'static> FromSystem for Res<R> {
11-
type Error = InputError;
12+
type Error = ExtractionError;
1213

1314
fn from_system(system: &System, _: &Context) -> Result<Self, Self::Error> {
1415
let arc = system
@@ -19,7 +20,7 @@ impl<R: Send + Sync + 'static> FromSystem for Res<R> {
1920
std::any::type_name::<R>()
2021
)
2122
})
22-
.map_err(InputError::from)?;
23+
.map_err(ExtractionError::from)?;
2324
Ok(Res(arc))
2425
}
2526
}

src/extract/system.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use anyhow::Context as AnyhowCtx;
22
use serde::de::DeserializeOwned;
33

4+
use crate::errors::ExtractionError;
45
use crate::system::{FromSystem, System as SystemState};
5-
use crate::task::{Context, InputError};
6+
use crate::task::Context;
67

78
/// Extracts the root of the system state
89
#[derive(Debug, Clone)]
910
pub struct System<S>(pub S);
1011

1112
impl<S: DeserializeOwned> FromSystem for System<S> {
12-
type Error = InputError;
13+
type Error = ExtractionError;
1314

1415
fn from_system(system: &SystemState, _: &Context) -> Result<Self, Self::Error> {
1516
// This will fail if the value cannot be deserialized into the target type

src/extract/target.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use anyhow::Context as AnyhowCxt;
22
use serde::de::DeserializeOwned;
33
use std::ops::Deref;
44

5+
use crate::errors::ExtractionError;
56
use crate::system::{FromSystem, System};
6-
use crate::task::{Context, FromContext, InputError};
7+
use crate::task::{Context, FromContext};
78

89
pub struct Target<T>(pub T);
910

1011
impl<T: DeserializeOwned> FromContext for Target<T> {
11-
type Error = InputError;
12+
type Error = ExtractionError;
1213

1314
fn from_context(context: &Context) -> Result<Self, Self::Error> {
1415
let value = &context.target;
@@ -26,7 +27,7 @@ impl<T: DeserializeOwned> FromContext for Target<T> {
2627
}
2728

2829
impl<T: DeserializeOwned> FromSystem for Target<T> {
29-
type Error = InputError;
30+
type Error = ExtractionError;
3031

3132
fn from_system(_: &System, context: &Context) -> Result<Self, Self::Error> {
3233
Self::from_context(context)

src/extract/view.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ use serde::Serialize;
1010
use serde_json::Value;
1111
use std::ops::{Deref, DerefMut};
1212

13+
use crate::errors::ExtractionError;
1314
use crate::path::Path;
1415
use crate::system::{FromSystem, System};
15-
use crate::task::{Context, Effect, Error, InputError, IntoResult, UnexpectedError};
16+
use crate::task::{Context, Effect, Error, IntoResult};
1617

1718
/// Extracts a pointer to a sub-element of the global state indicated
1819
/// by the path.
@@ -75,7 +76,7 @@ impl<T> Pointer<T> {
7576
}
7677

7778
impl<T: DeserializeOwned> FromSystem for Pointer<T> {
78-
type Error = InputError;
79+
type Error = ExtractionError;
7980

8081
fn from_system(system: &System, context: &Context) -> Result<Self, Self::Error> {
8182
let json_ptr = context.path.as_ref();
@@ -180,9 +181,10 @@ impl<T: Serialize> IntoResult<Patch> for Pointer<T> {
180181
fn into_result(self) -> Result<Patch, Error> {
181182
let before = self.initial;
182183
let after = if let Some(state) = self.state {
183-
serde_json::to_value(state)
184-
.with_context(|| "Failed to serialize pointer value")
185-
.map_err(UnexpectedError::from)?
184+
// This should not happen unless there is a bug (hopefully).
185+
// if this happens during worker operation, it will be catched
186+
// as a panic in the task
187+
serde_json::to_value(state).expect("failed to serialize pointer value")
186188
} else {
187189
Value::Null
188190
};
@@ -210,14 +212,6 @@ impl<T, E> From<Pointer<T>> for Effect<Pointer<T>, E> {
210212
}
211213
}
212214

213-
// Allow tasks to return a pointer
214-
// This converts the pointer into a pure effect
215-
impl<T: Serialize> From<Pointer<T>> for Effect<Patch, Error> {
216-
fn from(ptr: Pointer<T>) -> Effect<Patch, Error> {
217-
Effect::from_result(ptr.into_result())
218-
}
219-
}
220-
221215
/// Extracts a sub-element of a state S as indicated by
222216
/// a path.
223217
///
@@ -227,7 +221,7 @@ impl<T: Serialize> From<Pointer<T>> for Effect<Patch, Error> {
227221
pub struct View<T>(Pointer<T>);
228222

229223
impl<T: DeserializeOwned> FromSystem for View<T> {
230-
type Error = InputError;
224+
type Error = ExtractionError;
231225

232226
fn from_system(system: &System, context: &Context) -> Result<Self, Self::Error> {
233227
let pointer = Pointer::<T>::from_system(system, context)?;
@@ -269,14 +263,6 @@ impl<T, E> From<View<T>> for Effect<View<T>, E> {
269263
}
270264
}
271265

272-
// Allow tasks to return a view
273-
// This converts the view into a pure effect
274-
impl<T: Serialize> From<View<T>> for Effect<Patch, Error> {
275-
fn from(view: View<T>) -> Effect<Patch, Error> {
276-
Effect::from_result(view.into_result())
277-
}
278-
}
279-
280266
#[cfg(test)]
281267
mod tests {
282268
use super::*;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod path;
22
mod planner;
33
mod system;
44

5+
pub mod errors;
56
pub mod extract;
67
pub mod task;
78
pub mod worker;

0 commit comments

Comments
 (0)