diff options
author | initard <alex.solomes@softwareag.com> | 2022-01-27 17:51:29 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-27 17:51:29 +0100 |
commit | cd15f10bb6f893a18e63c54ad41a677a1e5ec1bb (patch) | |
tree | e8e2a571417837519ff4270214642a8c9c02aa0a /crates | |
parent | 6bbcdff8de2569bfff3e1a8085c21aaf98a5d96f (diff) |
#733 Configurable Device Type (#782)
* configurable device type (#733)
Signed-off-by: initard <solo@softwareag.com>
* adding device data fragment to mapper init (#733)
Signed-off-by: initard <solo@softwareag.com>
* fixing test
Signed-off-by: initard <solo@softwareag.com>
* adding device type when making new device (#733)
Signed-off-by: initard <solo@softwareag.com>
* removing comment and unused package
Signed-off-by: initard <solo@softwareag.com>
* passing down device type and removing tedge util
Signed-off-by: initard <solo@softwareag.com>
* string formatting (#733)
Signed-off-by: initard <solo@softwareag.com>
* documenting device data settings
Signed-off-by: initard <solo@softwareag.com>
* addressing minor pr comments on syntax (#733)
Signed-off-by: initard <solo@softwareag.com>
Co-authored-by: initard <solo@softwareag.com>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/common/tedge_config/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/common/tedge_config/src/error.rs | 3 | ||||
-rw-r--r-- | crates/common/tedge_config/src/settings.rs | 11 | ||||
-rw-r--r-- | crates/common/tedge_config/src/tedge_config.rs | 26 | ||||
-rw-r--r-- | crates/common/tedge_config/src/tedge_config_defaults.rs | 8 | ||||
-rw-r--r-- | crates/common/tedge_config/src/tedge_config_dto.rs | 3 | ||||
-rw-r--r-- | crates/common/tedge_config/tests/test_tedge_config.rs | 1 | ||||
-rw-r--r-- | crates/core/tedge/src/cli/config/config_key.rs | 1 | ||||
-rw-r--r-- | crates/core/tedge/src/cli/connect/c8y_direct_connection.rs | 17 | ||||
-rw-r--r-- | crates/core/tedge/src/cli/connect/command.rs | 5 | ||||
-rw-r--r-- | crates/core/tedge_mapper/src/c8y_converter.rs | 40 | ||||
-rw-r--r-- | crates/core/tedge_mapper/src/c8y_fragments.rs | 27 | ||||
-rw-r--r-- | crates/core/tedge_mapper/src/c8y_mapper.rs | 9 |
13 files changed, 136 insertions, 16 deletions
diff --git a/crates/common/tedge_config/Cargo.toml b/crates/common/tedge_config/Cargo.toml index dc82f240..bce75bd9 100644 --- a/crates/common/tedge_config/Cargo.toml +++ b/crates/common/tedge_config/Cargo.toml @@ -9,6 +9,7 @@ rust-version = "1.58" certificate = { path = "../certificate" } serde = { version = "1.0", features = ["derive"] } tedge_utils = { path = "../tedge_utils" } +tedge_users = { path = "../tedge_users" } tempfile = "3.2" thiserror = "1.0" toml = "0.5" diff --git a/crates/common/tedge_config/src/error.rs b/crates/common/tedge_config/src/error.rs index f1e23642..09cc9383 100644 --- a/crates/common/tedge_config/src/error.rs +++ b/crates/common/tedge_config/src/error.rs @@ -17,4 +17,7 @@ pub enum TEdgeConfigError { #[error("Config file not found: {0}")] ConfigFileNotFound(std::path::PathBuf), + + #[error("Home directory is not found.")] + HomeDirNotFound, } diff --git a/crates/common/tedge_config/src/settings.rs b/crates/common/tedge_config/src/settings.rs index 6f043fb5..d7b8c0d7 100644 --- a/crates/common/tedge_config/src/settings.rs +++ b/crates/common/tedge_config/src/settings.rs @@ -23,6 +23,17 @@ impl ConfigSetting for DeviceIdSetting { type Value = String; } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct DeviceTypeSetting; + +impl ConfigSetting for DeviceTypeSetting { + const KEY: &'static str = "device.type"; + + const DESCRIPTION: &'static str = "The default device type. Example: thin-edge.io"; + + type Value = String; +} + /// /// Path to the private key file. Example: /home/user/.tedge/tedge-private-key.pem /// diff --git a/crates/common/tedge_config/src/tedge_config.rs b/crates/common/tedge_config/src/tedge_config.rs index 1385a010..52e0178e 100644 --- a/crates/common/tedge_config/src/tedge_config.rs +++ b/crates/common/tedge_config/src/tedge_config.rs @@ -33,6 +33,32 @@ impl ConfigSettingAccessor<DeviceIdSetting> for TEdgeConfig { } } +impl ConfigSettingAccessor<DeviceTypeSetting> for TEdgeConfig { + fn query(&self, _setting: DeviceTypeSetting) -> ConfigSettingResult<String> { + let device_type = self + .data + .device + .device_type + .clone() + .unwrap_or_else(|| self.config_defaults.default_device_type.clone()); + Ok(device_type) + } + + fn update( + &mut self, + _setting: DeviceTypeSetting, + value: <DeviceTypeSetting as ConfigSetting>::Value, + ) -> ConfigSettingResult<()> { + self.data.device.device_type = Some(value); + Ok(()) + } + + fn unset(&mut self, _setting: DeviceTypeSetting) -> ConfigSettingResult<()> { + self.data.device.device_type = None; + Ok(()) + } +} + fn device_id_read_only_error() -> ConfigSettingError { ConfigSettingError::ReadonlySetting { message: concat!( diff --git a/crates/common/tedge_config/src/tedge_config_defaults.rs b/crates/common/tedge_config/src/tedge_config_defaults.rs index 99eac131..3c7cea92 100644 --- a/crates/common/tedge_config/src/tedge_config_defaults.rs +++ b/crates/common/tedge_config/src/tedge_config_defaults.rs @@ -6,6 +6,7 @@ use std::path::Path; const DEFAULT_ETC_PATH: &str = "/etc"; const DEFAULT_PORT: u16 = 1883; const DEFAULT_TMP_PATH: &str = "/tmp"; +const DEFAULT_DEVICE_TYPE: &str = "thin-edge.io"; /// Stores default values for use by `TEdgeConfig` in case no configuration setting /// is available. @@ -41,6 +42,9 @@ pub struct TEdgeConfigDefaults { /// Default tmp path pub default_tmp_path: FilePath, + + /// Default device type + pub default_device_type: String, } impl From<&TEdgeConfigLocation> for TEdgeConfigDefaults { @@ -63,6 +67,7 @@ impl From<&TEdgeConfigLocation> for TEdgeConfigDefaults { default_mapper_timestamp: Flag(true), default_mqtt_port: Port(DEFAULT_PORT), default_tmp_path: tmp_path.into(), + default_device_type: DEFAULT_DEVICE_TYPE.into(), } } } @@ -85,7 +90,8 @@ fn test_from_tedge_config_location() { default_c8y_root_cert_path: FilePath::from("/etc/ssl/certs"), default_mapper_timestamp: Flag(true), default_mqtt_port: Port(DEFAULT_PORT), - default_tmp_path: FilePath::from("/tmp") + default_tmp_path: FilePath::from("/tmp"), + default_device_type: DEFAULT_DEVICE_TYPE.into(), } ); } diff --git a/crates/common/tedge_config/src/tedge_config_dto.rs b/crates/common/tedge_config/src/tedge_config_dto.rs index 8b9cc47c..ae9cd15f 100644 --- a/crates/common/tedge_config/src/tedge_config_dto.rs +++ b/crates/common/tedge_config/src/tedge_config_dto.rs @@ -44,6 +44,9 @@ pub(crate) struct DeviceConfigDto { /// Path where the device's certificate is stored. /// Defaults to $HOME/.tedge/tedge-certificate.crt pub(crate) cert_path: Option<FilePath>, + + #[serde(rename = "type")] + pub(crate) device_type: Option<String>, } /// Represents the Cumulocity specific configurations defined in the diff --git a/crates/common/tedge_config/tests/test_tedge_config.rs b/crates/common/tedge_config/tests/test_tedge_config.rs index c77aab7e..26118adf 100644 --- a/crates/common/tedge_config/tests/test_tedge_config.rs +++ b/crates/common/tedge_config/tests/test_tedge_config.rs @@ -857,6 +857,7 @@ fn dummy_tedge_config_defaults() -> TEdgeConfigDefaults { default_mapper_timestamp: Flag(true), default_mqtt_port: Port(1883), default_tmp_path: FilePath::from("/tmp"), + default_device_type: String::from("test"), } } diff --git a/crates/core/tedge/src/cli/config/config_key.rs b/crates/core/tedge/src/cli/config/config_key.rs index f2c35c12..1b0fcef3 100644 --- a/crates/core/tedge/src/cli/config/config_key.rs +++ b/crates/core/tedge/src/cli/config/config_key.rs @@ -40,6 +40,7 @@ impl ConfigKey { pub fn list_all() -> Vec<ConfigKey> { vec![ config_key!(DeviceIdSetting), + config_key!(DeviceTypeSetting), config_key!(DeviceKeyPathSetting), config_key!(DeviceCertPathSetting), config_key!(C8yUrlSetting), diff --git a/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs b/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs index a9c45b31..049429b2 100644 --- a/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs +++ b/crates/core/tedge/src/cli/connect/c8y_direct_connection.rs @@ -18,6 +18,7 @@ use tedge_users::UserManager; pub fn create_device_with_direct_connection( user_manager: UserManager, bridge_config: &BridgeConfig, + device_type: &str, ) -> Result<(), ConnectError> { const DEVICE_ALREADY_EXISTS: &[u8] = b"41,100,Device already existing"; const DEVICE_CREATE_ERROR_TOPIC: &str = "s/e"; @@ -49,7 +50,11 @@ pub fn create_device_with_direct_connection( for event in connection.iter() { match event { Ok(Event::Incoming(Packet::SubAck(_))) => { - publish_device_create_message(&mut client, &bridge_config.remote_clientid.clone())?; + publish_device_create_message( + &mut client, + &bridge_config.remote_clientid.clone(), + &device_type, + )?; } Ok(Event::Incoming(Packet::Publish(response))) => { // We got a response @@ -64,6 +69,7 @@ pub fn create_device_with_direct_connection( publish_device_create_message( &mut client, &bridge_config.remote_clientid.clone(), + &device_type, )?; device_create_try += 1; } else { @@ -88,14 +94,17 @@ pub fn create_device_with_direct_connection( Err(ConnectError::TimeoutElapsedError) } -fn publish_device_create_message(client: &mut Client, device_id: &str) -> Result<(), ConnectError> { +fn publish_device_create_message( + client: &mut Client, + device_id: &str, + device_type: &str, +) -> Result<(), ConnectError> { const DEVICE_CREATE_PUBLISH_TOPIC: &str = "s/us"; - const DEVICE_TYPE: &str = "thin-edge.io"; client.publish( DEVICE_CREATE_PUBLISH_TOPIC, QoS::ExactlyOnce, false, - format!("100,{},{}", device_id, DEVICE_TYPE).as_bytes(), + format!("100,{},{}", device_id, device_type).as_bytes(), )?; Ok(()) } diff --git a/crates/core/tedge/src/cli/connect/command.rs b/crates/core/tedge/src/cli/connect/command.rs index 004d1891..18052a03 100644 --- a/crates/core/tedge/src/cli/connect/command.rs +++ b/crates/core/tedge/src/cli/connect/command.rs @@ -118,12 +118,15 @@ impl Command for ConnectCommand { ); self.config_repository.store(&config)?; + let device_type = config.query(DeviceTypeSetting)?; + new_bridge( &bridge_config, &updated_mosquitto_config, self.service_manager.as_ref(), self.user_manager.clone(), &self.config_location, + &device_type, )?; match self.check_connection(&config) { @@ -373,6 +376,7 @@ fn new_bridge( service_manager: &dyn SystemServiceManager, user_manager: UserManager, config_location: &TEdgeConfigLocation, + device_type: &str, ) -> Result<(), ConnectError> { println!("Checking if {} is available.\n", service_manager.name()); let service_manager_result = service_manager.check_operational(); @@ -397,6 +401,7 @@ fn new_bridge( let () = c8y_direct_connection::create_device_with_direct_connection( user_manager, bridge_config, + &device_type, )?; } diff --git a/crates/core/tedge_mapper/src/c8y_converter.rs b/crates/core/tedge_mapper/src/c8y_converter.rs index 81c8e7c0..40c47f5f 100644 --- a/crates/core/tedge_mapper/src/c8y_converter.rs +++ b/crates/core/tedge_mapper/src/c8y_converter.rs @@ -1,4 +1,4 @@ -use crate::c8y_fragments::C8yAgentFragment; +use crate::c8y_fragments::{C8yAgentFragment, C8yDeviceDataFragment}; use crate::error::*; use crate::size_threshold::SizeThreshold; use crate::{converter::*, operations::Operations}; @@ -24,10 +24,11 @@ pub struct CumulocityConverter { children: HashSet<String>, pub(crate) mapper_config: MapperConfig, device_name: String, + device_type: String, } impl CumulocityConverter { - pub fn new(size_threshold: SizeThreshold, device_name: String) -> Self { + pub fn new(size_threshold: SizeThreshold, device_name: String, device_type: String) -> Self { let mut topic_fiter = make_valid_topic_filter_or_panic("tedge/measurements"); let () = topic_fiter .add("tedge/measurements/+") @@ -49,6 +50,7 @@ impl CumulocityConverter { children, mapper_config, device_name, + device_type, } } @@ -124,13 +126,28 @@ impl Converter for CumulocityConverter { let supported_operations_message = create_supported_operations_fragments()?; + let device_data_message = + create_device_data_fragments(&self.device_name, &self.device_type)?; + Ok(vec![ supported_operations_message, + device_data_message, inventory_fragments_message, ]) } } +fn create_device_data_fragments( + device_name: &str, + device_type: &str, +) -> Result<Message, ConversionError> { + let device_data = C8yDeviceDataFragment::from_type(device_type)?; + let ops_msg = device_data.to_json()?; + + let topic = Topic::new_unchecked(&format!("{INVENTORY_MANAGED_OBJECTS_TOPIC}/{device_name}",)); + Ok(Message::new(&topic, ops_msg.to_string())) +} + fn create_supported_operations_fragments() -> Result<Message, ConversionError> { let ops = Operations::try_new(SUPPORTED_OPERATIONS_DIRECTORY, C8Y_CLOUD)?; let ops = ops.get_operations_list(); @@ -144,10 +161,7 @@ fn create_supported_operations_fragments() -> Result<Message, ConversionError> { fn create_inventory_fragments_message(device_name: &str) -> Result<Message, ConversionError> { let ops_msg = get_inventory_fragments(INVENTORY_FRAGMENTS_FILE_LOCATION)?; - let topic = Topic::new_unchecked(&format!( - "{}/{}", - INVENTORY_MANAGED_OBJECTS_TOPIC, device_name - )); + let topic = Topic::new_unchecked(&format!("{INVENTORY_MANAGED_OBJECTS_TOPIC}/{device_name}",)); Ok(Message::new(&topic, ops_msg.to_string())) } @@ -227,10 +241,12 @@ mod test { #[test] fn convert_thin_edge_json_with_child_id() { let device_name = String::from("test"); + let device_type = String::from("test_type"); let mut converter = Box::new(CumulocityConverter::new( SizeThreshold(16 * 1024), device_name, + device_type, )); let in_topic = "tedge/measurements/child1"; let in_payload = r#"{"temp": 1, "time": "2021-11-16T17:45:40.571760714+01:00"}"#; @@ -263,10 +279,12 @@ mod test { #[test] fn convert_first_thin_edge_json_invalid_then_valid_with_child_id() { let device_name = String::from("test"); + let device_type = String::from("test_type"); let mut converter = Box::new(CumulocityConverter::new( SizeThreshold(16 * 1024), device_name, + device_type, )); let in_topic = "tedge/measurements/child1"; let in_invalid_payload = r#"{"temp": invalid}"#; @@ -304,10 +322,12 @@ mod test { #[test] fn convert_two_thin_edge_json_messages_given_different_child_id() { let device_name = String::from("test"); + let device_type = String::from("test_type"); let mut converter = Box::new(CumulocityConverter::new( SizeThreshold(16 * 1024), device_name, + device_type, )); let in_payload = r#"{"temp": 1, "time": "2021-11-16T17:45:40.571760714+01:00"}"#; @@ -358,10 +378,14 @@ mod test { #[test] fn check_c8y_threshold_packet_size() -> Result<(), anyhow::Error> { - let size_threshold = SizeThreshold(16 * 1024); let device_name = String::from("test"); + let device_type = String::from("test_type"); - let converter = CumulocityConverter::new(size_threshold, device_name); + let converter = Box::new(CumulocityConverter::new( + SizeThreshold(16 * 1024), + device_name, + device_type, + )); let buffer = create_packet(1024 * 20); let err = converter.size_threshold.validate(&buffer).unwrap_err(); assert_eq!( diff --git a/crates/core/tedge_mapper/src/c8y_fragments.rs b/crates/core/tedge_mapper/src/c8y_fragments.rs index 3584ad8a..6536f648 100644 --- a/crates/core/tedge_mapper/src/c8y_fragments.rs +++ b/crates/core/tedge_mapper/src/c8y_fragments.rs @@ -4,10 +4,14 @@ use std::process::Command; use crate::error::ConversionError; use tracing::warn; +const DEFAULT_AGENT_FRAGMENT_NAME: &str = "thin-edge.io"; +const DEFAULT_AGENT_FRAGMENT_URL: &str = "https://thin-edge.io"; + #[derive(Debug, Serialize)] pub struct C8yAgent { name: String, version: String, + url: String, } #[derive(Debug, Serialize)] @@ -19,8 +23,9 @@ pub struct C8yAgentFragment { impl C8yAgentFragment { pub fn new() -> Result<Self, ConversionError> { let c8y_agent = C8yAgent { - name: "thin-edge.io".to_string(), + name: DEFAULT_AGENT_FRAGMENT_NAME.into(), version: get_tedge_version()?, + url: DEFAULT_AGENT_FRAGMENT_URL.into(), }; Ok(Self { c8y_agent }) } @@ -50,3 +55,23 @@ pub fn get_tedge_version() -> Result<String, ConversionError> { } } } + +#[derive(Debug, Serialize)] +pub struct C8yDeviceDataFragment { + #[serde(rename = "type")] + device_type: String, +} + +impl C8yDeviceDataFragment { + pub fn from_type(device_type: &str) -> Result<Self, ConversionError> { + Ok(Self { + device_type: device_type.into(), + }) + } + + pub fn to_json(&self) -> Result<serde_json::Value, ConversionError> { + let json_string = serde_json::to_string(&self)?; + let jsond: serde_json::Value = serde_json::from_str(&json_string)?; + Ok(jsond) + } +} diff --git a/crates/core/tedge_mapper/src/c8y_mapper.rs b/crates/core/tedge_mapper/src/c8y_mapper.rs index aa5c553e..cf38dee9 100644 --- a/crates/core/tedge_mapper/src/c8y_mapper.rs +++ b/crates/core/tedge_mapper/src/c8y_mapper.rs @@ -3,7 +3,7 @@ use crate::component::TEdgeComponent; use crate::mapper::*; use crate::size_threshold::SizeThreshold; use async_trait::async_trait; -use tedge_config::{ConfigSettingAccessor, DeviceIdSetting, TEdgeConfig}; +use tedge_config::{ConfigSettingAccessor, DeviceIdSetting, DeviceTypeSetting, TEdgeConfig}; use tracing::{info_span, Instrument}; const CUMULOCITY_MAPPER_NAME: &str = "tedge-mapper-c8y"; @@ -22,8 +22,13 @@ impl TEdgeComponent for CumulocityMapper { let size_threshold = SizeThreshold(16 * 1024); let device_name = tedge_config.query(DeviceIdSetting)?; + let device_type = tedge_config.query(DeviceTypeSetting)?; - let converter = Box::new(CumulocityConverter::new(size_threshold, device_name)); + let converter = Box::new(CumulocityConverter::new( + size_threshold, + device_name, + device_type, + )); let mut mapper = create_mapper(CUMULOCITY_MAPPER_NAME, &tedge_config, converter).await?; |