summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Müller <m.mueller@ifm.com>2022-05-24 11:00:51 +0200
committerMarcel Müller <m.mueller@ifm.com>2022-05-24 11:53:17 +0200
commitbed3ed4cf7df1be1f015dc8f9a33b2997e584f59 (patch)
tree482b9f1f38341f36f8b71aafe4af8b7f3eb10347
parenta6cce594489cff20bc5540127dc4fa074d7576e7 (diff)
Convert TedgeCore to a more complex error type
Errors until now where a flat enum. This did not reflect the multitude of ways the application can produce errors. This patch tries to align this reality with the error reporting. Signed-off-by: Marcel Müller <m.mueller@ifm.com>
-rw-r--r--Cargo.lock5
-rw-r--r--crates/core/tedge_core/Cargo.toml1
-rw-r--r--crates/core/tedge_core/src/communication.rs22
-rw-r--r--crates/core/tedge_core/src/configuration.rs28
-rw-r--r--crates/core/tedge_core/src/errors.rs255
-rw-r--r--crates/core/tedge_core/src/lib.rs135
-rw-r--r--crates/core/tedge_core/src/plugin_task.rs200
-rw-r--r--crates/core/tedge_core/src/reactor.rs109
-rw-r--r--crates/core/tedge_core/tests/check_concurrent_messages.rs4
-rw-r--r--crates/core/tedge_core/tests/plugin_does_not_shutdown.rs2
-rw-r--r--crates/core/tedge_core/tests/plugin_does_not_support_message.rs6
-rw-r--r--crates/core/tedge_core/tests/plugin_panic_lifecycle.rs2
-rw-r--r--crates/core/tedge_core/tests/plugin_panics_in_msg_handler.rs2
-rw-r--r--crates/core/tedge_core/tests/plugin_send_to_self_name.rs2
-rw-r--r--crates/core/tedge_core/tests/verify_configuration_fails.rs12
15 files changed, 459 insertions, 326 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9d568031..71a4b03e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1423,9 +1423,9 @@ checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
[[package]]
name = "itertools"
-version = "0.10.1"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
@@ -3460,6 +3460,7 @@ dependencies = [
"criterion",
"env_logger 0.9.0",
"futures",
+ "itertools",
"miette",
"serde",
"static_assertions",
diff --git a/crates/core/tedge_core/Cargo.toml b/crates/core/tedge_core/Cargo.toml
index 7a579b1b..0a3e8103 100644
--- a/crates/core/tedge_core/Cargo.toml
+++ b/crates/core/tedge_core/Cargo.toml
@@ -17,6 +17,7 @@ toml = "0.5.8"
tracing = "0.1"
tedge_api = { path = "../tedge_api" }
+itertools = "0.10.3"
[features]
tracing-off = ["tracing/release_max_level_off"]
diff --git a/crates/core/tedge_core/src/communication.rs b/crates/core/tedge_core/src/communication.rs
index 7c29e52e..80039949 100644
--- a/crates/core/tedge_core/src/communication.rs
+++ b/crates/core/tedge_core/src/communication.rs
@@ -1,13 +1,14 @@
use std::collections::HashMap;
use std::sync::Arc;
+use itertools::Itertools;
use tedge_api::address::MessageSender;
use tedge_api::error::DirectoryError;
use tedge_api::message::MessageType;
use tedge_api::plugin::PluginDirectory as ApiPluginDirectory;
use tedge_api::Address;
-use crate::errors::TedgeApplicationError;
+use crate::errors::{PluginInstantiationError, TedgeApplicationError};
/// Type for taking care of addresses
///
@@ -22,10 +23,16 @@ pub struct CorePluginDirectory {
impl CorePluginDirectory {
pub(crate) fn collect_from<I>(iter: I) -> Result<Self, TedgeApplicationError>
where
- I: std::iter::IntoIterator<Item = Result<(String, PluginInfo), TedgeApplicationError>>,
+ I: std::iter::IntoIterator<Item = Result<(String, PluginInfo), PluginInstantiationError>>,
{
+ let (oks, errors): (_, Vec<_>) = iter.into_iter().partition_result();
+
+ if !errors.is_empty() {
+ return Err(TedgeApplicationError::PluginInstantiationsError { errors });
+ }
+
Ok(CorePluginDirectory {
- plugins: iter.into_iter().collect::<Result<HashMap<_, _>, _>>()?,
+ plugins: oks,
core_communicator: MessageSender::new(Default::default()),
})
}
@@ -287,9 +294,8 @@ mod tests {
);
let channel_size = 1;
- let tedge_builder = crate::TedgeApplication::builder()
- .with_plugin_builder(testplugin::Builder {})
- .unwrap();
+ let tedge_builder =
+ crate::TedgeApplication::builder().with_plugin_builder(testplugin::Builder {});
let config: TedgeConfiguration = toml::de::from_str(&conf).unwrap();
let directory_iter = config.plugins().iter().map(|(pname, pconfig)| {
@@ -303,9 +309,7 @@ mod tests {
.cloned()
.collect::<Vec<MessageType>>()
})
- .ok_or_else(|| {
- TedgeApplicationError::UnknownPluginKind(pconfig.kind().as_ref().to_string())
- })?;
+ .ok_or_else(|| panic!())?;
Ok((
pname.to_string(),
diff --git a/crates/core/tedge_core/src/configuration.rs b/crates/core/tedge_core/src/configuration.rs
index 1403215a..7e7fc868 100644
--- a/crates/core/tedge_core/src/configuration.rs
+++ b/crates/core/tedge_core/src/configuration.rs
@@ -4,11 +4,11 @@ use std::{
path::{Path, PathBuf},
};
+use miette::IntoDiagnostic;
use tedge_api::PluginBuilder;
use tracing::debug;
-use crate::communication::PluginDirectory;
-use crate::errors::TedgeApplicationError;
+use crate::{communication::PluginDirectory, errors::PluginConfigurationError};
#[derive(serde::Deserialize, Debug)]
pub struct TedgeConfiguration {
@@ -76,39 +76,37 @@ impl InstanceConfiguration {
&self,
builder: &Box<dyn PluginBuilder<PluginDirectory>>,
root_config_path: &Path,
- ) -> crate::errors::Result<toml::Value> {
+ ) -> Result<toml::Value, miette::Error> {
match self {
InstanceConfiguration::Config(cfg) => builder
.verify_configuration(&cfg)
.await
- .map_err(TedgeApplicationError::PluginConfigVerificationFailed)
.map(|_| cfg.to_owned()),
InstanceConfiguration::ConfigFilePath(path) => {
async fn inner(
builder: &Box<dyn PluginBuilder<PluginDirectory>>,
root_config_path: &Path,
path: &Path,
- ) -> crate::errors::Result<toml::Value> {
+ ) -> Result<toml::Value, miette::Error> {
let file_path = root_config_path
.parent()
- .ok_or_else(|| {
- TedgeApplicationError::PathNotAFilePath(root_config_path.to_path_buf())
+ .ok_or_else(|| PluginConfigurationError::PathNotAFilePath {
+ path: root_config_path.to_path_buf(),
})?
.join(path);
debug!("Reading config file: {}", file_path.display());
let file_contents =
- tokio::fs::read_to_string(file_path).await.map_err(|_| {
- TedgeApplicationError::PluginConfigReadFailed(path.to_path_buf())
+ tokio::fs::read_to_string(file_path).await.map_err(|e| {
+ PluginConfigurationError::PathNotReadable {
+ path: path.to_path_buf(),
+ error: e,
+ }
})?;
- let cfg = toml::from_str(&file_contents)?;
+ let cfg = toml::from_str(&file_contents).into_diagnostic()?;
- builder
- .verify_configuration(&cfg)
- .await
- .map_err(TedgeApplicationError::PluginConfigVerificationFailed)
- .map(|_| cfg)
+ builder.verify_configuration(&cfg).await.map(|_| cfg)
}
inner(builder, root_config_path, &path).await
diff --git a/crates/core/tedge_core/src/errors.rs b/crates/core/tedge_core/src/errors.rs
index 17cdef1d..0fd8c547 100644
--- a/crates/core/tedge_core/src/errors.rs
+++ b/crates/core/tedge_core/src/errors.rs
@@ -1,66 +1,239 @@
use std::path::PathBuf;
+use std::time::Duration;
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
pub enum TedgeApplicationError {
- #[error("Plugin instantiation failed")]
- PluginInstantiationFailed(tedge_api::error::PluginError),
+ #[error("Could not complete building the tedge application because of the following errors")]
+ ApplicationBuilderErrors {
+ #[related]
+ errors: Vec<TedgeApplicationBuilderError>,
+ },
+ #[error("Could not verify the configuration of one or more plugins")]
+ PluginConfigVerificationsError {
+ #[related]
+ errors: Vec<PluginConfigurationError>,
+ },
+ #[error("Could not instantiate one or more plugins")]
+ PluginInstantiationsError {
+ #[related]
+ errors: Vec<PluginInstantiationError>,
+ },
+ #[error("Could not shutdown one or more plugins")]
+ PluginLifecycleErrors {
+ #[related]
+ errors: Vec<PluginLifecycleError>,
+ },
+}
+
+#[derive(Debug, miette::Diagnostic, thiserror::Error)]
+pub enum TedgeApplicationBuilderError {
+ #[error("A plugin builder with the duplicate name '{name}' was registered")]
+ #[diagnostic(help("The duplicate name was registered by the builder name '{builder_name}'"))]
+ DuplicateKind {
+ name: String,
+ builder_name: &'static str,
+ },
+ #[error("Could not read configuration at {:?}", path)]
+ PathNotReadable {
+ path: PathBuf,
+ #[source]
+ error: std::io::Error,
+ },
+ #[error("Could not parse configuration at {:?}", path)]
+ ConfigNotParseable {
+ path: PathBuf,
+ #[source]
+ error: toml::de::Error,
+ },
+}
+
+#[derive(Debug, miette::Diagnostic, thiserror::Error)]
+pub enum PluginConfigurationError {
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ Verification(PluginConfigVerificationError),
+ #[error("Given path is not a filepath: {:?}", path)]
+ PathNotAFilePath { path: PathBuf },
+ #[error("Could not read from path: {:?}", path)]
+ PathNotReadable {
+ path: PathBuf,
+ #[source]
+ error: std::io::Error,
+ },
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ UnknownKind(PluginKindUnknownError),
+}
- #[error("Plugin errored during startup")]
- PluginErroredOnStart(tedge_api::error::PluginError),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("No plugin with kind '{name}' is currently registered with the application")]
+pub struct PluginKindUnknownError {
+ pub name: String,
+ #[diagnostic(help)]
+ pub alternatives: Option<String>,
+}
- #[error("Failed to read configuration file")]
- ConfigReadFailed(std::io::Error),
+#[derive(Debug, thiserror::Error)]
+#[error("Failed to instantiate plugin '{name}'")]
+pub struct PluginConfigVerificationError {
+ pub name: String,
+ pub error: tedge_api::error::PluginError,
+}
- #[error("Path is not a file path {0}")]
- PathNotAFilePath(PathBuf),
+impl miette::Diagnostic for PluginConfigVerificationError {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
+ Some(Box::new(std::iter::once(self.error.as_ref())))
+ }
+}
- #[error("Config verification failed")]
- PluginConfigVerificationFailed(tedge_api::error::PluginError),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("No configuration for plugin '{name}' was found.")]
+#[diagnostic(help("Add a configuration block for plugin '{name}'"))]
+pub struct PluginConfigurationNotFoundError {
+ pub name: String,
+}
- #[error("Failed to deserialize configuration")]
- PluginConfigurationDeserializationFailed(#[from] toml::de::Error),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+pub enum PluginInstantiationError {
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ BuilderInstantiation(PluginBuilderInstantiationError),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ KindNotFound(PluginKindUnknownError),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ ConfigurationNotFound(PluginConfigurationNotFoundError),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ ConfigurationVerificationFailed(PluginConfigVerificationError),
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Failed to instantiate plugin '{name}'")]
+pub struct PluginBuilderInstantiationError {
+ pub name: String,
+ pub error: tedge_api::error::PluginError,
+}
- #[error("Failed to read configuration file: {0}")]
- PluginConfigReadFailed(PathBuf),
+impl miette::Diagnostic for PluginBuilderInstantiationError {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
+ Some(Box::new(std::iter::once(self.error.as_ref())))
+ }
+}
- #[error("Plugin kind exists already: {0}")]
- PluginKindExists(String),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+pub enum PluginLifecycleError {
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginStartPanicked(PluginStartPanicked),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginStartFailed(PluginStartFailed),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginMainPanicked(PluginMainPanicked),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginMainFailed(PluginMainFailed),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginMessageHandlerPanicked(PluginMessageHandlerPanicked),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginMessageHandlerFailed(PluginMessageHandlerFailed),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginStopPanicked(PluginStopPanicked),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginStopTimeout(PluginStopTimeout),
+ #[error(transparent)]
+ #[diagnostic(transparent)]
+ PluginStopFailed(PluginStopFailed),
+}
- #[error("The following Plugin kind are not covered in the configuration: {0}")]
- UnconfiguredPlugins(crate::utils::CommaSeperatedString),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("Plugin '{name}' panicked while starting up")]
+pub struct PluginStartPanicked {
+ pub name: String,
+}
- #[error("The following Plugin has no configuration: {0}")]
- PluginConfigMissing(String),
+#[derive(Debug, thiserror::Error)]
+#[error("Plugin '{name}' encountered an error while starting up")]
+pub struct PluginStartFailed {
+ pub name: String,
+ pub error: tedge_api::PluginError,
+}
- #[error("Unknown Plugin kind: {0}")]
- UnknownPluginKind(String),
+impl miette::Diagnostic for PluginStartFailed {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
+ Some(Box::new(std::iter::once(self.error.as_ref())))
+ }
+}
- #[error("Plugin '{0}' shutdown timeouted")]
- PluginShutdownTimeout(String),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("Plugin '{name}' panicked while running main")]
+pub struct PluginMainPanicked {
+ pub name: String,
+}
- #[error("Plugin '{0}' shutdown errored")]
- PluginShutdownError(String),
+#[derive(Debug, thiserror::Error)]
+#[error("Plugin '{name}' encountered an error while running main")]
+pub struct PluginMainFailed {
+ pub name: String,
+ pub error: tedge_api::PluginError,
+}
- #[error("Plugin '{0}' setup paniced")]
- PluginSetupPaniced(String),
+impl miette::Diagnostic for PluginMainFailed {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
+ Some(Box::new(std::iter::once(self.error.as_ref())))
+ }
+}
- #[error("Plugin '{0}' setup failed")]
- PluginSetupFailed(String, tedge_api::error::PluginError),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("Plugin '{name}' panicked while handling messages of type '{handled_message_type}'")]
+pub struct PluginMessageHandlerPanicked {
+ pub name: String,
+ pub handled_message_type: String,
+}
- #[error("Plugin '{0}' setup paniced")]
- PluginMainPaniced(String),
+#[derive(Debug, thiserror::Error)]
+#[error("Plugin '{name}' panicked while starting up")]
+pub struct PluginMessageHandlerFailed {
+ pub name: String,
+ pub handled_message_type: String,
+ pub error: tedge_api::PluginError,
+}
- #[error("Plugin '{0}' setup failed")]
- PluginMainFailed(String, tedge_api::error::PluginError),
+impl miette::Diagnostic for PluginMessageHandlerFailed {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
+ Some(Box::new(std::iter::once(self.error.as_ref())))
+ }
+}
- #[error("Plugin '{0}' paniced in message handler")]
- PluginMessageHandlerPaniced(String),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("Plugin '{name}' panicked while stopping")]
+pub struct PluginStopPanicked {
+ pub name: String,
+}
- #[error("Plugin message handling for plugin '{0}' failed")]
- PluginMessageHandlingFailed(String),
+#[derive(miette::Diagnostic, Debug, thiserror::Error)]
+#[error("Plugin '{name}' did not stop after a timeout of '{}'")]
+pub struct PluginStopTimeout {
+ pub name: String,
+ pub timeout_duration: Duration,
+}
- #[error("Message handling scheduling failed for plugin '{0}'")]
- MessageHandlingJobFailed(String, tokio::task::JoinError),
+#[derive(Debug, thiserror::Error)]
+#[error("Plugin '{name}' encountered an error while stopping")]
+pub struct PluginStopFailed {
+ pub name: String,
+ pub error: tedge_api::PluginError,
}
-pub(crate) type Result<T> = std::result::Result<T, TedgeApplicationError>;
+impl miette::Diagnostic for PluginStopFailed {
+ fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
+ Some(Box::new(std::iter::once(self.error.as_ref())))
+ }
+}
diff --git a/crates/core/tedge_core/src/lib.rs b/crates/core/tedge_core/src/lib.rs
index 5fee11ed..8ea4a0fa 100644
--- a/crates/core/tedge_core/src/lib.rs
+++ b/crates/core/tedge_core/src/lib.rs
@@ -4,29 +4,32 @@ use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
-use miette::IntoDiagnostic;
+use itertools::Itertools;
use tedge_api::plugin::HandleTypes;
use tedge_api::PluginBuilder;
use tokio_util::sync::CancellationToken;
use tracing::debug;
-use tracing::debug_span;
+
use tracing::event;
-use tracing::Instrument;
+
use tracing::Level;
mod communication;
pub mod configuration;
mod core_task;
pub mod errors;
+mod message_handler;
mod plugin_task;
mod reactor;
mod utils;
-mod message_handler;
pub use crate::communication::PluginDirectory;
use crate::configuration::PluginInstanceConfiguration;
use crate::configuration::TedgeConfiguration;
-use crate::errors::Result;
+use crate::errors::PluginConfigVerificationError;
+use crate::errors::PluginConfigurationError;
+use crate::errors::PluginKindUnknownError;
+use crate::errors::TedgeApplicationBuilderError;
use crate::errors::TedgeApplicationError;
/// A TedgeApplication
@@ -59,6 +62,7 @@ impl TedgeApplication {
TedgeApplicationBuilder {
cancellation_token: CancellationToken::new(),
plugin_builders: HashMap::new(),
+ errors: vec![],
}
}
@@ -83,52 +87,67 @@ impl TedgeApplication {
/// Run the TedgeApplication that has been setup for running
///
/// This function runs as long as there is no shutdown signalled to the application.
- #[tracing::instrument(skip(self), level = "debug")]
- pub async fn run(self) -> Result<()> {
- // This `Reactor` type is only used to seperate the public-interface implementing parts of
- // this crate from the orchestration and lifecycle management code bits.
+ ///
+ /// # Note
+ ///
+ /// This function makes sure that the configuration is verified before the plugins are started.
+ /// So there is no need to call [TedgeApplication::verify_configuration] before this.
+ pub async fn run(self) -> Result<(), TedgeApplicationError> {
crate::reactor::Reactor(self).run().await
}
/// Check whether all configured plugin kinds exist (are available in registered plugins)
/// and that the configurations for the individual plugins are actually valid.
- pub async fn verify_configurations(&self) -> Vec<(String, Result<()>)> {
+ #[tracing::instrument(skip(self))]
+ pub async fn verify_configurations(&self) -> Result<(), TedgeApplicationError> {
use futures::stream::StreamExt;
debug!("Verifying configurations");
- self.config()
+ let results = self
+ .config()
.plugins()
.iter()
.map(
- |(plugin_name, plugin_cfg): (&String, &PluginInstanceConfiguration)| {
- async {
- if let Some((_, builder)) =
- self.plugin_builders().get(plugin_cfg.kind().as_ref())
- {
- debug!("Verifying {}", plugin_cfg.kind().as_ref());
- let res = plugin_cfg
- .configuration()
- .verify_with_builder(builder, self.config_path())
- .await
- .into_diagnostic()
- .map_err(TedgeApplicationError::PluginConfigVerificationFailed)
- .map(|_| ());
- (plugin_name.to_string(), res)
- } else {
- (
- plugin_name.to_string(),
- Err(TedgeApplicationError::UnknownPluginKind(
- plugin_cfg.kind().as_ref().to_string(),
- )),
- )
- }
+ |(plugin_name, plugin_cfg): (&String, &PluginInstanceConfiguration)| async {
+ if let Some((_, builder)) =
+ self.plugin_builders().get(plugin_cfg.kind().as_ref())
+ {
+ debug!("Verifying {}", plugin_cfg.kind().as_ref());
+ let res = plugin_cfg
+ .configuration()
+ .verify_with_builder(builder, self.config_path())
+ .await
+ .map_err(|e| {
+ PluginConfigurationError::Verification(
+ PluginConfigVerificationError {
+ name: plugin_name.to_string(),
+ error: e,
+ },
+ )
+ });
+
+ res
+ } else {
+ Err(PluginConfigurationError::UnknownKind(
+ PluginKindUnknownError {
+ name: plugin_cfg.kind().as_ref().to_string(),
+ alternatives: None,
+ },
+ ))
}
- .instrument(debug_span!("verify configuration", plugin.name = %plugin_name))
},
)
.collect::<futures::stream::FuturesUnordered<_>>()
- .collect::<Vec<(String, Result<()>)>>()
- .await
+ .collect::<Vec<Result<_, _>>>()
+ .await;
+
+ let (_oks, errors): (Vec<_>, Vec<_>) = results.into_iter().partition_result();
+
+ if !errors.is_empty() {
+ return Err(TedgeApplicationError::PluginConfigVerificationsError { errors });
+ }
+
+ Ok(())
}
}
@@ -136,6 +155,7 @@ impl TedgeApplication {
pub struct TedgeApplicationBuilder {
cancellation_token: CancellationToken,
plugin_builders: HashMap<String, (HandleTypes, Box<dyn PluginBuilder<PluginDirectory>>)>,
+ errors: Vec<TedgeApplicationBuilderError>,
}
impl TedgeApplicationBuilder {
@@ -148,10 +168,7 @@ impl TedgeApplicationBuilder {
/// running once the application starts up, but merely that the application _knows_ about this
/// plugin builder and is able to construct a plugin with this builder, if necessary (e.g. if
/// configured in a configuration file).
- pub fn with_plugin_builder<PB: PluginBuilder<PluginDirectory>>(
- mut self,
- builder: PB,
- ) -> Result<Self> {
+ pub fn with_plugin_builder<PB: PluginBuilder<PluginDirectory>>(mut self, builder: PB) -> Self {
let handle_types = PB::kind_message_types();
let kind_name = PB::kind_name();
event!(
@@ -162,14 +179,17 @@ impl TedgeApplicationBuilder {
);
if self.plugin_builders.contains_key(kind_name) {
- return Err(TedgeApplicationError::PluginKindExists(
- kind_name.to_string(),
- ));
+ self.errors
+ .push(TedgeApplicationBuilderError::DuplicateKind {
+ name: kind_name.to_string(),
+ builder_name: std::any::type_name::<PB>(),
+ });
+ return self;
}
self.plugin_builders
.insert(kind_name.to_string(), (handle_types, Box::new(builder)));
- Ok(self)
+ self
}
/// Finalize the [`TedgeApplication`] by instantiating it with a `TedgeConfiguration`]
@@ -178,14 +198,31 @@ impl TedgeApplicationBuilder {
pub async fn with_config_from_path(
self,
config_path: impl AsRef<Path>,
- ) -> Result<(TedgeApplicationCancelSender, TedgeApplication)> {
+ ) -> Result<(TedgeApplicationCancelSender, TedgeApplication), TedgeApplicationError> {
+ if !self.errors.is_empty() {
+ return Err(TedgeApplicationError::ApplicationBuilderErrors {
+ errors: self.errors,
+ });
+ }
let config_path = config_path.as_ref();
debug!(?config_path, "Loading config from path");
- let config_str = tokio::fs::read_to_string(&config_path)
- .await
- .map_err(TedgeApplicationError::ConfigReadFailed)?;
- let config = toml::de::from_str(&config_str)?;
+ let config_str = tokio::fs::read_to_string(&config_path).await.map_err(|e| {
+ TedgeApplicationError::ApplicationBuilderErrors {
+ errors: vec![TedgeApplicationBuilderError::PathNotReadable {
+ path: config_path.to_path_buf(),
+ error: e,
+ }],
+ }
+ })?;
+ let config = toml::de::from_str(&config_str).map_err(|e| {
+ TedgeApplicationError::ApplicationBuilderErrors {
+ errors: vec![TedgeApplicationBuilderError::ConfigNotParseable {
+ path: config_path.to_path_buf(),
+ error: e,
+ }],
+ }
+ })?;
let cancellation = TedgeApplicationCancelSender(self.cancellation_token.clone());
let app = TedgeApplication {
config_path: config_path.to_path_buf(),
diff --git a/crates/core/tedge_core/src/plugin_task.rs b/crates/core/tedge_core/src/plugin_task.rs
index 7f4b994d..031117b3 100644
--- a/crates/core/tedge_core/src/plugin_task.rs
+++ b/crates/core/tedge_core/src/plugin_task.rs
@@ -9,14 +9,19 @@ use tokio::sync::mpsc::Sender;
use tokio::sync::RwLock;
use tokio::sync::Semaphore;
use tokio_util::sync::CancellationToken;
-use tracing::debug;
use tracing::error;
use tracing::info;
use tracing::trace;
use tracing::Instrument;
-use crate::errors::Result;
-use crate::errors::TedgeApplicationError;
+use crate::errors::PluginLifecycleError;
+use crate::errors::PluginMainFailed;
+use crate::errors::PluginMainPanicked;
+use crate::errors::PluginStartFailed;
+use crate::errors::PluginStartPanicked;
+use crate::errors::PluginStopFailed;
+use crate::errors::PluginStopPanicked;
+use crate::errors::PluginStopTimeout;
use crate::message_handler::make_message_handler;
/// Type for handling the lifecycle of one individual Plugin instance
@@ -66,11 +71,11 @@ impl PluginTask {
}
impl PluginTask {
- pub async fn run_start(&mut self) -> crate::errors::Result<()> {
+ pub async fn run_start(&mut self) -> Result<(), PluginLifecycleError> {
let plugin_name: &str = &self.plugin_name;
let mut plug_write = self.plugin.write().await;
- // we can use AssertUnwindSafe here because we're _not_ using the plugin after a panic has
+ // weIch bin ja noch can use AssertUnwindSafe here because we're _not_ using the plugin after a panic has
// happened.
match std::panic::AssertUnwindSafe(plug_write.plugin_mut().start())
.catch_unwind()
@@ -91,13 +96,18 @@ impl PluginTask {
// don't make use of the plugin for unwind safety reasons, and the plugin
// will be dropped
- return Err(TedgeApplicationError::PluginSetupPaniced(
- plugin_name.to_string(),
+ return Err(PluginLifecycleError::PluginStartPanicked(
+ PluginStartPanicked {
+ name: plugin_name.to_string(),
+ },
));
}
Ok(res) => {
res.map_err(|e| {
- TedgeApplicationError::PluginSetupFailed(plugin_name.to_string(), e)
+ PluginLifecycleError::PluginStartFailed(PluginStartFailed {
+ name: plugin_name.to_string(),
+ error: e,
+ })
})?;
}
};
@@ -105,7 +115,7 @@ impl PluginTask {
Ok(())
}
- pub async fn enable_communications(&self) -> crate::errors::Result<()> {
+ pub async fn enable_communications(&self) -> Result<(), PluginLifecycleError> {