summaryrefslogtreecommitdiffstats
path: root/tedge_config
diff options
context:
space:
mode:
authorMichael Neumann <mneumann@ntecs.de>2021-04-13 15:04:30 +0200
committerGitHub <noreply@github.com>2021-04-13 15:04:30 +0200
commit697eacb6e9e851cd856b9eb792bbca6e400dd331 (patch)
tree37c2b7e236eff8519a8d66ff1879ef2e9f836003 /tedge_config
parente69dea81e85ec977c95339646aac3585c47a5a5f (diff)
[CIT-304] Migrate tedge config to use the new tedge_config crate (#168)
- Disentangle TEdgeConfig from `tedge config` CLI - Disentangle `tedge config` commands from CLI parsing - Do all the parsing in `config/cli.rs`. - Commands are defined in `config/commands/*.rs`. One (sub-)command per file. - The implementation of the commands become easier to read and understand, and you can test each command independently. - Do not list valid keys when running `tedge config [get|set|unset] --help` - When running `tedge config get --help`, the output is now: ARGS: <key> Configuration key. Run `tedge config list --doc` for available keys - Prior to this commit, a list of keys was shown after the `<key>`, but formatting was pretty ugly. - The list of valid keys was generated by the macro. Get rid of it. IMHO this was a mistake in the first place. - Instead, point the user at running `tedge config list --doc` for a list of valid keys. TODO: Currently the `tedge config list --doc` output does not mention whether a given key is read-only or not. - If we want the previous behaviour, it's better to just maintain the list of valid keys in the structopt comment. - tedge_config - Fix KEY's of some ConfigSettings These are the keys that are used in tedge/tests/main.rs. - Use crate tedge_config in `tedge config` CLI - Add `ConfigSettingAccessorStringExt` trait to `tedge_config`. We need that in order to get/set keys as strings. It provides a `query_string`, `query_string_optional` and `update_string` methods. - Provide a default implementation for `ConfigSettingAccessorStringExt` for all ConfigSettings on `TEdgeConfig`. - Introduce newtype `FilePath` and get rid of `PathBuf` in `TEdgeConfig` and `TEdgeConfigDto`. We need this because `PathBuf` does not implement `TryInto<String>` which we need for the default implementation mentioned in the previous step. - Make `TEdgeConfig` clonable. This is required when we update or unset a config setting in cli/config/commands/{set,unset}.rs. In the command we receive a `config: TEdgeConfig`, but as we are accessing it through the `&self` in `execute`, we cannot modify it. To get a mutable version of `config`, simply clone it. - For those `tedge config` commands that need to write a configuration back, inject a `TEdgeConfigRepository`. TODO: We should already inject the `TEdgeConfigRepository` in `build_command`. - Get rid of parts of the macro. We still need to keep parts of it around for the other commands. - Pass BuildCommandContext to BuildCommand::build_command - This allows to inject TEdgeConfigRepository for commands that need to write a config. - Especially useful during the migration period, where we can still provide `build_command` with an old config::TEdgeConfig. - TODO: Some tests failing. I think some of the test conditions are wrong! - Fix test - We changed the default value - When writing tedge.toml, create parent directory - This fixes the tests. - Create $HOME/.tedge or /etc/tedge if it does not exist yet. - This needs some more love. Permissions should be set correctly. - Remove underscored from trait definition - Split TEdgeConfigDefaults from TEdgeConfigLocation - `TEdgeConfigLocation` only tells us something about where the `tedge.config` is located (and about it's root directory). - `TEdgeConfigDefaults` only tells us something about the default values used in `TEdgeConfig` as fallbacks. - Both of them are independent entities. - You can derive `TEdgeConfigDefaults::from(&TEdgeConfigLocation)`. - Drop `Clone` - Use `config_repo.load()` instead of `config.clone()` - Silence a few clippy warnings - Rename BuildCommandContext to just BuildContext
Diffstat (limited to 'tedge_config')
-rw-r--r--tedge_config/src/config_setting.rs28
-rw-r--r--tedge_config/src/lib.rs5
-rw-r--r--tedge_config/src/models/file_path.rs40
-rw-r--r--tedge_config/src/models/mod.rs3
-rw-r--r--tedge_config/src/settings.rs15
-rw-r--r--tedge_config/src/tedge_config.rs63
-rw-r--r--tedge_config/src/tedge_config_defaults.rs72
-rw-r--r--tedge_config/src/tedge_config_dto.rs9
-rw-r--r--tedge_config/src/tedge_config_location.rs166
-rw-r--r--tedge_config/src/tedge_config_repository.rs29
-rw-r--r--tedge_config/tests/test_tedge_config.rs198
11 files changed, 386 insertions, 242 deletions
diff --git a/tedge_config/src/config_setting.rs b/tedge_config/src/config_setting.rs
index c34d1fc9..fde2c46c 100644
--- a/tedge_config/src/config_setting.rs
+++ b/tedge_config/src/config_setting.rs
@@ -27,6 +27,24 @@ pub trait ConfigSettingAccessor<T: ConfigSetting> {
fn unset(&mut self, _setting: T) -> ConfigSettingResult<()>;
}
+/// Extension trait that provides methods to query a setting as a String or
+/// update a setting provided a String value.
+pub trait ConfigSettingAccessorStringExt<T: ConfigSetting>: ConfigSettingAccessor<T> {
+ /// Read a configuration setting and convert it into a String.
+ fn query_string(&self, setting: T) -> ConfigSettingResult<String>;
+
+ fn query_string_optional(&self, setting: T) -> ConfigSettingResult<Option<String>> {
+ match self.query_string(setting) {
+ Ok(value) => Ok(Some(value)),
+ Err(ConfigSettingError::ConfigNotSet { .. }) => Ok(None),
+ Err(err) => Err(err),
+ }
+ }
+
+ /// Update a configuration setting from a String value
+ fn update_string(&mut self, setting: T, value: String) -> ConfigSettingResult<()>;
+}
+
pub type ConfigSettingResult<T> = Result<T, ConfigSettingError>;
#[derive(thiserror::Error, Debug)]
@@ -37,6 +55,12 @@ pub enum ConfigSettingError {
)]
ConfigNotSet { key: &'static str },
- #[error("Readonly setting")]
- ReadonlySetting,
+ #[error("Readonly setting: {message}")]
+ ReadonlySetting { message: &'static str },
+
+ #[error("Conversion from String failed")]
+ ConversionFromStringFailed,
+
+ #[error("Conversion into String failed")]
+ ConversionIntoStringFailed,
}
diff --git a/tedge_config/src/lib.rs b/tedge_config/src/lib.rs
index ce268495..2efe321c 100644
--- a/tedge_config/src/lib.rs
+++ b/tedge_config/src/lib.rs
@@ -3,10 +3,13 @@ mod error;
mod models;
mod settings;
mod tedge_config;
+mod tedge_config_defaults;
mod tedge_config_dto;
mod tedge_config_location;
mod tedge_config_repository;
use self::tedge_config_dto::*;
pub use self::{config_setting::*, error::*, models::*, settings::*};
-pub use self::{tedge_config::*, tedge_config_location::*, tedge_config_repository::*};
+pub use self::{
+ tedge_config::*, tedge_config_defaults::*, tedge_config_location::*, tedge_config_repository::*,
+};
diff --git a/tedge_config/src/models/file_path.rs b/tedge_config/src/models/file_path.rs
new file mode 100644
index 00000000..e74dff98
--- /dev/null
+++ b/tedge_config/src/models/file_path.rs
@@ -0,0 +1,40 @@
+use std::convert::TryInto;
+use std::path::PathBuf;
+
+/// Represents a path to a file or directory.
+///
+/// We need this newtype in order to implement `TryInto<String>`.
+/// `PathBuf` does not implement `TryInto<String>`.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
+#[serde(transparent)]
+pub struct FilePath(PathBuf);
+
+#[derive(thiserror::Error, Debug)]
+#[error("FilePath to String conversion failed: {0:?}")]
+pub struct FilePathToStringConversionFailure(std::ffi::OsString);
+
+impl<T> From<T> for FilePath
+where
+ PathBuf: From<T>,
+{
+ fn from(input: T) -> Self {
+ Self(PathBuf::from(input))
+ }
+}
+
+impl Into<PathBuf> for FilePath {
+ fn into(self) -> PathBuf {
+ self.0
+ }
+}
+
+impl TryInto<String> for FilePath {
+ type Error = FilePathToStringConversionFailure;
+
+ fn try_into(self) -> Result<String, FilePathToStringConversionFailure> {
+ self.0
+ .into_os_string()
+ .into_string()
+ .map_err(FilePathToStringConversionFailure)
+ }
+}
diff --git a/tedge_config/src/models/mod.rs b/tedge_config/src/models/mod.rs
index ef226c0f..ce81e3af 100644
--- a/tedge_config/src/models/mod.rs
+++ b/tedge_config/src/models/mod.rs
@@ -1,3 +1,4 @@
pub mod connect_url;
+pub mod file_path;
-pub use self::connect_url::*;
+pub use self::{connect_url::*, file_path::*};
diff --git a/tedge_config/src/settings.rs b/tedge_config/src/settings.rs
index e85c5cf2..f247cfe7 100644
--- a/tedge_config/src/settings.rs
+++ b/tedge_config/src/settings.rs
@@ -1,5 +1,4 @@
use crate::{config_setting::*, models::*};
-use std::path::PathBuf;
///
/// Identifier of the device within the fleet. It must be globally
@@ -34,7 +33,7 @@ impl ConfigSetting for DeviceKeyPathSetting {
const DESCRIPTION: &'static str =
"Path to the private key file. Example: /home/user/.tedge/tedge-private-key.pem";
- type Value = PathBuf;
+ type Value = FilePath;
}
///
@@ -51,7 +50,7 @@ impl ConfigSetting for DeviceCertPathSetting {
const DESCRIPTION: &'static str =
"Path to the certificate file. Example: /home/user/.tedge/tedge-certificate.crt";
- type Value = PathBuf;
+ type Value = FilePath;
}
///
@@ -79,14 +78,14 @@ impl ConfigSetting for C8yUrlSetting {
pub struct C8yRootCertPathSetting;
impl ConfigSetting for C8yRootCertPathSetting {
- const KEY: &'static str = "c8y.root_cert_path";
+ const KEY: &'static str = "c8y.root.cert.path";
const DESCRIPTION: &'static str = concat!(
"Path where Cumulocity root certificate(s) are located. ",
"Example: /home/user/.tedge/c8y-trusted-root-certificates.pem"
);
- type Value = PathBuf;
+ type Value = FilePath;
}
///
@@ -98,7 +97,7 @@ impl ConfigSetting for C8yRootCertPathSetting {
pub struct AzureUrlSetting;
impl ConfigSetting for AzureUrlSetting {
- const KEY: &'static str = "device.id";
+ const KEY: &'static str = "azure.url";
const DESCRIPTION: &'static str = concat!(
"Tenant endpoint URL of Azure IoT tenant. ",
@@ -117,12 +116,12 @@ impl ConfigSetting for AzureUrlSetting {
pub struct AzureRootCertPathSetting;
impl ConfigSetting for AzureRootCertPathSetting {
- const KEY: &'static str = "azure.root_cert_path";
+ const KEY: &'static str = "azure.root.cert.path";
const DESCRIPTION: &'static str = concat!(
"Path where Azure IoT root certificate(s) are located. ",
"Example: /home/user/.tedge/azure-trusted-root-certificates.pem"
);
- type Value = PathBuf;
+ type Value = FilePath;
}
diff --git a/tedge_config/src/tedge_config.rs b/tedge_config/src/tedge_config.rs
index 89d7b86f..644680bd 100644
--- a/tedge_config/src/tedge_config.rs
+++ b/tedge_config/src/tedge_config.rs
@@ -1,5 +1,5 @@
use crate::*;
-use std::path::PathBuf;
+use std::convert::{TryFrom, TryInto};
/// Represents the complete configuration of a thin edge device.
/// This configuration is a wrapper over the device specific configurations
@@ -9,6 +9,7 @@ use std::path::PathBuf;
pub struct TEdgeConfig {
pub(crate) data: TEdgeConfigDto,
pub(crate) config_location: TEdgeConfigLocation,
+ pub(crate) config_defaults: TEdgeConfigDefaults,
}
impl ConfigSettingAccessor<DeviceIdSetting> for TEdgeConfig {
@@ -23,11 +24,21 @@ impl ConfigSettingAccessor<DeviceIdSetting> for TEdgeConfig {
}
fn update(&mut self, _setting: DeviceIdSetting, _value: String) -> ConfigSettingResult<()> {
- Err(ConfigSettingError::ReadonlySetting)
+ Err(ConfigSettingError::ReadonlySetting {
+ message: concat!(
+ "Setting the device id is only allowed with `tedge cert create`.\n",
+ "To set 'device.id', use `tedge cert create --device-id <id>`."
+ ),
+ })
}
fn unset(&mut self, _setting: DeviceIdSetting) -> ConfigSettingResult<()> {
- Err(ConfigSettingError::ReadonlySetting)
+ Err(ConfigSettingError::ReadonlySetting {
+ message: concat!(
+ "Setting the device id is only allowed with `tedge cert create`.\n",
+ "To set 'device.id', use `tedge cert create --device-id <id>`."
+ ),
+ })
}
}
@@ -76,19 +87,19 @@ impl ConfigSettingAccessor<C8yUrlSetting> for TEdgeConfig {
}
impl ConfigSettingAccessor<DeviceCertPathSetting> for TEdgeConfig {
- fn query(&self, _setting: DeviceCertPathSetting) -> ConfigSettingResult<PathBuf> {
+ fn query(&self, _setting: DeviceCertPathSetting) -> ConfigSettingResult<FilePath> {
Ok(self
.data
.device
.cert_path
.clone()
- .unwrap_or_else(|| self.config_location.default_device_cert_path.clone()))
+ .unwrap_or_else(|| self.config_defaults.default_device_cert_path.clone()))
}
fn update(
&mut self,
_setting: DeviceCertPathSetting,
- value: PathBuf,
+ value: FilePath,
) -> ConfigSettingResult<()> {
self.data.device.cert_path = Some(value);
Ok(())
@@ -101,19 +112,19 @@ impl ConfigSettingAccessor<DeviceCertPathSetting> for TEdgeConfig {
}
impl ConfigSettingAccessor<DeviceKeyPathSetting> for TEdgeConfig {
- fn query(&self, _setting: DeviceKeyPathSetting) -> ConfigSettingResult<PathBuf> {
+ fn query(&self, _setting: DeviceKeyPathSetting) -> ConfigSettingResult<FilePath> {
Ok(self
.data
.device
.key_path
.clone()
- .unwrap_or_else(|| self.config_location.default_device_key_path.clone()))
+ .unwrap_or_else(|| self.config_defaults.default_device_key_path.clone()))
}
fn update(
&mut self,
_setting: DeviceKeyPathSetting,
- value: PathBuf,
+ value: FilePath,
) -> ConfigSettingResult<()> {
self.data.device.key_path = Some(value);
Ok(())
@@ -126,19 +137,19 @@ impl ConfigSettingAccessor<DeviceKeyPathSetting> for TEdgeConfig {
}
impl ConfigSettingAccessor<AzureRootCertPathSetting> for TEdgeConfig {
- fn query(&self, _setting: AzureRootCertPathSetting) -> ConfigSettingResult<PathBuf> {
+ fn query(&self, _setting: AzureRootCertPathSetting) -> ConfigSettingResult<FilePath> {
Ok(self
.data
.azure
.root_cert_path
.clone()
- .unwrap_or_else(|| self.config_location.default_azure_root_cert_path.clone()))
+ .unwrap_or_else(|| self.config_defaults.default_azure_root_cert_path.clone()))
}
fn update(
&mut self,
_setting: AzureRootCertPathSetting,
- value: PathBuf,
+ value: FilePath,
) -> ConfigSettingResult<()> {
self.data.azure.root_cert_path = Some(value);
Ok(())
@@ -151,19 +162,19 @@ impl ConfigSettingAccessor<AzureRootCertPathSetting> for TEdgeConfig {
}
impl ConfigSettingAccessor<C8yRootCertPathSetting> for TEdgeConfig {
- fn query(&self, _setting: C8yRootCertPathSetting) -> ConfigSettingResult<PathBuf> {
+ fn query(&self, _setting: C8yRootCertPathSetting) -> ConfigSettingResult<FilePath> {
Ok(self
.data
.c8y
.root_cert_path
.clone()
- .unwrap_or_else(|| self.config_location.default_c8y_root_cert_path.clone()))
+ .unwrap_or_else(|| self.config_defaults.default_c8y_root_cert_path.clone()))
}
fn update(
&mut self,
_setting: C8yRootCertPathSetting,
- value: PathBuf,
+ value: FilePath,
) -> ConfigSettingResult<()> {
self.data.c8y.root_cert_path = Some(value);
Ok(())
@@ -174,3 +185,25 @@ impl ConfigSettingAccessor<C8yRootCertPathSetting> for TEdgeConfig {
Ok(())
}
}
+
+/// Generic extension trait implementation for all `ConfigSetting`s of `TEdgeConfig`
+/// that provide `TryFrom`/`TryInto` implementations for `String`.
+impl<T, E, F> ConfigSettingAccessorStringExt<T> for TEdgeConfig
+where
+ T: ConfigSetting,
+ TEdgeConfig: ConfigSettingAccessor<T>,
+ T::Value: TryFrom<String, Error = E>,
+ T::Value: TryInto<String, Error = F>,
+{
+ fn query_string(&self, setting: T) -> ConfigSettingResult<String> {
+ self.query(setting)?
+ .try_into()
+ .map_err(|_e| ConfigSettingError::ConversionIntoStringFailed)
+ }
+
+ fn update_string(&mut self, setting: T, string_value: String) -> ConfigSettingResult<()> {
+ T::Value::try_from(string_value)
+ .map_err(|_e| ConfigSettingError::ConversionFromStringFailed)
+ .and_then(|value| self.update(setting, value))
+ }
+}
diff --git a/tedge_config/src/tedge_config_defaults.rs b/tedge_config/src/tedge_config_defaults.rs
new file mode 100644
index 00000000..6896c51b
--- /dev/null
+++ b/tedge_config/src/tedge_config_defaults.rs
@@ -0,0 +1,72 @@
+use crate::models::FilePath;
+use crate::TEdgeConfigLocation;
+use std::path::Path;
+
+const DEFAULT_ETC_PATH: &str = "/etc";
+
+/// Stores default values for use by `TEdgeConfig` in case no configuration setting
+/// is available.
+///
+/// We DO NOT base the defaults on the currently executing user. Instead, we derive
+/// the defaults from the location of the `tedge.toml` file. This allows run
+/// `sudo tedge -c '$HOME/.tedge/tedge.toml ...` where the defaults are picked up
+/// correctly.
+///
+/// The choice, where to find `tedge.toml` on the other hand is based on the executing user AND the
+/// env `$HOME`. But once we have found `tedge.toml`, we never again have to care about the
+/// executing user (except when `chown`ing files...).
+///
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TEdgeConfigDefaults {
+ /// Default device cert path
+ pub default_device_cert_path: FilePath,
+
+ /// Default device key path
+ pub default_device_key_path: FilePath,
+
+ /// Default path for azure root certificates
+ pub default_azure_root_cert_path: FilePath,
+
+ /// Default path for c8y root certificates
+ pub default_c8y_root_cert_path: FilePath,
+}
+
+impl From<&TEdgeConfigLocation> for TEdgeConfigDefaults {
+ fn from(config_location: &TEdgeConfigLocation) -> Self {
+ let system_cert_path = Path::new(DEFAULT_ETC_PATH).join("ssl").join("certs");
+ Self {
+ default_device_cert_path: config_location
+ .tedge_config_root_path()
+ .join("device-certs")
+ .join("tedge-certificate.pem")
+ .into(),
+ default_device_key_path: config_location
+ .tedge_config_root_path()
+ .join("device-certs")
+ .join("tedge-private-key.pem")
+ .into(),
+ default_azure_root_cert_path: system_cert_path.clone().into(),
+ default_c8y_root_cert_path: system_cert_path.into(),
+ }
+ }
+}
+
+#[test]
+fn test_from_tedge_config_location() {
+ let config_location = TEdgeConfigLocation::from_custom_root("/opt/etc/_tedge");
+ let defaults = TEdgeConfigDefaults::from(&config_location);
+
+ assert_eq!(
+ defaults,
+ TEdgeConfigDefaults {
+ default_device_cert_path: FilePath::from(
+ "/opt/etc/_tedge/device-certs/tedge-certificate.pem"
+ ),
+ default_device_key_path: FilePath::from(
+ "/opt/etc/_tedge/device-certs/tedge-private-key.pem"
+ ),
+ default_azure_root_cert_path: FilePath::from("/etc/ssl/certs"),
+ default_c8y_root_cert_path: FilePath::from("/etc/ssl/certs")
+ }
+ );
+}
diff --git a/tedge_config/src/tedge_config_dto.rs b/tedge_config/src/tedge_config_dto.rs
index 7f67770d..2927901c 100644
--- a/tedge_config/src/tedge_config_dto.rs
+++ b/tedge_config/src/tedge_config_dto.rs
@@ -2,7 +2,6 @@
use crate::*;
use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
#[serde(deny_unknown_fields)]
#[derive(Debug, Default, Deserialize, Serialize)]
@@ -28,11 +27,11 @@ pub(crate) struct DeviceConfigDto {
/// Path where the device's private key is stored.
/// Defaults to $HOME/.tedge/tedge-private.pem
- pub(crate) key_path: Option<PathBuf>,
+ pub(crate) key_path: Option<FilePath>,
/// Path where the device's certificate is stored.
/// Defaults to $HOME/.tedge/tedge-certificate.crt
- pub(crate) cert_path: Option<PathBuf>,
+ pub(crate) cert_path: Option<FilePath>,
}
/// Represents the Cumulocity specific configurations defined in the
@@ -48,7 +47,7 @@ pub(crate) struct CumulocityConfigDto {
/// The path where Cumulocity root certificate(s) are stored.
/// The value can be a directory path as well as the path of the direct certificate file.
- pub(crate) root_cert_path: Option<PathBuf>,
+ pub(crate) root_cert_path: Option<FilePath>,
}
#[serde(deny_unknown_fields)]
@@ -56,5 +55,5 @@ pub(crate) struct CumulocityConfigDto {
pub(crate) struct AzureConfigDto {
pub(crate) connect: Option<String>,
pub(crate) url: Option<ConnectUrl>,
- pub(crate) root_cert_path: Option<PathBuf>,
+ pub(crate) root_cert_path: Option<FilePath>,
}
diff --git a/tedge_config/src/tedge_config_location.rs b/tedge_config/src/tedge_config_location.rs
index fdf45f51..1ca2ff28 100644
--- a/tedge_config/src/tedge_config_location.rs
+++ b/tedge_config/src/tedge_config_location.rs
@@ -3,137 +3,89 @@ use std::path::{Path, PathBuf};
const DEFAULT_ETC_PATH: &str = "/etc";
const TEDGE_CONFIG_FILE: &str = "tedge.toml";
-/// Information about where `tedge.toml` is located and the defaults that are based
-/// on that location.
+/// Information about where `tedge.toml` is located.
///
/// Broadly speaking, we distinguish two different locations:
///
-/// - System-wide locations under `/etc/tedge`
+/// - System-wide locations under `/etc/tedge` or `/usr/local/etc/tedge`.
/// - User-local locations under `$HOME/.tedge`
///
-/// We DO NOT base the defaults on the currently executing user. Instead, we base
-/// the defaults on the location of the `tedge.toml` file. If it is located in
-/// `/etc`, regardless of the executing user, we use defaults that use system-wide
-/// locations (e.g. `/etc/ssl/certs`). Whereas if `tedge.toml` is located in a users
-/// home directory, we base the defaults on locations within the users home directory.
-///
-/// This allows run `sudo tedge -c '$HOME/.tedge/tedge.toml ...` where the defaults are picked up
-/// correctly.
-///
-/// The choice, where to find `tedge.toml` on the other hand is based on the executing user AND the
-/// env `$HOME`. But once we have found `tedge.toml`, we never again have to care about the
-/// executing user (except when `chown`ing files...).
-///
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TEdgeConfigLocation {
- /// Full path to `tedge.toml`.
- pub tedge_config_path: PathBuf,
-
- /// Default device cert path
- pub default_device_cert_path: PathBuf,
-
- /// Default device key path
- pub default_device_key_path: PathBuf,
-
- /// Default path for azure root certificates
- pub default_azure_root_cert_path: PathBuf,
+ /// Root directory where `tedge.toml` and other tedge related configuration files are located.
+ pub tedge_config_root_path: PathBuf,
- /// Default path for c8y root certificates
- pub default_c8y_root_cert_path: PathBuf,
+ /// Full path to the `tedge.toml` file.
+ pub tedge_config_file_path: PathBuf,
}
impl TEdgeConfigLocation {
- /// `tedge.toml` is located in `/etc/tedge`. All defaults are based on system locations.
+ pub fn from_custom_root(tedge_config_root_path: impl AsRef<Path>) -> Self {
+ Self {
+ tedge_config_root_path: tedge_config_root_path.as_ref().to_path_buf(),
+ tedge_config_file_path: tedge_config_root_path.as_ref().join(TEDGE_CONFIG_FILE),
+ }
+ }
+
+ /// `tedge.toml` is located in `/etc/tedge`.
pub fn from_default_system_location() -> Self {
- Self::from_system_location(DEFAULT_ETC_PATH)
+ Self::from_custom_root(Path::new(DEFAULT_ETC_PATH).join("tedge"))
}
- /// `tedge.toml` is located in `${etc_path}/tedge`. All defaults are based on system locations.
- pub fn from_system_location(etc_path: impl AsRef<Path>) -> Self {
- let etc_path = etc_path.as_ref();
- let tedge_path = etc_path.join("tedge");
- Self {
- tedge_config_path: tedge_path.join(TEDGE_CONFIG_FILE),
- default_device_cert_path: tedge_path
- .join("device-certs")
- .join("tedge-certificate.pem"),
- default_device_key_path: tedge_path
- .join("device-certs")
- .join("tedge-private-key.pem"),
- default_azure_root_cert_path: etc_path.join("ssl").join("certs"),
- default_c8y_root_cert_path: etc_path.join("ssl").join("certs"),
- }
+ /// `tedge.toml` is located in `${etc_path}/tedge`.
+ pub fn from_custom_etc_location(custom_etc_path: impl AsRef<Path>) -> Self {
+ Self::from_custom_root(custom_etc_path.as_ref().join("tedge"))
}
- /// `tedge.toml` is located in `$HOME/.tedge/tedge.toml`. All defaults are relative to the
- /// `$HOME/.tedge` directory.
+ /// `tedge.toml` is located in `${home_path}/.tedge`.
pub fn from_users_home_location(home_path: impl AsRef<Path>) -> Self {
- let etc_path = Path::new(DEFAULT_ETC_PATH);
- let tedge_path = home_path.as_ref().join(".tedge");
- Self {
- tedge_config_path: tedge_path.join(TEDGE_CONFIG_FILE),
- default_device_cert_path: tedge_path
- .join("device-certs")
- .join("tedge-certificate.pem"),
- default_device_key_path: tedge_path
- .join("device-certs")
- .join("tedge-private-key.pem"),
- default_azure_root_cert_path: etc_path.join("ssl").join("certs"),
- default_c8y_root_cert_path: etc_path.join("ssl").join("certs"),
- }
+ Self::from_custom_root(home_path.as_ref().join(".tedge"))
+ }
+
+ pub fn tedge_config_root_path(&self) -> &Path {
+ &self.tedge_config_root_path
+ }
+ pub fn tedge_config_file_path(&self) -> &Path {
+ &self.tedge_config_file_path
}
}
#[test]
-fn test_from_default_system_location() {
- let config_location = TEdgeConfigLocation::from_default_system_location();
- assert_eq!(
- config_location.tedge_config_path,
- PathBuf::from("/etc/tedge/tedge.toml")
- );
+fn test_from_custom_root() {
+ let config_location = TEdgeConfigLocation::from_custom_root("/opt/etc/tedge");
assert_eq!(
- config_location.default_device_cert_path,
- PathBuf::from("/etc/tedge/device-certs/tedge-certificate.pem")
+ config_location.tedge_config_root_path,
+ PathBuf::from("/opt/etc/tedge")
);
assert_eq!(
- config_location.default_device_key_path,
- PathBuf::from("/etc/tedge/device-certs/tedge-private-key.pem")
- );
- assert_eq!(
- config_location.default_azure_root_cert_path,
- PathBuf::from("/etc/ssl/certs")
- );
- assert_eq!(
- config_location.default_c8y_root_cert_path,
- PathBuf::from("/etc/ssl/certs")
+ config_location.tedge_config_file_path,
+ PathBuf::from("/opt/etc/tedge/tedge.toml")
);
}
#[test]
-fn test_from_system_location() {
- // "/usr/local/etc" is often used for installed services on FreeBSD
- let config_location = TEdgeConfigLocation::from_system_location("/usr/local/etc");
- assert_eq!(
- config_location.tedge_config_path,
- PathBuf::from("/usr/local/etc/tedge/tedge.toml")
- );
+fn test_from_default_system_location() {
+ let config_location = TEdgeConfigLocation::from_default_system_location();
assert_eq!(
- config_location.default_device_cert_path,
- PathBuf::from("/usr/local/etc/tedge/device-certs/tedge-certificate.pem")
+ config_location.tedge_config_root_path,
+ PathBuf::from("/etc/tedge")
);
assert_eq!(
- config_location.default_device_key_path,
- PathBuf::from("/usr/local/etc/tedge/device-certs/tedge-private-key.pem")
+ config_location.tedge_config_file_path,
+ PathBuf::from("/etc/tedge/tedge.toml")
);
- // XXX: This should actually be "/etc/ssl/certs".
+}
+
+#[test]
+fn test_from_custom_etc_location() {
+ let config_location = TEdgeConfigLocation::from_custom_etc_location("/usr/local/etc");
assert_eq!(
- config_location.default_azure_root_cert_path,
- PathBuf::from("/usr/local/etc/ssl/certs")
+ config_location.tedge_config_root_path,
+ PathBuf::from("/usr/local/etc/tedge")
);
- // XXX: This should actually be "/etc/ssl/certs".
assert_eq!(
- config_location.default_c8y_root_cert_path,
- PathBuf::from("/usr/local/etc/ssl/certs")
+ config_location.tedge_config_file_path,
+ PathBuf::from("/usr/local/etc/tedge/tedge.toml")
);
}
@@ -141,23 +93,11 @@ fn test_from_system_location() {
fn test_from_users_home_location() {
let config_location = TEdgeConfigLocation::from_users_home_location("/home/user");
assert_eq!(
- config_location.tedge_config_path,
- PathBuf::from("/home/user/.tedge/tedge.toml")
- );
- assert_eq!(
- config_location.default_device_cert_path,
- PathBuf::from("/home/user/.tedge/device-certs/tedge-certificate.pem")
+ config_location.tedge_config_root_path,
+ PathBuf::from("/home/user/.tedge")
);
assert_eq!(
- config_location.default_device_key_path,
- PathBuf::from("/home/user/.tedge/device-certs/tedge-private-key.pem")
- );
- assert_eq!(
- config_location.default_azure_root_cert_path,
- PathBuf::from("/etc/ssl/certs")
- );
- assert_eq!(
- config_location.default_c8y_root_cert_path,
- PathBuf::from("/etc/ssl/certs")
+ config_location.tedge_config_file_path,
+ PathBuf::from("/home/user/.tedge/tedge.toml")
);
}
diff --git a/tedge_config/src/tedge_config_repository.rs b/tedge_config/src/tedge_config_repository.rs
index 97e60ecd..c7fdd2b2 100644
--- a/tedge_config/src/tedge_config_repository.rs
+++ b/tedge_config/src/tedge_config_repository.rs
@@ -7,6 +7,7 @@ use tempfile::NamedTempFile;
///
pub struct TEdgeConfigRepository {
config_location: TEdgeConfigLocation,
+ config_defaults: TEdgeConfigDefaults,
}
pub trait ConfigRepository<T> {
@@ -19,7 +20,8 @@ impl ConfigRepository<TEdgeConfig> for TEdgeConfigRepository {
type Error = TEdgeConfigError;
fn load(&self) -> Result<TEdgeConfig, TEdgeConfigError> {
- let config = self.read_file_or_default(self.config_location.tedge_config_path.clone())?;
+ let config =
+ self.read_file_or_default(self.config_location.tedge_config_file_path().into())?;
Ok(config)
}
@@ -28,7 +30,14 @@ impl ConfigRepository<TEdgeConfig> for TEdgeConfigRepository {
let toml = toml::to_string_pretty(&config.data)?;
let mut file = NamedTempFile::new()?;
file.write_all(toml.as_bytes())?;
- match file.persist(self.config_location.tedge_config_path.clone()) {
+
+ // Create $HOME/.tedge or /etc/tedge directory if it does not exist
+ if !self.config_location.tedge