From f0ddba329cbbb495580c5b183b48f18f48f44501 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Sun, 26 Jan 2020 21:49:43 +0100 Subject: [PATCH] [ref+feat] conf: split in several files & add global conf from env vars --- Cargo.lock | 18 + lib/core/conf/Cargo.toml | 5 + lib/core/conf/src/constants.rs | 18 +- lib/core/conf/src/env.rs | 126 ++++ lib/core/conf/src/errors.rs | 65 ++ lib/core/conf/src/file.rs | 172 ++++++ lib/core/conf/src/global_conf.rs | 56 ++ lib/core/conf/src/global_conf/v2.rs | 107 ++++ lib/core/conf/src/keypairs.rs | 213 +++++++ .../conf/src/{keys.rs => keypairs/cli.rs} | 28 +- lib/core/conf/src/lib.rs | 583 ++---------------- lib/core/conf/src/modules_conf.rs | 46 ++ lib/core/conf/src/resources.rs | 53 ++ lib/core/conf/src/v1.rs | 48 ++ lib/core/core/src/change_conf.rs | 2 +- lib/core/core/src/commands/keys.rs | 2 +- lib/core/core/src/errors.rs | 8 +- lib/core/core/src/lib.rs | 4 +- lib/core/core/src/router.rs | 4 +- lib/core/module/src/lib.rs | 12 +- .../blockchain/blockchain/src/sync/mod.rs | 2 +- 21 files changed, 1006 insertions(+), 566 deletions(-) create mode 100644 lib/core/conf/src/env.rs create mode 100644 lib/core/conf/src/errors.rs create mode 100644 lib/core/conf/src/file.rs create mode 100644 lib/core/conf/src/global_conf.rs create mode 100644 lib/core/conf/src/global_conf/v2.rs create mode 100644 lib/core/conf/src/keypairs.rs rename lib/core/conf/src/{keys.rs => keypairs/cli.rs} (94%) create mode 100644 lib/core/conf/src/modules_conf.rs create mode 100644 lib/core/conf/src/resources.rs create mode 100644 lib/core/conf/src/v1.rs diff --git a/Cargo.lock b/Cargo.lock index 83097552..e020f1ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -998,8 +998,11 @@ dependencies = [ "dup-crypto 0.7.0", "durs-common-tools 0.2.0", "durs-module 0.3.0-dev", + "envy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1335,6 +1338,14 @@ dependencies = [ "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "envy" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" version = "0.1.6" @@ -1988,6 +1999,11 @@ name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "opaque-debug" version = "0.2.3" @@ -3132,6 +3148,7 @@ dependencies = [ "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" "checksum enum-as-inner 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "900a6c7fbe523f4c2884eaf26b57b81bb69b6810a01a236390a7ac021d09492e" +"checksum envy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f938a4abd5b75fe3737902dbc2e79ca142cc1526827a9e40b829a086758531a9" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" @@ -3207,6 +3224,7 @@ dependencies = [ "checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" "checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" diff --git a/lib/core/conf/Cargo.toml b/lib/core/conf/Cargo.toml index f66ac63f..3449896a 100644 --- a/lib/core/conf/Cargo.toml +++ b/lib/core/conf/Cargo.toml @@ -16,6 +16,7 @@ dubp-currency-params = { path = "../../dubp/currency-params" } dubp-user-docs= { path = "../../dubp/user-docs" } durs-module = { path = "../module" } durs-common-tools = { path = "../../tools/common-tools" } +envy = "0.4.1" failure = "0.1.5" log = "0.4.*" rpassword = "4.0.3" @@ -24,4 +25,8 @@ serde_derive = "1.0.*" serde_json = "1.0.*" unwrap = "1.2.1" +[dev-dependencies] +maplit = "1.0.2" +once_cell = "1.3.1" + [features] diff --git a/lib/core/conf/src/constants.rs b/lib/core/conf/src/constants.rs index 7f0179cd..d2d323b5 100644 --- a/lib/core/conf/src/constants.rs +++ b/lib/core/conf/src/constants.rs @@ -15,20 +15,26 @@ //! Dunitrust configuration constants -/// User datas folder +/// User datas folder. pub static USER_DATAS_FOLDER: &str = "durs-dev"; -/// Configuration filename +/// Configuration filename. pub static CONF_FILENAME: &str = "conf.json"; -/// Keypairs filename +/// Keypairs filename. pub static KEYPAIRS_FILENAME: &str = "keypairs.json"; -/// If no currency is specified by the user, is the currency will be chosen by default +/// If no currency is specified by the user, is the currency will be chosen by default. pub static DEFAULT_CURRENCY: &str = "g1"; -/// Default value for `default_sync_module` conf field +/// Default value for `default_sync_module` conf field. pub static DEFAULT_DEFAULT_SYNC_MODULE: &str = "ws2p"; -/// Modules datas folder +/// Modules datas folder. pub static MODULES_DATAS_FOLDER: &str = "datas"; + +/// Prefix for dunitrust environment variables. +pub static DURS_ENV_PREFIX: &str = "DURS_"; + +/// Name of the environment variable that indicates the version of the configuration. +pub static DURS_CONF_VERSION: &str = "DURS_CONF_VERSION"; diff --git a/lib/core/conf/src/env.rs b/lib/core/conf/src/env.rs new file mode 100644 index 00000000..27c2e099 --- /dev/null +++ b/lib/core/conf/src/env.rs @@ -0,0 +1,126 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust configuration from environment variables + +use crate::constants; +use crate::errors::DursConfEnvError; +use crate::global_conf::v2::DuRsGlobalUserConfV2; +use crate::global_conf::DuRsGlobalUserConf; + +/// Load global user configuration from environment variables +pub fn load_env_global_user_conf() -> Result<DuRsGlobalUserConf, DursConfEnvError> { + if let Ok(conf_version) = std::env::var(constants::DURS_CONF_VERSION) { + match conf_version + .parse::<usize>() + .map_err(DursConfEnvError::ConfVersionParseErr)? + { + 2 => Ok(DuRsGlobalUserConf::V2( + envy::prefixed(constants::DURS_ENV_PREFIX) + .from_env::<DuRsGlobalUserConfV2>() + .map_err(DursConfEnvError::EnvyErr)?, + )), + v => Err(DursConfEnvError::UnsupportedVersion { + expected: vec![2], + found: v, + }), + } + } else { + Ok(DuRsGlobalUserConf::V2(DuRsGlobalUserConfV2::default())) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use dubp_currency_params::CurrencyName; + use durs_module::ModuleName; + use maplit::hashset; + use once_cell::sync::Lazy; + use std::sync::Mutex; + + // Empty mutex used to ensure that only one test runs at a time + static MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(())); + + #[test] + fn test_env_conf_without_env_vars() -> Result<(), DursConfEnvError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + + std::env::remove_var(constants::DURS_CONF_VERSION); + + assert_eq!( + DuRsGlobalUserConf::V2(DuRsGlobalUserConfV2::default()), + load_env_global_user_conf()?, + ); + + Ok(()) + } + + #[test] + fn test_env_conf_with_unsupported_conf_version_var() -> Result<(), DursConfEnvError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + + std::env::set_var(constants::DURS_CONF_VERSION, "3"); + + if let Err(DursConfEnvError::UnsupportedVersion { .. }) = load_env_global_user_conf() { + Ok(()) + } else { + panic!("load_env_global_user_conf() must return an error DursConfEnvError::UnsupportedVersion."); + } + } + + #[test] + fn test_env_conf_with_some_valid_env_vars() -> Result<(), DursConfEnvError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + + std::env::set_var(constants::DURS_CONF_VERSION, "2"); + std::env::set_var(&format!("{}CURRENCY", constants::DURS_ENV_PREFIX), "g1"); + std::env::set_var( + &format!("{}DISABLED", constants::DURS_ENV_PREFIX), + "tui,gva", + ); + + assert_eq!( + DuRsGlobalUserConf::V2(DuRsGlobalUserConfV2 { + currency: Some(CurrencyName(String::from("g1"))), + my_node_id: None, + default_sync_module: None, + ressources_usage: None, + disabled: Some(hashset![ + ModuleName("tui".to_owned()), + ModuleName("gva".to_owned()) + ]), + enabled: None, + }), + load_env_global_user_conf()?, + ); + + Ok(()) + } + + #[test] + fn test_env_conf_with_invalid_conf_version_var() -> Result<(), DursConfEnvError> { + let _lock = MUTEX.lock().expect("MUTEX poisoned"); + + std::env::set_var(constants::DURS_CONF_VERSION, "str"); + + if let Err(DursConfEnvError::ConfVersionParseErr(_)) = load_env_global_user_conf() { + Ok(()) + } else { + panic!("load_env_global_user_conf() must return an error DursConfEnvError::ConfVersionParseErr."); + } + } +} diff --git a/lib/core/conf/src/errors.rs b/lib/core/conf/src/errors.rs new file mode 100644 index 00000000..18884dc0 --- /dev/null +++ b/lib/core/conf/src/errors.rs @@ -0,0 +1,65 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust configuration errors + +use failure::Fail; + +/// Error with configuration file +#[derive(Debug, Fail)] +pub enum DursConfError { + /// Env var error + #[fail(display = "fail to parse configuration file: {}", _0)] + EnvVarErr(DursConfEnvError), + /// File error + #[fail(display = "{}", _0)] + FileErr(DursConfFileError), +} + +/// Error with configuration file +#[derive(Debug, Fail)] +pub enum DursConfEnvError { + /// Fail to parse conf version + #[fail(display = "Fail to parse conf version : {}.", _0)] + ConfVersionParseErr(std::num::ParseIntError), + /// Envy error + #[fail(display = "{}", _0)] + EnvyErr(envy::Error), + /// Unsupported version + #[fail( + display = "Version {} not supported. List of supported versions : {:?}.", + found, expected + )] + UnsupportedVersion { + /// List of supported versions + expected: Vec<usize>, + /// Version found + found: usize, + }, +} + +/// Error with configuration file +#[derive(Debug, Fail)] +pub enum DursConfFileError { + /// Read error + #[fail(display = "fail to read configuration file: {}", _0)] + ReadError(std::io::Error), + /// Parse error + #[fail(display = "fail to parse configuration file: {}", _0)] + ParseError(serde_json::Error), + /// Write error + #[fail(display = "fail to write configuration file: {}", _0)] + WriteError(std::io::Error), +} diff --git a/lib/core/conf/src/file.rs b/lib/core/conf/src/file.rs new file mode 100644 index 00000000..bbdab08a --- /dev/null +++ b/lib/core/conf/src/file.rs @@ -0,0 +1,172 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust configuration file + +use crate::constants; +use crate::errors::DursConfFileError; +use crate::DuRsConf; +use durs_module::DursConfTrait; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; + +#[inline] +/// Return path to configuration file +pub fn get_conf_path(profile_path: &PathBuf) -> PathBuf { + let mut conf_path = profile_path.clone(); + conf_path.push(constants::CONF_FILENAME); + conf_path +} + +/// Load configuration from file +pub fn load_conf_from_file(mut conf_file_path: PathBuf) -> Result<DuRsConf, DursConfFileError> { + // Open conf file + conf_file_path.push(constants::CONF_FILENAME); + if conf_file_path.as_path().exists() { + match File::open(conf_file_path.as_path()) { + Ok(mut f) => { + let mut contents = String::new(); + f.read_to_string(&mut contents) + .map_err(DursConfFileError::ReadError)?; + // Parse conf file + let conf: DuRsConf = + serde_json::from_str(&contents).map_err(DursConfFileError::ParseError)?; + // Upgrade conf to latest version + let (conf, upgraded) = conf.upgrade(); + // If conf is upgraded, rewrite conf file + if upgraded { + write_conf_file(conf_file_path.as_path(), &conf) + .map_err(DursConfFileError::WriteError)?; + } + Ok(conf) + } + Err(e) => Err(DursConfFileError::ReadError(e)), + } + } else { + // Create conf file with default conf + let conf = DuRsConf::default(); + write_conf_file(conf_file_path.as_path(), &conf) + .unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to write default conf file!"))); + Ok(conf) + } +} + +/// Save configuration in profile folder +pub fn write_conf_file<DC: DursConfTrait>( + conf_path: &Path, + conf: &DC, +) -> Result<(), std::io::Error> { + let mut f = File::create(conf_path)?; + f.write_all( + serde_json::to_string_pretty(conf) + .expect("Fatal error : fail to write default conf file !") + .as_bytes(), + )?; + f.sync_all()?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[inline] + fn save_old_conf(profile_path: PathBuf) -> std::io::Result<()> { + let mut conf_path = profile_path.clone(); + conf_path.push(constants::CONF_FILENAME); + let mut conf_sav_path = profile_path; + conf_sav_path.push("conf-sav.json"); + std::fs::copy(conf_path.as_path(), conf_sav_path.as_path())?; + Ok(()) + } + + fn restore_old_conf_and_save_upgraded_conf(profile_path: PathBuf) -> std::io::Result<()> { + let mut conf_path = profile_path.clone(); + conf_path.push(constants::CONF_FILENAME); + let mut conf_sav_path = profile_path.clone(); + conf_sav_path.push("conf-sav.json"); + let mut conf_upgraded_path = profile_path; + conf_upgraded_path.push("conf-upgraded.json"); + std::fs::copy(conf_path.as_path(), &conf_upgraded_path.as_path())?; + std::fs::copy(conf_sav_path.as_path(), &conf_path.as_path())?; + std::fs::remove_file(conf_sav_path.as_path())?; + Ok(()) + } + + #[test] + fn load_conf_file_v1() -> Result<(), DursConfFileError> { + let profile_path = PathBuf::from("./test/v1/"); + save_old_conf(PathBuf::from(profile_path.clone())) + .map_err(DursConfFileError::WriteError)?; + let conf = load_conf_from_file(profile_path.clone())?; + assert_eq!( + conf.modules() + .get("ws2p") + .expect("Not found ws2p conf") + .clone(), + json!({ + "sync_endpoints": [ + { + "endpoint": "WS2P c1c39a0a i3.ifee.fr 80 /ws2p", + "pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx" + }, + { + "endpoint": "WS2P 15af24db g1.ifee.fr 80 /ws2p", + "pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG" + }, + { + "endpoint": "WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p", + "pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef" + } + ] + }) + ); + restore_old_conf_and_save_upgraded_conf(profile_path) + .map_err(DursConfFileError::WriteError)?; + + Ok(()) + } + + #[test] + fn load_conf_file_v2() -> Result<(), DursConfFileError> { + let profile_path = PathBuf::from("./test/v2/"); + let conf = load_conf_from_file(profile_path)?; + assert_eq!( + conf.modules() + .get("ws2p") + .expect("Not found ws2p conf") + .clone(), + json!({ + "sync_endpoints": [ + { + "endpoint": "WS2P c1c39a0a i3.ifee.fr 80 /ws2p", + "pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx" + }, + { + "endpoint": "WS2P 15af24db g1.ifee.fr 80 /ws2p", + "pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG" + }, + { + "endpoint": "WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p", + "pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef" + } + ] + }) + ); + Ok(()) + } +} diff --git a/lib/core/conf/src/global_conf.rs b/lib/core/conf/src/global_conf.rs new file mode 100644 index 00000000..07b03afe --- /dev/null +++ b/lib/core/conf/src/global_conf.rs @@ -0,0 +1,56 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust global configuration + +pub mod v2; + +use durs_common_tools::fatal_error; +use durs_module::{DursGlobalConfTrait, ModuleName}; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +/// Dunitrust global configuration (without modules configuration) +pub enum DuRsGlobalConf { + /// Dunitrust global configuration v1 + V1(crate::v1::DuRsConfV1), + /// Dunitrust global configuration v2 + V2(v2::DuRsGlobalConfV2), +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +/// Dunitrust global configuration (without modules configuration) +pub enum DuRsGlobalUserConf { + /// Dunitrust global user configuration v2 + V2(v2::DuRsGlobalUserConfV2), +} + +impl DursGlobalConfTrait for DuRsGlobalConf { + type GlobalUserConf = DuRsGlobalUserConf; + + fn my_node_id(&self) -> u32 { + match *self { + DuRsGlobalConf::V1(ref conf_v1) => conf_v1.my_node_id, + DuRsGlobalConf::V2(ref conf_v2) => conf_v2.my_node_id, + } + } + fn default_sync_module(&self) -> ModuleName { + match *self { + DuRsGlobalConf::V1(_) => { + fatal_error!("Feature default_sync_module not exist in durs conf v1 !") + } + DuRsGlobalConf::V2(ref conf_v2) => conf_v2.default_sync_module.clone(), + } + } +} diff --git a/lib/core/conf/src/global_conf/v2.rs b/lib/core/conf/src/global_conf/v2.rs new file mode 100644 index 00000000..c4c7dd9b --- /dev/null +++ b/lib/core/conf/src/global_conf/v2.rs @@ -0,0 +1,107 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust global configuration V2 + +use crate::constants; +use crate::resources::ResourcesUsage; +use crate::v1::DuRsConfV1; +use dubp_currency_params::CurrencyName; +use durs_module::ModuleName; +use std::collections::HashSet; + +#[derive(Debug, Default, Clone, Deserialize, PartialEq, Serialize)] +/// Dunitrust configuration v2 +pub struct DuRsGlobalUserConfV2 { + /// Currency name + pub currency: Option<CurrencyName>, + /// Node unique identifier + pub my_node_id: Option<u32>, + /// Name of the module used by default for synchronization + pub default_sync_module: Option<ModuleName>, + /// Ressources usage + pub ressources_usage: Option<ResourcesUsage>, + /// Disabled modules + pub disabled: Option<HashSet<ModuleName>>, + /// Enabled modules + pub enabled: Option<HashSet<ModuleName>>, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +/// Dunitrust configuration v2 +pub struct DuRsGlobalConfV2 { + /// Currency name + pub currency: CurrencyName, + /// Duniter node unique identifier + pub my_node_id: u32, + /// Name of the module used by default for synchronization + pub default_sync_module: ModuleName, + /// Ressources usage + pub ressources_usage: ResourcesUsage, + /// Disabled modules + pub disabled: HashSet<ModuleName>, + /// Enabled modules + pub enabled: HashSet<ModuleName>, +} + +impl Default for DuRsGlobalConfV2 { + fn default() -> Self { + DuRsGlobalConfV2 { + currency: CurrencyName(String::from(constants::DEFAULT_CURRENCY)), + my_node_id: crate::generate_random_node_id(), + default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)), + ressources_usage: ResourcesUsage::default(), + disabled: HashSet::with_capacity(0), + enabled: HashSet::with_capacity(0), + } + } +} + +impl From<DuRsConfV1> for DuRsGlobalConfV2 { + fn from(conf_v1: DuRsConfV1) -> Self { + DuRsGlobalConfV2 { + currency: conf_v1.currency, + my_node_id: conf_v1.my_node_id, + default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)), + ressources_usage: ResourcesUsage::default(), + disabled: conf_v1.disabled, + enabled: conf_v1.enabled, + } + } +} + +impl DuRsGlobalConfV2 { + /// Override configuration with user configuration + pub fn r#override(self, global_user_conf: DuRsGlobalUserConfV2) -> Self { + DuRsGlobalConfV2 { + currency: global_user_conf.currency.unwrap_or(self.currency), + my_node_id: global_user_conf.my_node_id.unwrap_or(self.my_node_id), + default_sync_module: global_user_conf + .default_sync_module + .unwrap_or(self.default_sync_module), + ressources_usage: global_user_conf + .ressources_usage + .unwrap_or(self.ressources_usage), + disabled: global_user_conf.disabled.unwrap_or(self.disabled), + enabled: global_user_conf.enabled.unwrap_or(self.enabled), + } + } +} + +impl From<DuRsGlobalUserConfV2> for DuRsGlobalConfV2 { + fn from(global_user_conf: DuRsGlobalUserConfV2) -> Self { + Self::default().r#override(global_user_conf) + } +} diff --git a/lib/core/conf/src/keypairs.rs b/lib/core/conf/src/keypairs.rs new file mode 100644 index 00000000..6c6f2efb --- /dev/null +++ b/lib/core/conf/src/keypairs.rs @@ -0,0 +1,213 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust keypairs + +pub mod cli; + +use crate::constants; +use crate::errors::DursConfError; +use dup_crypto::keys::*; +use durs_module::{RequiredKeys, RequiredKeysContent}; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; + +#[derive(Debug, Clone, PartialEq, Eq)] +/// Keypairs filled in by the user (via a file or by direct entry in the terminal). +pub struct DuniterKeyPairs { + /// Keypair used by the node to sign its communications with other nodes. This keypair is mandatory, if it's not filled in, a random keypair is generated. + pub network_keypair: KeyPairEnum, + /// Keypair used to sign the blocks forged by this node. If this keypair is'nt filled in, the node will not calculate blocks. + pub member_keypair: Option<KeyPairEnum>, +} + +impl Serialize for DuniterKeyPairs { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let member_seed = if let Some(ref member_keypair) = self.member_keypair { + member_keypair.seed().to_string() + } else { + String::from("") + }; + let member_pub = if let Some(ref member_keypair) = self.member_keypair { + member_keypair.public_key().to_string() + } else { + String::from("") + }; + let mut state = serializer.serialize_struct("DuniterKeyPairs", 4)?; + state.serialize_field( + "network_seed", + &self.network_keypair.seed().to_string().as_str(), + )?; + state.serialize_field( + "network_pub", + &self.network_keypair.public_key().to_string().as_str(), + )?; + state.serialize_field("member_seed", member_seed.as_str())?; + state.serialize_field("member_pub", member_pub.as_str())?; + state.end() + } +} + +impl DuniterKeyPairs { + /// Returns only the keys indicated as required + pub fn get_required_keys_content( + required_keys: RequiredKeys, + keypairs: DuniterKeyPairs, + ) -> RequiredKeysContent { + match required_keys { + RequiredKeys::MemberKeyPair() => { + RequiredKeysContent::MemberKeyPair(keypairs.member_keypair) + } + RequiredKeys::MemberPublicKey() => { + RequiredKeysContent::MemberPublicKey(if let Some(keys) = keypairs.member_keypair { + Some(keys.public_key()) + } else { + None + }) + } + RequiredKeys::NetworkKeyPair() => { + RequiredKeysContent::NetworkKeyPair(keypairs.network_keypair) + } + RequiredKeys::NetworkPublicKey() => { + RequiredKeysContent::NetworkPublicKey(keypairs.network_keypair.public_key()) + } + RequiredKeys::None() => RequiredKeysContent::None(), + } + } +} + +/// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic ! +fn generate_random_keypair(algo: KeysAlgo) -> KeyPairEnum { + match algo { + KeysAlgo::Ed25519 => KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair::generate_random()), + KeysAlgo::Schnorr => panic!("Schnorr algo not yet supported !"), + } +} + +/// Save keypairs in profile folder +// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic ! +pub fn write_keypairs_file( + file_path: &PathBuf, + keypairs: &DuniterKeyPairs, +) -> Result<(), std::io::Error> { + let mut f = File::create(file_path.as_path())?; + f.write_all( + serde_json::to_string_pretty(keypairs) + .unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to deserialize keypairs !"))) + .as_bytes(), + )?; + f.sync_all()?; + Ok(()) +} + +/// Load keypairs from file +pub fn load_keypairs_from_file( + profile_path: &PathBuf, + keypairs_file_path: &Option<PathBuf>, +) -> Result<DuniterKeyPairs, DursConfError> { + // Get KeyPairs + let keypairs_path = if let Some(ref keypairs_file_path) = keypairs_file_path { + keypairs_file_path.clone() + } else { + let mut keypairs_path = profile_path.clone(); + keypairs_path.push(constants::KEYPAIRS_FILENAME); + keypairs_path + }; + if keypairs_path.as_path().exists() { + if let Ok(mut f) = File::open(keypairs_path.as_path()) { + let mut contents = String::new(); + if f.read_to_string(&mut contents).is_ok() { + let json_conf: serde_json::Value = + serde_json::from_str(&contents).expect("Conf: Fail to parse keypairs file !"); + + if let Some(network_seed) = json_conf.get("network_seed") { + if let Some(network_pub) = json_conf.get("network_pub") { + let network_seed = network_seed + .as_str() + .expect("Conf: Fail to parse keypairs file !"); + let network_pub = network_pub + .as_str() + .expect("Conf: Fail to parse keypairs file !"); + let network_keypair = KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair { + seed: Seed32::from_base58(network_seed) + .expect("conf : keypairs file : fail to parse network_seed !"), + pubkey: ed25519::PublicKey::from_base58(network_pub) + .expect("conf : keypairs file : fail to parse network_pub !"), + }); + + let member_keypair = if let Some(member_seed) = json_conf.get("member_seed") + { + if let Some(member_pub) = json_conf.get("member_pub") { + let member_seed = member_seed + .as_str() + .expect("Conf: Fail to parse keypairs file !"); + let member_pub = member_pub + .as_str() + .expect("Conf: Fail to parse keypairs file !"); + if member_seed.is_empty() || member_pub.is_empty() { + None + } else { + Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair { + seed: Seed32::from_base58(member_seed).expect( + "conf : keypairs file : fail to parse member_seed !", + ), + pubkey: ed25519::PublicKey::from_base58(member_pub).expect( + "conf : keypairs file : fail to parse member_pub !", + ), + })) + } + } else { + panic!("Fatal error : keypairs file wrong format : no field member_pub !") + } + } else { + panic!( + "Fatal error : keypairs file wrong format : no field member_seed !" + ) + }; + + // Return keypairs + Ok(DuniterKeyPairs { + network_keypair, + member_keypair, + }) + } else { + panic!("Fatal error : keypairs file wrong format : no field salt !") + } + } else { + panic!("Fatal error : keypairs file wrong format : no field password !") + } + } else { + panic!("Fail to read keypairs file !"); + } + } else { + panic!("Fail to open keypairs file !"); + } + } else { + // Create keypairs file with random keypair + let keypairs = DuniterKeyPairs { + network_keypair: generate_random_keypair(KeysAlgo::Ed25519), + member_keypair: None, + }; + write_keypairs_file(&keypairs_path, &keypairs).unwrap_or_else(|_| { + panic!(dbg!("Fatal error : fail to write default keypairs file !")) + }); + Ok(keypairs) + } +} diff --git a/lib/core/conf/src/keys.rs b/lib/core/conf/src/keypairs/cli.rs similarity index 94% rename from lib/core/conf/src/keys.rs rename to lib/core/conf/src/keypairs/cli.rs index 86a3b110..22b5c245 100644 --- a/lib/core/conf/src/keys.rs +++ b/lib/core/conf/src/keypairs/cli.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -//! Dunitrust keys configuration module +//! Dunitrust keypairs cli commands #![deny( missing_docs, @@ -75,23 +75,21 @@ pub fn modify_member_keys( pub fn clear_keys(network: bool, member: bool, key_pairs: DuniterKeyPairs) -> DuniterKeyPairs { inner_clear_keys( if network { - match question_prompt("Clear your network keypair?", &["y", "n"]) { - Ok(answer) if answer == "y" => { - println!("Generating a new network keypair!"); - true - } - _ => false, + if let Ok("y") = question_prompt("Clear your network keypair?", &["y", "n"]) { + println!("Generating a new network keypair!"); + true + } else { + false } } else { false }, if member { - match question_prompt("Clear your member keypair?", &["y", "n"]) { - Ok(answer) if answer == "y" => { - println!("Deleting member keypair!"); - true - } - _ => false, + if let Ok("y") = question_prompt("Clear your member keypair?", &["y", "n"]) { + println!("Deleting member keypair!"); + true + } else { + false } } else { false @@ -107,7 +105,7 @@ fn inner_clear_keys( mut key_pairs: DuniterKeyPairs, ) -> DuniterKeyPairs { if network { - key_pairs.network_keypair = generate_random_keypair(KeysAlgo::Ed25519); + key_pairs.network_keypair = super::generate_random_keypair(KeysAlgo::Ed25519); } if member { key_pairs.member_keypair = None @@ -137,7 +135,7 @@ pub fn save_keypairs( conf_keys_path.push(crate::constants::KEYPAIRS_FILENAME); conf_keys_path }; - write_keypairs_file(&conf_keys_path, &key_pairs)?; + super::write_keypairs_file(&conf_keys_path, &key_pairs)?; Ok(()) } diff --git a/lib/core/conf/src/lib.rs b/lib/core/conf/src/lib.rs index d6fbc76e..0cf9a602 100644 --- a/lib/core/conf/src/lib.rs +++ b/lib/core/conf/src/lib.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -//! Dunitrust configuration files properties module +//! Dunitrust configuration module #![deny( clippy::option_unwrap_used, @@ -35,23 +35,30 @@ extern crate log; extern crate serde_derive; pub mod constants; -pub mod keys; +mod env; +pub mod errors; +pub mod file; +mod global_conf; +pub mod keypairs; +mod modules_conf; +mod resources; +mod v1; + +pub use crate::errors::DursConfError; +pub use crate::keypairs::DuniterKeyPairs; use crate::constants::MODULES_DATAS_FOLDER; +use crate::global_conf::v2::DuRsGlobalConfV2; +use crate::global_conf::{DuRsGlobalConf, DuRsGlobalUserConf}; +use crate::modules_conf::ModulesConf; use dubp_currency_params::CurrencyName; use dup_crypto::keys::*; use dup_crypto::rand; use durs_common_tools::fatal_error; -use durs_module::{ - DursConfTrait, DursGlobalConfTrait, ModuleName, RequiredKeys, RequiredKeysContent, -}; -use failure::Fail; -use serde::ser::{Serialize, SerializeStruct, Serializer}; +use durs_module::{DursConfTrait, DursGlobalConfTrait, ModuleName}; use std::collections::HashSet; use std::fs; -use std::fs::File; -use std::io::prelude::*; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[derive(Debug, Clone)] /// User request on global conf @@ -66,185 +73,25 @@ pub enum ChangeGlobalConf { None(), } -#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] -/// Modules conf -pub struct ModulesConf(pub serde_json::Value); - -impl Default for ModulesConf { - #[inline] - fn default() -> Self { - ModulesConf(serde_json::Value::Null) - } -} - -impl ModulesConf { - /// Change module conf - pub fn set_module_conf(&mut self, module_name: ModuleName, new_module_conf: serde_json::Value) { - if self.0.is_null() { - let mut new_modules_conf = serde_json::Map::with_capacity(1); - new_modules_conf.insert(module_name.0, new_module_conf); - self.0 = serde_json::value::to_value(new_modules_conf) - .expect("Fail to create map of new modules conf !"); - } else { - self.0 - .as_object_mut() - .expect("Conf file currupted !") - .insert(module_name.0, new_module_conf); - } - } -} - -#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] -/// Duniter configuration v1 -pub struct DuRsConfV1 { - /// Currency name - pub currency: CurrencyName, - /// Duniter node unique identifier - pub my_node_id: u32, - /// Configuration of modules in json format (obtained from the conf.json file) - pub modules: ModulesConf, - /// Disabled modules - pub disabled: HashSet<ModuleName>, - /// Enabled modules - pub enabled: HashSet<ModuleName>, -} - -impl Default for DuRsConfV1 { - fn default() -> Self { - DuRsConfV1 { - currency: CurrencyName(String::from(constants::DEFAULT_CURRENCY)), - my_node_id: generate_random_node_id(), - modules: ModulesConf::default(), - disabled: HashSet::with_capacity(0), - enabled: HashSet::with_capacity(0), - } - } -} - -#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)] -/// Ressource usage -pub enum ResourceUsage { - /// Minimal use of the resource, to the detriment of performance - Minimal, - /// Trade-off between resource use and performance - Medium, - /// A performance-oriented trade-off, the use of the resource is slightly limited - Large, - /// No restrictions on the use of the resource, maximizes performance - Infinite, -} -#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)] -/// Ressources usage -pub struct ResourcesUsage { - /// Cpu usage - pub cpu_usage: ResourceUsage, - /// Network usage - pub network_usage: ResourceUsage, - /// Memory usage - pub memory_usage: ResourceUsage, - /// Disk space usage - pub disk_space_usage: ResourceUsage, -} - -impl Default for ResourcesUsage { - fn default() -> Self { - ResourcesUsage { - cpu_usage: ResourceUsage::Large, - network_usage: ResourceUsage::Large, - memory_usage: ResourceUsage::Large, - disk_space_usage: ResourceUsage::Large, - } - } -} - -#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] -/// Duniter configuration v2 -pub struct DuRsConfV2 { - /// Currency name - pub currency: CurrencyName, - /// Duniter node unique identifier - pub my_node_id: u32, - /// Name of the module used by default for synchronization - pub default_sync_module: ModuleName, - /// Ressources usage - pub ressources_usage: ResourcesUsage, - /// Disabled modules - pub disabled: HashSet<ModuleName>, - /// Enabled modules - pub enabled: HashSet<ModuleName>, -} - -impl Default for DuRsConfV2 { - fn default() -> Self { - DuRsConfV2 { - currency: CurrencyName(String::from(constants::DEFAULT_CURRENCY)), - my_node_id: generate_random_node_id(), - default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)), - ressources_usage: ResourcesUsage::default(), - disabled: HashSet::with_capacity(0), - enabled: HashSet::with_capacity(0), - } - } -} - -impl From<DuRsConfV1> for DuRsConfV2 { - fn from(conf_v1: DuRsConfV1) -> Self { - DuRsConfV2 { - currency: conf_v1.currency, - my_node_id: conf_v1.my_node_id, - default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)), - ressources_usage: ResourcesUsage::default(), - disabled: conf_v1.disabled, - enabled: conf_v1.enabled, - } - } -} - #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] /// Dunitrust node configuration pub enum DuRsConf { /// Dunitrust node configuration v1 - V1(DuRsConfV1), + V1(v1::DuRsConfV1), /// Dunitrust node configuration v2 V2 { /// Global configuration - global_conf: DuRsConfV2, + global_conf: DuRsGlobalConfV2, /// Modules configuration modules_conf: ModulesConf, }, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -/// Dunitrust global configuration (without modules configuration) -pub enum DuRsGlobalConf { - /// Dunitrust global configuration v1 - V1(DuRsConfV1), - /// Dunitrust global configuration v2 - V2(DuRsConfV2), -} - -impl DursGlobalConfTrait for DuRsGlobalConf { - fn my_node_id(&self) -> u32 { - match *self { - DuRsGlobalConf::V1(ref conf_v1) => conf_v1.my_node_id, - DuRsGlobalConf::V2(ref conf_v2) => conf_v2.my_node_id, - } - } - fn default_sync_module(&self) -> ModuleName { - match *self { - DuRsGlobalConf::V1(_) => { - fatal_error!("Feature default_sync_module not exist in durs conf v1 !") - } - DuRsGlobalConf::V2(ref conf_v2) => conf_v2.default_sync_module.clone(), - } - } -} - impl Default for DuRsConf { #[inline] fn default() -> Self { DuRsConf::V2 { - global_conf: DuRsConfV2::default(), + global_conf: DuRsGlobalConfV2::default(), modules_conf: ModulesConf::default(), } } @@ -261,12 +108,30 @@ impl DursConfTrait for DuRsConf { } => DuRsGlobalConf::V2(global_conf.clone()), } } + fn override_global_conf( + self, + global_user_conf: <Self::GlobalConf as DursGlobalConfTrait>::GlobalUserConf, + ) -> Self { + match self { + DuRsConf::V1(conf_v1) => DuRsConf::V1(conf_v1), + DuRsConf::V2 { + global_conf, + modules_conf, + } => { + let DuRsGlobalUserConf::V2(global_user_conf_v2) = global_user_conf; + DuRsConf::V2 { + global_conf: global_conf.r#override(global_user_conf_v2), + modules_conf, + } + } + } + } fn upgrade(self) -> (Self, bool) { if let DuRsConf::V1(conf_v1) = self { let modules_conf = conf_v1.modules.clone(); ( DuRsConf::V2 { - global_conf: DuRsConfV2::from(conf_v1), + global_conf: DuRsGlobalConfV2::from(conf_v1), modules_conf, }, true, @@ -365,81 +230,6 @@ impl DursConfTrait for DuRsConf { } } -#[derive(Debug, Clone, PartialEq, Eq)] -/// Keypairs filled in by the user (via a file or by direct entry in the terminal). -pub struct DuniterKeyPairs { - /// Keypair used by the node to sign its communications with other nodes. This keypair is mandatory, if it's not filled in, a random keypair is generated. - pub network_keypair: KeyPairEnum, - /// Keypair used to sign the blocks forged by this node. If this keypair is'nt filled in, the node will not calculate blocks. - pub member_keypair: Option<KeyPairEnum>, -} - -impl Serialize for DuniterKeyPairs { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let member_seed = if let Some(ref member_keypair) = self.member_keypair { - member_keypair.seed().to_string() - } else { - String::from("") - }; - let member_pub = if let Some(ref member_keypair) = self.member_keypair { - member_keypair.public_key().to_string() - } else { - String::from("") - }; - let mut state = serializer.serialize_struct("DuniterKeyPairs", 4)?; - state.serialize_field( - "network_seed", - &self.network_keypair.seed().to_string().as_str(), - )?; - state.serialize_field( - "network_pub", - &self.network_keypair.public_key().to_string().as_str(), - )?; - state.serialize_field("member_seed", member_seed.as_str())?; - state.serialize_field("member_pub", member_pub.as_str())?; - state.end() - } -} - -impl DuniterKeyPairs { - /// Returns only the keys indicated as required - pub fn get_required_keys_content( - required_keys: RequiredKeys, - keypairs: DuniterKeyPairs, - ) -> RequiredKeysContent { - match required_keys { - RequiredKeys::MemberKeyPair() => { - RequiredKeysContent::MemberKeyPair(keypairs.member_keypair) - } - RequiredKeys::MemberPublicKey() => { - RequiredKeysContent::MemberPublicKey(if let Some(keys) = keypairs.member_keypair { - Some(keys.public_key()) - } else { - None - }) - } - RequiredKeys::NetworkKeyPair() => { - RequiredKeysContent::NetworkKeyPair(keypairs.network_keypair) - } - RequiredKeys::NetworkPublicKey() => { - RequiredKeysContent::NetworkPublicKey(keypairs.network_keypair.public_key()) - } - RequiredKeys::None() => RequiredKeysContent::None(), - } - } -} - -// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic ! -fn generate_random_keypair(algo: KeysAlgo) -> KeyPairEnum { - match algo { - KeysAlgo::Ed25519 => KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair::generate_random()), - KeysAlgo::Schnorr => panic!("Schnorr algo not yet supported !"), - } -} - #[inline] fn generate_random_node_id() -> u32 { rand::gen_u32() @@ -465,14 +255,6 @@ pub fn get_datas_path(profile_path: PathBuf) -> PathBuf { datas_path } -#[inline] -/// Return path to configuration file -pub fn get_conf_path(profile_path: &PathBuf) -> PathBuf { - let mut conf_path = profile_path.clone(); - conf_path.push(constants::CONF_FILENAME); - conf_path -} - /// Returns the path to the folder containing the user data of the running profile // Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic ! pub fn get_profile_path(profiles_path: &Option<PathBuf>, profile_name: &str) -> PathBuf { @@ -503,184 +285,27 @@ pub fn get_profile_path(profiles_path: &Option<PathBuf>, profile_name: &str) -> profile_path } -/// Get keypairs file path -pub fn keypairs_filepath(profiles_path: &Option<PathBuf>, profile: &str) -> PathBuf { - let profile_path = get_profile_path(profiles_path, profile); - let mut conf_keys_path = profile_path; - conf_keys_path.push(constants::KEYPAIRS_FILENAME); - conf_keys_path -} - /// Load configuration. pub fn load_conf( profile_path: PathBuf, keypairs_file_path: &Option<PathBuf>, -) -> Result<(DuRsConf, DuniterKeyPairs), DursConfFileError> { - // Load conf - let (conf, keypairs) = load_conf_at_path(profile_path, keypairs_file_path)?; +) -> Result<(DuRsConf, DuniterKeyPairs), DursConfError> { + let keypairs = crate::keypairs::load_keypairs_from_file(&profile_path, keypairs_file_path)?; - // Return conf and keypairs - Ok((conf, keypairs)) -} - -/// Error with configuration file -#[derive(Debug, Fail)] -pub enum DursConfFileError { - /// Read error - #[fail(display = "fail to read configuration file: {}", _0)] - ReadError(std::io::Error), - /// Parse error - #[fail(display = "fail to parse configuration file: {}", _0)] - ParseError(serde_json::Error), - /// Write error - #[fail(display = "fail to write configuration file: {}", _0)] - WriteError(std::io::Error), -} - -/// Load configuration. at specified path -// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic ! -pub fn load_conf_at_path( - profile_path: PathBuf, - keypairs_file_path: &Option<PathBuf>, -) -> Result<(DuRsConf, DuniterKeyPairs), DursConfFileError> { - // Get KeyPairs - let keypairs_path = if let Some(ref keypairs_file_path) = keypairs_file_path { - keypairs_file_path.clone() - } else { - let mut keypairs_path = profile_path.clone(); - keypairs_path.push(constants::KEYPAIRS_FILENAME); - keypairs_path - }; - let keypairs = if keypairs_path.as_path().exists() { - if let Ok(mut f) = File::open(keypairs_path.as_path()) { - let mut contents = String::new(); - if f.read_to_string(&mut contents).is_ok() { - let json_conf: serde_json::Value = - serde_json::from_str(&contents).expect("Conf: Fail to parse keypairs file !"); - - if let Some(network_seed) = json_conf.get("network_seed") { - if let Some(network_pub) = json_conf.get("network_pub") { - let network_seed = network_seed - .as_str() - .expect("Conf: Fail to parse keypairs file !"); - let network_pub = network_pub - .as_str() - .expect("Conf: Fail to parse keypairs file !"); - let network_keypair = KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair { - seed: Seed32::from_base58(network_seed) - .expect("conf : keypairs file : fail to parse network_seed !"), - pubkey: ed25519::PublicKey::from_base58(network_pub) - .expect("conf : keypairs file : fail to parse network_pub !"), - }); - - let member_keypair = if let Some(member_seed) = json_conf.get("member_seed") - { - if let Some(member_pub) = json_conf.get("member_pub") { - let member_seed = member_seed - .as_str() - .expect("Conf: Fail to parse keypairs file !"); - let member_pub = member_pub - .as_str() - .expect("Conf: Fail to parse keypairs file !"); - if member_seed.is_empty() || member_pub.is_empty() { - None - } else { - Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair { - seed: Seed32::from_base58(member_seed).expect( - "conf : keypairs file : fail to parse member_seed !", - ), - pubkey: ed25519::PublicKey::from_base58(member_pub).expect( - "conf : keypairs file : fail to parse member_pub !", - ), - })) - } - } else { - panic!("Fatal error : keypairs file wrong format : no field salt !") - } - } else { - panic!("Fatal error : keypairs file wrong format : no field password !") - }; + // Load conf from file + let conf_from_file = + crate::file::load_conf_from_file(profile_path).map_err(DursConfError::FileErr)?; - // Create keypairs file with random keypair - DuniterKeyPairs { - network_keypair, - member_keypair, - } - } else { - panic!("Fatal error : keypairs file wrong format : no field salt !") - } - } else { - panic!("Fatal error : keypairs file wrong format : no field password !") - } - } else { - panic!("Fail to read keypairs file !"); - } - } else { - panic!("Fail to open keypairs file !"); - } - } else { - // Create keypairs file with random keypair - let keypairs = DuniterKeyPairs { - network_keypair: generate_random_keypair(KeysAlgo::Ed25519), - member_keypair: None, - }; - write_keypairs_file(&keypairs_path, &keypairs).unwrap_or_else(|_| { - panic!(dbg!("Fatal error : fail to write default keypairs file !")) - }); - keypairs - }; + // Try to load global user conf from env vars + let env_global_user_conf = + env::load_env_global_user_conf().map_err(DursConfError::EnvVarErr)?; - // Open conf file - let mut conf_path = profile_path; - conf_path.push(constants::CONF_FILENAME); - let conf = if conf_path.as_path().exists() { - match File::open(conf_path.as_path()) { - Ok(mut f) => { - let mut contents = String::new(); - f.read_to_string(&mut contents) - .map_err(DursConfFileError::ReadError)?; - // Parse conf file - let conf: DuRsConf = - serde_json::from_str(&contents).map_err(DursConfFileError::ParseError)?; - // Upgrade conf to latest version - let (conf, upgraded) = conf.upgrade(); - // If conf is upgraded, rewrite conf file - if upgraded { - write_conf_file(conf_path.as_path(), &conf) - .map_err(DursConfFileError::WriteError)?; - } - conf - } - Err(e) => return Err(DursConfFileError::ReadError(e)), - } - } else { - // Create conf file with default conf - let conf = DuRsConf::default(); - write_conf_file(conf_path.as_path(), &conf) - .unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to write default conf file!"))); - conf - }; + // Override global conf with env global user conf + let conf = conf_from_file.override_global_conf(env_global_user_conf); - // Return conf and keypairs Ok((conf, keypairs)) } -/// Save keypairs in profile folder -// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic ! -pub fn write_keypairs_file( - file_path: &PathBuf, - keypairs: &DuniterKeyPairs, -) -> Result<(), std::io::Error> { - let mut f = File::create(file_path.as_path())?; - f.write_all( - serde_json::to_string_pretty(keypairs) - .unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to deserialize keypairs !"))) - .as_bytes(), - )?; - f.sync_all()?; - Ok(()) -} - /// Write new module conf pub fn write_new_module_conf<DC: DursConfTrait>( conf: &mut DC, @@ -691,22 +316,8 @@ pub fn write_new_module_conf<DC: DursConfTrait>( conf.set_module_conf(module_name, new_module_conf); let mut conf_path = profile_path; conf_path.push(crate::constants::CONF_FILENAME); - write_conf_file(conf_path.as_path(), conf).expect("Fail to write new conf file ! "); -} - -/// Save configuration in profile folder -pub fn write_conf_file<DC: DursConfTrait>( - conf_path: &Path, - conf: &DC, -) -> Result<(), std::io::Error> { - let mut f = File::create(conf_path)?; - f.write_all( - serde_json::to_string_pretty(conf) - .expect("Fatal error : fail to write default conf file !") - .as_bytes(), - )?; - f.sync_all()?; - Ok(()) + crate::file::write_conf_file(conf_path.as_path(), conf) + .expect("Fail to write new conf file ! "); } /// Returns the path to the database containing the blockchain @@ -722,95 +333,3 @@ pub fn get_blockchain_db_path(profile_path: PathBuf) -> PathBuf { } db_path } - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - #[inline] - fn save_old_conf(profile_path: PathBuf) -> std::io::Result<()> { - let mut conf_path = profile_path.clone(); - conf_path.push(constants::CONF_FILENAME); - let mut conf_sav_path = profile_path; - conf_sav_path.push("conf-sav.json"); - std::fs::copy(conf_path.as_path(), conf_sav_path.as_path())?; - Ok(()) - } - - fn restore_old_conf_and_save_upgraded_conf(profile_path: PathBuf) -> std::io::Result<()> { - let mut conf_path = profile_path.clone(); - conf_path.push(constants::CONF_FILENAME); - let mut conf_sav_path = profile_path.clone(); - conf_sav_path.push("conf-sav.json"); - let mut conf_upgraded_path = profile_path; - conf_upgraded_path.push("conf-upgraded.json"); - std::fs::copy(conf_path.as_path(), &conf_upgraded_path.as_path())?; - std::fs::copy(conf_sav_path.as_path(), &conf_path.as_path())?; - std::fs::remove_file(conf_sav_path.as_path())?; - Ok(()) - } - - #[test] - fn load_conf_file_v1() -> Result<(), DursConfFileError> { - let profile_path = PathBuf::from("./test/v1/"); - save_old_conf(PathBuf::from(profile_path.clone())) - .map_err(DursConfFileError::WriteError)?; - let (conf, _keys) = load_conf_at_path(profile_path.clone(), &None)?; - assert_eq!( - conf.modules() - .get("ws2p") - .expect("Not found ws2p conf") - .clone(), - json!({ - "sync_endpoints": [ - { - "endpoint": "WS2P c1c39a0a i3.ifee.fr 80 /ws2p", - "pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx" - }, - { - "endpoint": "WS2P 15af24db g1.ifee.fr 80 /ws2p", - "pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG" - }, - { - "endpoint": "WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p", - "pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef" - } - ] - }) - ); - restore_old_conf_and_save_upgraded_conf(profile_path) - .map_err(DursConfFileError::WriteError)?; - - Ok(()) - } - - #[test] - fn load_conf_file_v2() -> Result<(), DursConfFileError> { - let profile_path = PathBuf::from("./test/v2/"); - let (conf, _keys) = load_conf_at_path(profile_path, &None)?; - assert_eq!( - conf.modules() - .get("ws2p") - .expect("Not found ws2p conf") - .clone(), - json!({ - "sync_endpoints": [ - { - "endpoint": "WS2P c1c39a0a i3.ifee.fr 80 /ws2p", - "pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx" - }, - { - "endpoint": "WS2P 15af24db g1.ifee.fr 80 /ws2p", - "pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG" - }, - { - "endpoint": "WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p", - "pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef" - } - ] - }) - ); - Ok(()) - } -} diff --git a/lib/core/conf/src/modules_conf.rs b/lib/core/conf/src/modules_conf.rs new file mode 100644 index 00000000..38e975b0 --- /dev/null +++ b/lib/core/conf/src/modules_conf.rs @@ -0,0 +1,46 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust modules configuration + +use durs_module::ModuleName; + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +/// Modules conf +pub struct ModulesConf(pub serde_json::Value); + +impl Default for ModulesConf { + #[inline] + fn default() -> Self { + ModulesConf(serde_json::Value::Null) + } +} + +impl ModulesConf { + /// Change module conf + pub fn set_module_conf(&mut self, module_name: ModuleName, new_module_conf: serde_json::Value) { + if self.0.is_null() { + let mut new_modules_conf = serde_json::Map::with_capacity(1); + new_modules_conf.insert(module_name.0, new_module_conf); + self.0 = serde_json::value::to_value(new_modules_conf) + .expect("Fail to create map of new modules conf !"); + } else { + self.0 + .as_object_mut() + .expect("Conf file currupted !") + .insert(module_name.0, new_module_conf); + } + } +} diff --git a/lib/core/conf/src/resources.rs b/lib/core/conf/src/resources.rs new file mode 100644 index 00000000..75a1e84c --- /dev/null +++ b/lib/core/conf/src/resources.rs @@ -0,0 +1,53 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust resources usage configuration + +#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)] +/// Ressource usage +pub enum ResourceUsage { + /// Minimal use of the resource, to the detriment of performance + Minimal, + /// Trade-off between resource use and performance + Medium, + /// A performance-oriented trade-off, the use of the resource is slightly limited + Large, + /// No restrictions on the use of the resource, maximizes performance + Infinite, +} + +#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)] +/// Ressources usage +pub struct ResourcesUsage { + /// Cpu usage + pub cpu_usage: ResourceUsage, + /// Network usage + pub network_usage: ResourceUsage, + /// Memory usage + pub memory_usage: ResourceUsage, + /// Disk space usage + pub disk_space_usage: ResourceUsage, +} + +impl Default for ResourcesUsage { + fn default() -> Self { + ResourcesUsage { + cpu_usage: ResourceUsage::Large, + network_usage: ResourceUsage::Large, + memory_usage: ResourceUsage::Large, + disk_space_usage: ResourceUsage::Large, + } + } +} diff --git a/lib/core/conf/src/v1.rs b/lib/core/conf/src/v1.rs new file mode 100644 index 00000000..b6836776 --- /dev/null +++ b/lib/core/conf/src/v1.rs @@ -0,0 +1,48 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Dunitrust configuration v1 + +use crate::modules_conf::ModulesConf; +use dubp_currency_params::CurrencyName; +use durs_module::ModuleName; +use std::collections::HashSet; + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +/// Duniter configuration v1 +pub struct DuRsConfV1 { + /// Currency name + pub currency: CurrencyName, + /// Duniter node unique identifier + pub my_node_id: u32, + /// Configuration of modules in json format (obtained from the conf.json file) + pub modules: ModulesConf, + /// Disabled modules + pub disabled: HashSet<ModuleName>, + /// Enabled modules + pub enabled: HashSet<ModuleName>, +} + +impl Default for DuRsConfV1 { + fn default() -> Self { + DuRsConfV1 { + currency: CurrencyName(String::from(crate::constants::DEFAULT_CURRENCY)), + my_node_id: crate::generate_random_node_id(), + modules: ModulesConf::default(), + disabled: HashSet::with_capacity(0), + enabled: HashSet::with_capacity(0), + } + } +} diff --git a/lib/core/core/src/change_conf.rs b/lib/core/core/src/change_conf.rs index 9e6c9d64..cd12fc0c 100644 --- a/lib/core/core/src/change_conf.rs +++ b/lib/core/core/src/change_conf.rs @@ -34,6 +34,6 @@ pub fn change_global_conf<DC: DursConfTrait>( } // Write new conf - durs_conf::write_conf_file(&durs_conf::get_conf_path(profile_path), conf) + durs_conf::file::write_conf_file(&durs_conf::file::get_conf_path(profile_path), conf) .map_err(DursCoreError::FailUpdateConf) } diff --git a/lib/core/core/src/commands/keys.rs b/lib/core/core/src/commands/keys.rs index 8bf320f9..61736c89 100644 --- a/lib/core/core/src/commands/keys.rs +++ b/lib/core/core/src/commands/keys.rs @@ -20,7 +20,7 @@ use crate::errors::DursCoreError; use crate::DursCore; use clap::arg_enum; use clear_on_drop::clear::Clear; -use durs_conf::keys::*; +use durs_conf::keypairs::cli::*; use durs_conf::DuRsConf; #[derive(StructOpt, Debug, Clone)] diff --git a/lib/core/core/src/errors.rs b/lib/core/core/src/errors.rs index 9021d4c1..9e40dd0e 100644 --- a/lib/core/core/src/errors.rs +++ b/lib/core/core/src/errors.rs @@ -17,16 +17,13 @@ use crate::logger::InitLoggerError; use dubp_currency_params::db::CurrencyParamsDbError; -use durs_conf::keys::WizardError; +use durs_conf::keypairs::cli::WizardError; use durs_module::{ModuleStaticName, PlugModuleError}; use failure::{Error, Fail}; #[derive(Debug, Fail)] /// Dunitrust server error pub enum DursCoreError { - /// Error with configuration file - #[fail(display = "Error with configuration file: {}", _0)] - ConfFileError(durs_conf::DursConfFileError), /// Generic error that impl Fail #[fail(display = "{}", _0)] Error(Error), @@ -54,6 +51,9 @@ pub enum DursCoreError { /// Error on initialization of the logger #[fail(display = "Error on initialization of the logger: {}", _0)] InitLoggerError(InitLoggerError), + /// Error at configuration loading + #[fail(display = "Error at configuration loading: {}", _0)] + LoadConfError(durs_conf::DursConfError), /// Plug module error #[fail(display = "Error on loading module '{}': {}", module_name, error)] PlugModuleError { diff --git a/lib/core/core/src/lib.rs b/lib/core/core/src/lib.rs index 74b4779c..edb2da35 100644 --- a/lib/core/core/src/lib.rs +++ b/lib/core/core/src/lib.rs @@ -45,7 +45,7 @@ use dubp_currency_params::CurrencyName; use durs_bc::{dbex::DbExQuery, BlockchainModule}; use durs_common_tools::fatal_error; pub use durs_conf::{ - constants::KEYPAIRS_FILENAME, keys::*, ChangeGlobalConf, DuRsConf, DuniterKeyPairs, + constants::KEYPAIRS_FILENAME, keypairs::cli::*, ChangeGlobalConf, DuRsConf, DuniterKeyPairs, }; use durs_message::*; use durs_module::*; @@ -246,7 +246,7 @@ impl DursCore<DuRsConf> { // Load global conf let (conf, keypairs) = durs_conf::load_conf(profile_path.clone(), &durs_core_opts.keypairs_file) - .map_err(DursCoreError::ConfFileError)?; + .map_err(DursCoreError::LoadConfError)?; info!("Success to load global conf."); // Get currency name diff --git a/lib/core/core/src/router.rs b/lib/core/core/src/router.rs index 1baf6ad2..db662040 100644 --- a/lib/core/core/src/router.rs +++ b/lib/core/core/src/router.rs @@ -238,13 +238,13 @@ fn start_conf_thread( mut conf: DuRsConf, receiver: &mpsc::Receiver<DursMsg>, ) { - let conf_path = durs_conf::get_conf_path(&profile_path); + let conf_path = durs_conf::file::get_conf_path(&profile_path); loop { match receiver.recv() { Ok(msg) => { if let DursMsg::SaveNewModuleConf(module_static_name, new_json_conf) = msg { conf.set_module_conf(ModuleName(module_static_name.to_string()), new_json_conf); - durs_conf::write_conf_file(&conf_path, &conf) + durs_conf::file::write_conf_file(&conf_path, &conf) .expect("Fail to write new module conf in conf file ! "); } } diff --git a/lib/core/module/src/lib.rs b/lib/core/module/src/lib.rs index b1d6c07e..23360f7c 100644 --- a/lib/core/module/src/lib.rs +++ b/lib/core/module/src/lib.rs @@ -109,6 +109,9 @@ impl ToString for ModuleReqFullId { pub trait DursGlobalConfTrait: Clone + Debug + PartialEq + Serialize + DeserializeOwned + Send + ToOwned { + /// Global user configuration + type GlobalUserConf; + /// Get node id fn my_node_id(&self) -> u32; /// Get default sync module @@ -130,16 +133,21 @@ pub trait DursConfTrait: fn enable(&mut self, module: ModuleName); /// Get enabled modules fn enabled_modules(&self) -> HashSet<ModuleName>; + /// Get currency name + fn get_currency(&self) -> CurrencyName; /// Get global conf fn get_global_conf(&self) -> Self::GlobalConf; + /// Override global configuration + fn override_global_conf( + self, + global_user_conf: <Self::GlobalConf as DursGlobalConfTrait>::GlobalUserConf, + ) -> Self; /// Get modules conf fn modules(&self) -> serde_json::Value; /// Get node id fn my_node_id(&self) -> u32 { self.get_global_conf().my_node_id() } - /// Get currency name - fn get_currency(&self) -> CurrencyName; /// Set currency fn set_currency(&mut self, new_currency: CurrencyName); /// Change module conf diff --git a/lib/modules/blockchain/blockchain/src/sync/mod.rs b/lib/modules/blockchain/blockchain/src/sync/mod.rs index 75d12308..77f4deed 100644 --- a/lib/modules/blockchain/blockchain/src/sync/mod.rs +++ b/lib/modules/blockchain/blockchain/src/sync/mod.rs @@ -180,7 +180,7 @@ pub fn local_sync<DC: DursConfTrait>( // Write new conf let mut conf_path = profile_path.clone(); conf_path.push(durs_conf::constants::CONF_FILENAME); - durs_conf::write_conf_file(conf_path.as_path(), &conf).expect("Fail to write new conf !"); + durs_conf::file::write_conf_file(conf_path.as_path(), &conf).expect("Fail to write new conf !"); // Open database let db = open_db(&db_path.as_path()).map_err(|_| LocalSyncError::FailToOpenDB)?; -- GitLab