diff --git a/Cargo.lock b/Cargo.lock index 3f58902eeed..7ffa90f400c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1148,7 +1148,6 @@ dependencies = [ "backoff", "log", "mockito", - "mockito", "nix", "regex", "reqwest", @@ -3554,6 +3553,7 @@ dependencies = [ "tedge_actors", "tedge_config", "tedge_config_manager", + "tedge_downloader_ext", "tedge_file_system_ext", "tedge_health_ext", "tedge_http_ext", diff --git a/Cargo.toml b/Cargo.toml index e10cebab1be..38e147202ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "plugins/tedge_apt_plugin", "plugins/tedge_configuration_plugin", "plugins/tedge_configuration_plugin", + "plugins/tedge_configuration_plugin", "plugins/tedge_dummy_plugin", "plugins/tedge_log_plugin", ] @@ -129,6 +130,7 @@ tedge_api = { path = "crates/core/tedge_api" } tedge_config = { path = "crates/common/tedge_config" } tedge_config_macros = { path = "crates/common/tedge_config_macros" } tedge_config_macros-impl = { path = "crates/common/tedge_config_macros/impl" } +tedge_config_manager = { path = "crates/extensions/tedge_config_manager" } tedge_downloader_ext = { path = "crates/extensions/tedge_downloader_ext" } tedge_file_system_ext = { path = "crates/extensions/tedge_file_system_ext" } tedge_health_ext = { path = "crates/extensions/tedge_health_ext" } diff --git a/plugins/tedge_configuration_plugin/Cargo.toml b/plugins/tedge_configuration_plugin/Cargo.toml new file mode 100644 index 00000000000..1b120ba61c6 --- /dev/null +++ b/plugins/tedge_configuration_plugin/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "tedge-configuration-plugin" +description = "Thin-edge device configuration management" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +homepage = { workspace = true } +repository = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +clap = { workspace = true } +tedge_actors = { workspace = true } +tedge_config = { workspace = true } +tedge_config_manager = { workspace = true } +tedge_downloader_ext = { workspace = true } +tedge_file_system_ext = { workspace = true } +tedge_health_ext = { workspace = true } +tedge_http_ext = { workspace = true } +tedge_mqtt_ext = { workspace = true } +tedge_signal_ext = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } diff --git a/plugins/tedge_configuration_plugin/src/main.rs b/plugins/tedge_configuration_plugin/src/main.rs new file mode 100644 index 00000000000..2b9584c38d3 --- /dev/null +++ b/plugins/tedge_configuration_plugin/src/main.rs @@ -0,0 +1,135 @@ +use clap::Parser; +use std::path::PathBuf; +use std::sync::Arc; +use tedge_actors::Runtime; +use tedge_config::system_services::get_log_level; +use tedge_config::system_services::set_log_level; +use tedge_config::TEdgeConfig; +use tedge_config::TEdgeConfigLocation; +use tedge_config::TEdgeConfigRepository; +use tedge_config::DEFAULT_TEDGE_CONFIG_PATH; +use tedge_config_manager::ConfigManagerBuilder; +use tedge_config_manager::ConfigManagerConfig; +use tedge_config_manager::ConfigManagerOptions; +use tedge_downloader_ext::DownloaderActor; +use tedge_file_system_ext::FsWatchActorBuilder; +use tedge_health_ext::HealthMonitorBuilder; +use tedge_http_ext::HttpActor; +use tedge_mqtt_ext::MqttActorBuilder; +use tedge_signal_ext::SignalActor; + +const AFTER_HELP_TEXT: &str = r#"On start, `tedge-configuration-plugin` notifies of its +managed configuration files and sends this list via MQTT. +`tedge-configuration-plugin` subscribes to the topics for the commands `config_snapshot` +and `config_update`. + +The thin-edge `CONFIG_DIR` is used: +* to find the `tedge.toml` where the following configs are defined: + ** `mqtt.bind.address` and `mqtt.bind.port` to connect to the tedge MQTT broker + ** `root.topic` and `device.topic`: for the MQTT topics to publish to and subscribe from +* to find/store the `tedge-configuration-plugin.toml`: the plugin configuration file"#; + +const TEDGE_CONFIGURATION_PLUGIN: &str = "tedge-configuration-plugin"; + +#[derive(Debug, Parser, Clone)] +#[clap( +name = clap::crate_name!(), +version = clap::crate_version!(), +about = clap::crate_description!(), +after_help = AFTER_HELP_TEXT +)] +pub struct ConfigPluginOpt { + /// Turn-on the debug log level. + /// + /// If off only reports ERROR, WARN, and INFO + /// If on also reports DEBUG + #[clap(long)] + pub debug: bool, + + #[clap(long = "config-dir", default_value = DEFAULT_TEDGE_CONFIG_PATH)] + pub config_dir: PathBuf, + + #[clap(long)] + mqtt_topic_root: Option>, + + #[clap(long)] + mqtt_device_topic_id: Option>, +} + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + let config_opt = ConfigPluginOpt::parse(); + + // Load tedge config from the provided location + let tedge_config_location = TEdgeConfigLocation::from_custom_root(&config_opt.config_dir); + + let log_level = if config_opt.debug { + tracing::Level::DEBUG + } else { + get_log_level( + "tedge-configuration-plugin", + &tedge_config_location.tedge_config_root_path, + )? + }; + set_log_level(log_level); + + let tedge_config = TEdgeConfigRepository::new(tedge_config_location).load()?; + + run(tedge_config, config_opt).await +} + +async fn run(tedge_config: TEdgeConfig, cliopts: ConfigPluginOpt) -> Result<(), anyhow::Error> { + let runtime_events_logger = None; + let mut runtime = Runtime::try_new(runtime_events_logger).await?; + + let mqtt_topic_root = cliopts + .mqtt_topic_root + .unwrap_or(tedge_config.mqtt.topic_root.clone().into()); + + let mqtt_device_topic_id = cliopts + .mqtt_device_topic_id + .unwrap_or(tedge_config.mqtt.device_topic_id.clone().into()); + + let mqtt_config = tedge_config.mqtt_config()?; + let mut mqtt_actor = MqttActorBuilder::new(mqtt_config.clone().with_session_name(format!( + "{TEDGE_CONFIGURATION_PLUGIN}#{mqtt_topic_root}/{mqtt_device_topic_id}", + ))); + + let mut fs_watch_actor = FsWatchActorBuilder::new(); + + let health_actor = HealthMonitorBuilder::new(TEDGE_CONFIGURATION_PLUGIN, &mut mqtt_actor); + + let mut http_actor = HttpActor::new().builder(); + + let mut downloader_actor = DownloaderActor::new().builder(); + + // Instantiate config manager actor + let manager_config = ConfigManagerConfig::from_options(ConfigManagerOptions { + config_dir: cliopts.config_dir, + mqtt_device_topic_id, + mqtt_topic_root, + })?; + + let config_actor = ConfigManagerBuilder::try_new( + manager_config, + &mut mqtt_actor, + &mut http_actor, + &mut fs_watch_actor, + &mut downloader_actor, + )?; + + // Shutdown on SIGINT + let signal_actor = SignalActor::builder(&runtime.get_handle()); + + // Run the actors + runtime.spawn(mqtt_actor).await?; + runtime.spawn(http_actor).await?; + runtime.spawn(downloader_actor).await?; + runtime.spawn(fs_watch_actor).await?; + runtime.spawn(config_actor).await?; + runtime.spawn(signal_actor).await?; + runtime.spawn(health_actor).await?; + + runtime.run_to_completion().await?; + Ok(()) +}