diff options
author | Matthias Beyer <matthias.beyer@ifm.com> | 2022-03-15 14:31:44 +0100 |
---|---|---|
committer | Matthias Beyer <matthias.beyer@ifm.com> | 2022-03-17 10:24:55 +0100 |
commit | 09eaab7b8a1c4cb5faee50f893c2061d994347c3 (patch) | |
tree | 3c5e1493822b9864198f3ecd165655e50ee45344 /tedge | |
parent | 07c50a1495fddf57bbae41003671a694d59a0427 (diff) |
Add tedge-cli binary
Signed-off-by: Matthias Beyer <matthias.beyer@ifm.com>
Diffstat (limited to 'tedge')
-rw-r--r-- | tedge/Cargo.toml | 44 | ||||
-rw-r--r-- | tedge/example-config.toml | 53 | ||||
-rw-r--r-- | tedge/src/cli.rs | 27 | ||||
-rw-r--r-- | tedge/src/logging.rs | 25 | ||||
-rw-r--r-- | tedge/src/main.rs | 156 |
5 files changed, 305 insertions, 0 deletions
diff --git a/tedge/Cargo.toml b/tedge/Cargo.toml new file mode 100644 index 00000000..b6d80a75 --- /dev/null +++ b/tedge/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "tedge-cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "3", features = ["derive", "cargo", "suggestions"] } +anyhow = "1" +toml = "0.5.8" +tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread", "signal"] } +cfg-if = "1" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } +console-subscriber = { version = "0.1", optional = true } + +tedge_api = { path = "../crates/core/tedge_api" } +tedge_core = { path = "../crates/core/tedge_core" } + +env_logger = { version = "0.9", optional = true } + +plugin_avg = { path = "../plugins/plugin_avg", optional = true } +plugin_log = { path = "../plugins/plugin_log", optional = true } +plugin_sysstat = { path = "../plugins/plugin_sysstat", optional = true } +plugin_inotify = { path = "../plugins/plugin_inotify", optional = true } + + +[features] +default = [ + "core_logging", + "builtin_plugin_avg", + "builtin_plugin_log", + "builtin_plugin_sysstat", + "builtin_plugin_inotify", +] + +core_debugging = ["console-subscriber"] +core_logging = ["tracing-subscriber"] +builtin_plugin_avg = ["plugin_avg"] +builtin_plugin_log = ["plugin_log"] +builtin_plugin_sysstat = ["plugin_sysstat"] +builtin_plugin_inotify = ["plugin_inotify"] + diff --git a/tedge/example-config.toml b/tedge/example-config.toml new file mode 100644 index 00000000..8a60fb05 --- /dev/null +++ b/tedge/example-config.toml @@ -0,0 +1,53 @@ +communication_buffer_size = 10 + +[plugins] + + +[plugins.logging] +kind = "log" + +[plugins.logging.configuration] +name = "logging" +level = "trace" +acknowledge = false +setup_logger = false + + +[plugins.syscpu] +kind = "sysinfo" + +[plugins.syscpu.configuration.cpu] +send_to = ["logging"] +interval_ms = 1000 + + +[plugins.syscpu.configuration.cpu.report_global_processor_info] +enable = true +frequency = true +cpu_usage = true +name = true +vendor_id = true +brand = true + +[plugins.syscpu.configuration.cpu.report_processor_info] +enable = false +frequency = false +cpu_usage = false +name = false +vendor_id = false +brand = false + +[plugins.syscpu.configuration.cpu.report_physical_core_count] +enable = true + + +[plugins.watchcargotoml] +kind = "inotify" + +[plugins.watchcargotoml.configuration] +target = "logging" +fail_on_err = false + +[plugins.watchcargotoml.configuration.pathes] +"Cargo.toml" = [ "ALL_EVENTS" ] + diff --git a/tedge/src/cli.rs b/tedge/src/cli.rs new file mode 100644 index 00000000..27c99526 --- /dev/null +++ b/tedge/src/cli.rs @@ -0,0 +1,27 @@ +use std::path::PathBuf; + +#[derive(Debug, clap::Parser)] +#[clap( + name = clap::crate_name!(), + version = clap::crate_version!(), + about = clap::crate_description!() +)] +pub(crate) struct Cli { + #[clap(short, long)] + pub(crate) verbose: bool, + + #[clap(short, long)] + pub(crate) debug: bool, + + #[clap(subcommand)] + pub(crate) command: CliCommand, +} + +#[derive(Debug, clap::Subcommand)] +pub(crate) enum CliCommand { + #[clap(name = "run")] + Run { config: PathBuf }, + + #[clap(name = "validate-config")] + ValidateConfig { config: PathBuf }, +} diff --git a/tedge/src/logging.rs b/tedge/src/logging.rs new file mode 100644 index 00000000..26e3e1a4 --- /dev/null +++ b/tedge/src/logging.rs @@ -0,0 +1,25 @@ +#[allow(unused)] +pub(crate) fn setup_logging(verbose: bool, debugging: bool) -> anyhow::Result<()> { + #[cfg(all(feature = "core_logging", not(feature = "core_debugging")))] + { + use tracing_subscriber::filter::EnvFilter; + use tracing_subscriber::filter::LevelFilter; + + let filter = if verbose && !debugging { + EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into()) + } else if debugging { + EnvFilter::from_default_env().add_directive(LevelFilter::DEBUG.into()) + } else { + EnvFilter::from_default_env() + }; + + let subscriber = tracing_subscriber::fmt::Subscriber::builder() + .with_env_filter(filter) + .finish(); + + tracing::subscriber::set_global_default(subscriber) + .map_err(|e| anyhow::anyhow!("Failed to set global subscriber: {:?}", e))?; + } + + Ok(()) +} diff --git a/tedge/src/main.rs b/tedge/src/main.rs new file mode 100644 index 00000000..7922f220 --- /dev/null +++ b/tedge/src/main.rs @@ -0,0 +1,156 @@ +use std::collections::HashSet; + +use clap::Parser; + +use tedge_api::PluginBuilder; +use tedge_core::configuration::TedgeConfiguration; +use tedge_core::TedgeApplication; +use tedge_core::TedgeApplicationCancelSender; +use tracing::debug; +use tracing::error; +use tracing::info; +use tracing::trace; + +mod cli; +mod logging; + +#[tokio::main] +#[tracing::instrument] +async fn main() -> anyhow::Result<()> { + #[cfg(feature = "core_debugging")] + { + console_subscriber::init(); + } + let args = crate::cli::Cli::parse(); + crate::logging::setup_logging(args.verbose, args.debug)?; + info!("Tedge booting..."); + debug!("Tedge CLI: {:?}", args); + let config = match args.command { + cli::CliCommand::Run { ref config } => config, + cli::CliCommand::ValidateConfig { ref config } => config, + }; + + let configuration = tokio::fs::read_to_string(config).await?; + + let config: TedgeConfiguration = toml::de::from_str(&configuration)?; + info!("Configuration loaded."); + + let application = TedgeApplication::builder(); + let mut plugin_kinds = HashSet::new(); + info!("Building application"); + + macro_rules! register_plugin { + ($app:ident, $cfg:tt, $pluginbuilder:expr) => {{ + cfg_if::cfg_if! { + if #[cfg(feature = $cfg)] { + info!("Registering plugin builder for plugins of type {}", $pluginbuilder.kind_name()); + if !plugin_kinds.insert($pluginbuilder.kind_name()) { + anyhow::bail!("Plugin kind '{}' was already registered, cannot register!", $pluginbuilder.kind_name()) + } + $app.with_plugin_builder(Box::new($pluginbuilder))? + } else { + trace!("Not supporting plugins of type {}", std::stringify!($pluginbuilder)); + $app + } + } + }} + } + + let application = register_plugin!( + application, + "builtin_plugin_avg", + plugin_avg::AvgPluginBuilder + ); + let application = register_plugin!( + application, + "builtin_plugin_log", + plugin_log::LogPluginBuilder + ); + let application = register_plugin!( + application, + "builtin_plugin_sysstat", + plugin_sysstat::SysStatPluginBuilder + ); + let application = register_plugin!( + application, + "builtin_plugin_inotify", + plugin_inotify::InotifyPluginBuilder + ); + + let (cancel_sender, application) = application.with_config(config)?; + info!("Application built"); + + match args.command { + cli::CliCommand::Run { .. } => { + debug!("Going to validate the configuration and run the application"); + + validate_config(&application).await?; + info!("Configuration validated"); + + run(cancel_sender, application).await + } + cli::CliCommand::ValidateConfig { .. } => { + debug!("Only going to validate the configuration"); + validate_config(&application).await?; + info!("Configuration validated"); + Ok(()) + } + } +} + +async fn run( + cancel_sender: TedgeApplicationCancelSender, + application: TedgeApplication, +) -> anyhow::Result<()> { + info!("Booting app now."); + let mut run_fut = Box::pin(application.run()); + + let kill_app = |fut| -> anyhow::Result<()> { + error!("Killing application"); + drop(fut); + anyhow::bail!("Application killed") + }; + + let res = tokio::select! { + res = &mut run_fut => { + res.map_err(anyhow::Error::from) + }, + + _int = tokio::signal::ctrl_c() => { + if !cancel_sender.is_cancelled() { + info!("Shutting down..."); + cancel_sender.cancel_app(); + tokio::select! { + res = &mut run_fut => res.map_err(anyhow::Error::from), + _ = tokio::signal::ctrl_c() => kill_app(run_fut), + } + } else { + kill_app(run_fut) + } + }, + }; + + info!("Bye"); + res +} + +async fn validate_config(application: &TedgeApplication) -> anyhow::Result<()> { + let mut any_err = false; + for (plugin_name, res) in application.verify_configurations().await { + match res { + Err(e) => { + error!("Error in Plugin '{}' configuration: {:?}", plugin_name, e); + any_err = true; + } + Ok(_) => { + info!("Plugin '{}' configured correctly", plugin_name); + } + } + } + + if any_err { + Err(anyhow::anyhow!("Plugin configuration error")) + } else { + Ok(()) + } +} |