diff --git a/packages/configuration/src/lib.rs b/packages/configuration/src/lib.rs index f785aa976..6de0e3ed7 100644 --- a/packages/configuration/src/lib.rs +++ b/packages/configuration/src/lib.rs @@ -228,14 +228,11 @@ //!``` use std::collections::{HashMap, HashSet}; use std::net::IpAddr; -use std::panic::Location; -use std::path::Path; use std::str::FromStr; use std::sync::Arc; use std::{env, fs}; use config::{Config, ConfigError, File, FileFormat}; -use log::warn; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, NoneAsEmptyString}; use thiserror::Error; @@ -414,17 +411,6 @@ pub enum Error { source: LocatedError<'static, dyn std::error::Error + Send + Sync>, }, - /// If you run the tracker without providing the configuration (via the - /// `TORRUST_TRACKER_CONFIG` environment variable or configuration file), - /// the tracker will create a default configuration file but it will not - /// load it. It will return this error instead and you have to restart the - /// it. - #[error("Default configuration created at: `{path}`, please review and reload tracker, {location}")] - CreatedNewConfigHalt { - location: &'static Location<'static>, - path: String, - }, - /// Unable to load the configuration from the configuration file. #[error("Failed processing the configuration: {source}")] ConfigError { source: LocatedError<'static, ConfigError> }, @@ -502,31 +488,32 @@ impl Configuration { /// /// # Errors /// - /// Will return `Err` if `path` does not exist or has a bad configuration. + /// Will return `Err` if `path` does not exist or has a bad configuration. pub fn load_from_file(path: &str) -> Result { let config_builder = Config::builder(); #[allow(unused_assignments)] let mut config = Config::default(); - if Path::new(path).exists() { - config = config_builder.add_source(File::with_name(path)).build()?; - } else { - warn!("No config file found."); - warn!("Creating config file.."); - let config = Configuration::default(); - config.save_to_file(path)?; - return Err(Error::CreatedNewConfigHalt { - location: Location::caller(), - path: path.to_string(), - }); - } + config = config_builder.add_source(File::with_name(path)).build()?; let torrust_config: Configuration = config.try_deserialize()?; Ok(torrust_config) } + /// Saves the default configuration at the given path. + /// + /// # Errors + /// + /// Will return `Err` if `path` is not a valid path or the configuration + /// file cannot be created. + pub fn create_default_configuration_file(path: &str) -> Result { + let config = Configuration::default(); + config.save_to_file(path)?; + Ok(config) + } + /// Loads the configuration from the environment variable. The whole /// configuration must be in the environment variable. It contains the same /// configuration as the configuration file with the same format. diff --git a/src/bootstrap/app.rs b/src/bootstrap/app.rs index c0e688a0d..6961e15f0 100644 --- a/src/bootstrap/app.rs +++ b/src/bootstrap/app.rs @@ -11,11 +11,11 @@ //! 2. Initialize static variables. //! 3. Initialize logging. //! 4. Initialize the domain tracker. -use std::env; use std::sync::Arc; use torrust_tracker_configuration::Configuration; +use super::config::initialize_configuration; use crate::bootstrap; use crate::shared::clock::static_time; use crate::shared::crypto::ephemeral_instance_keys; @@ -55,35 +55,6 @@ pub fn initialize_static() { lazy_static::initialize(&ephemeral_instance_keys::RANDOM_SEED); } -/// It loads the application configuration from the environment. -/// -/// There are two methods to inject the configuration: -/// -/// 1. By using a config file: `config.toml`. The file must be in the same folder where you are running the tracker. -/// 2. Environment variable: `TORRUST_TRACKER_CONFIG`. The variable contains the same contents as the `config.toml` file. -/// -/// Environment variable has priority over the config file. -/// -/// Refer to the [configuration documentation](https://docs.rs/torrust-tracker-configuration) for the configuration options. -/// -/// # Panics -/// -/// Will panic if it can't load the configuration from either -/// `./config.toml` file or the env var `TORRUST_TRACKER_CONFIG`. -#[must_use] -fn initialize_configuration() -> Configuration { - const CONFIG_PATH: &str = "./config.toml"; - const CONFIG_ENV_VAR_NAME: &str = "TORRUST_TRACKER_CONFIG"; - - if env::var(CONFIG_ENV_VAR_NAME).is_ok() { - println!("Loading configuration from env var {CONFIG_ENV_VAR_NAME}"); - Configuration::load_from_env_var(CONFIG_ENV_VAR_NAME).unwrap() - } else { - println!("Loading configuration from config file {CONFIG_PATH}"); - Configuration::load_from_file(CONFIG_PATH).unwrap() - } -} - /// It builds the domain tracker /// /// The tracker is the domain layer service. It's the entrypoint to make requests to the domain layer. diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs new file mode 100644 index 000000000..727bf59f7 --- /dev/null +++ b/src/bootstrap/config.rs @@ -0,0 +1,69 @@ +//! Initialize configuration from file or env var. +//! +//! All environment variables are prefixed with `TORRUST_TRACKER_BACK_`. +use std::env; +use std::path::Path; + +use torrust_tracker_configuration::{Configuration, Error}; + +// Environment variables + +/// The whole `config.toml` file content. It has priority over the config file. +/// Even if the file is not on the default path. +const ENV_VAR_CONFIG: &str = "TORRUST_TRACKER_CONFIG"; + +/// The `config.toml` file location. +pub const ENV_VAR_CONFIG_PATH: &str = "TORRUST_TRACKER_CONFIG_PATH"; + +// Default values + +const ENV_VAR_DEFAULT_CONFIG_PATH: &str = "./config.toml"; + +/// It loads the application configuration from the environment. +/// +/// There are two methods to inject the configuration: +/// +/// 1. By using a config file: `config.toml`. +/// 2. Environment variable: `TORRUST_TRACKER_CONFIG`. The variable contains the same contents as the `config.toml` file. +/// +/// Environment variable has priority over the config file. +/// +/// Refer to the [configuration documentation](https://docs.rs/torrust-tracker-configuration) for the configuration options. +/// +/// # Panics +/// +/// Will panic if it can't load the configuration from either +/// `./config.toml` file or the env var `TORRUST_TRACKER_CONFIG`. +#[must_use] +pub fn initialize_configuration() -> Configuration { + if env::var(ENV_VAR_CONFIG).is_ok() { + println!("Loading configuration from env var {ENV_VAR_CONFIG} ..."); + + Configuration::load_from_env_var(ENV_VAR_CONFIG).unwrap() + } else { + let config_path = env::var(ENV_VAR_CONFIG_PATH).unwrap_or_else(|_| ENV_VAR_DEFAULT_CONFIG_PATH.to_string()); + + println!("Loading configuration from configuration file: `{config_path}` ..."); + + load_from_file_or_create_default(&config_path).unwrap() + } +} + +/// Loads the configuration from the configuration file. If the file does +/// not exist, it will create a default configuration file and return an +/// error. +/// +/// # Errors +/// +/// Will return `Err` if `path` does not exist or has a bad configuration. +fn load_from_file_or_create_default(path: &str) -> Result { + if Path::new(&path).is_file() { + Configuration::load_from_file(path) + } else { + println!("Missing configuration file."); + println!("Creating a default configuration file: `{path}` ..."); + let config = Configuration::create_default_configuration_file(path)?; + println!("Please review the config file: `{path}` and restart the tracker if needed."); + Ok(config) + } +} diff --git a/src/bootstrap/mod.rs b/src/bootstrap/mod.rs index e39cf3adc..22044aafd 100644 --- a/src/bootstrap/mod.rs +++ b/src/bootstrap/mod.rs @@ -6,5 +6,6 @@ //! like cleaning torrents, and other jobs because they can be enabled/disabled depending on the configuration. //! For example, you can have more than one UDP and HTTP tracker, each server is executed like a independent job. pub mod app; +pub mod config; pub mod jobs; pub mod logging;