Skip to content

Commit

Permalink
Merge #631: Configuration overhaul: version 2 for the configuration t…
Browse files Browse the repository at this point in the history
…oml file (breaking changes)

35a125e refactor: [#591] order keys in config TOML files alphabetically (Jose Celano)
cb8935b feat!: [#591] use full socket addr in net config instead of only the port (Jose Celano)
52f3d9c feat!: [#591] inject full socket address in configuration (Jose Celano)
1bc00ce feat!: [#591] extract new types for SMTP server configuration (Jose Celano)
cd8248a feat!: [#591] extract new type for password constrains in configuration (Jose Celano)
50ebb9a feat!: [#591] move log_level into a new logging section in config (Jose Celano)
82fc32c feat!: [#591] rename enum variant EmailOnSignup::None to Ignored (Jose Celano)
bb75303 feat!: [#591] lowercase for auth::enail_on_signup emnum variants (Jose Celano)
eb987e9 feat!: [#591] config v2, tracker mode from tracker (Jose Celano)

Pull request description:

  A set of breaking changes in the TOML config file to improve readability and validation.

  ### Subtasks

  - [x] Normalize `TrackerMode` enum to use the same values as the [Tracker](https://github.com/torrust/torrust-tracker/blob/a7ec479ac0982ec69ec66f037392dac75bd981c0/packages/primitives/src/lib.rs#L68-L133).
  - [x] Use lowercase for `auth::email_on_signup` config option variants.
  - [x] Rename `auth::email_on_signup::None` variant to `auth::email_on_signup::Ingored`.
  - [x] Include the `log_lovel` option in the new section `logging` and make it mandatory with `info` default value.
  - [x] Extract password constraints in a new type (section `auth`).
  - [x] Extract SMPT credentials in a new type (section `mail`).
  - [x] Include the socket address (IP+PORT) instead of only the port (section `net`).
  - [x] Use alphabetical order for TOML keys inside the section.

  ### Notes

  - We are normalizing all variants in enums to lowercase in TOML files.
  - The option `base_url` (section `net`) already exists in the current version. It's not changed in the new version. It's optional. If it's not present it uses the request headers to get the current host.

  ### Current toml version

  ```toml
  log_level = "info"

  [website]
  name = "Torrust"

  [tracker]
  api_url = "http://localhost:1212"
  mode = "Public"
  token = "MyAccessToken"
  token_valid_seconds = 7257600
  url = "udp://localhost:6969"

  [net]
  port = 3001

  # Uncomment if you want to enable TSL for development
  #[net.tsl]
  #ssl_cert_path = "./storage/index/lib/tls/localhost.crt"
  #ssl_key_path = "./storage/index/lib/tls/localhost.key"

  [auth]
  email_on_signup = "Optional"
  max_password_length = 64
  min_password_length = 6
  secret_key = "MaxVerstappenWC2021"

  [database]
  connect_url = "sqlite://data.db?mode=rwc"

  [mail]
  email_verification_enabled = false
  from = "[email protected]"
  password = ""
  port = 25
  reply_to = "[email protected]"
  server = ""
  username = ""

  [image_cache]
  capacity = 128000000
  entry_size_limit = 4000000
  max_request_timeout_ms = 1000
  user_quota_bytes = 64000000
  user_quota_period_seconds = 3600

  [api]
  default_torrent_page_size = 10
  max_torrent_page_size = 30

  [tracker_statistics_importer]
  port = 3002
  torrent_info_update_interval = 3600
  ```

  ### New toml version

  ```toml
  [logging]
  log_level = "info"

  [website]
  name = "Torrust"

  [tracker]
  api_url = "http://localhost:1212"
  mode = "public"
  token = "MyAccessToken"
  token_valid_seconds = 7257600
  url = "udp://localhost:6969"

  [net]
  base_url = "http://localhost"
  bind_address = "0.0.0.0:3001"

  # Uncomment if you want to enable TSL for development
  #[net.tsl]
  #ssl_cert_path = "./storage/index/lib/tls/localhost.crt"
  #ssl_key_path = "./storage/index/lib/tls/localhost.key"

  [auth]
  email_on_signup = "Optional"
  secret_key = "MaxVerstappenWC2021"

  [auth.password_constraints]
  max_password_length = 64
  min_password_length = 6

  [database]
  connect_url = "sqlite://data.db?mode=rwc"

  [mail]
  email_verification_enabled = false
  from = "[email protected]"
  reply_to = "[email protected]"

  [mail.smtp]
  port = 25
  server = ""

  [mail.smtp.credentials]
  password = ""
  username = ""

  [image_cache]
  capacity = 128000000
  entry_size_limit = 4000000
  max_request_timeout_ms = 1000
  user_quota_bytes = 64000000
  user_quota_period_seconds = 3600

  [api]
  default_torrent_page_size = 10
  max_torrent_page_size = 30

  [tracker_statistics_importer]
  port = 3002
  torrent_info_update_interval = 3600

  ```

ACKs for top commit:
  josecelano:
    ACK 35a125e

Tree-SHA512: 8aa5b04d11c70c110f1e5fdc55da97740db6e637023dfc28668a72729816ac5ee3404c2fc7158942061594f61ec72adf4ab37cfd6b4d922eb23e87f7c4170ad1
  • Loading branch information
josecelano committed Jun 13, 2024
2 parents 32542e9 + 35a125e commit fb4f199
Show file tree
Hide file tree
Showing 30 changed files with 549 additions and 248 deletions.
4 changes: 3 additions & 1 deletion share/default/config/index.container.mysql.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
[logging]
log_level = "info"

[database]
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"

3 changes: 2 additions & 1 deletion share/default/config/index.container.sqlite3.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[logging]
log_level = "info"

[database]
connect_url = "sqlite:///var/lib/torrust/index/database/sqlite3.db?mode=rwc"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
1 change: 1 addition & 0 deletions share/default/config/index.development.sqlite3.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[logging]
log_level = "info"

# Uncomment if you want to enable TSL for development
Expand Down
5 changes: 3 additions & 2 deletions share/default/config/index.private.e2e.container.sqlite3.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[logging]
log_level = "info"

[tracker]
api_url = "http://tracker:1212"
mode = "Private"
mode = "private"
url = "http://tracker:7070"

[database]
connect_url = "sqlite:///var/lib/torrust/index/database/e2e_testing_sqlite3.db?mode=rwc"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
3 changes: 2 additions & 1 deletion share/default/config/index.public.e2e.container.mysql.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[logging]
log_level = "info"

[tracker]
Expand All @@ -7,6 +8,6 @@ url = "udp://tracker:6969"
[database]
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index_e2e_testing"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
3 changes: 2 additions & 1 deletion share/default/config/index.public.e2e.container.sqlite3.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[logging]
log_level = "info"

[tracker]
Expand All @@ -7,6 +8,6 @@ url = "udp://tracker:6969"
[database]
connect_url = "sqlite:///var/lib/torrust/index/database/e2e_testing_sqlite3.db?mode=rwc"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
7 changes: 3 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub struct Running {
/// It panics if there is an error connecting to the database.
#[allow(clippy::too_many_lines)]
pub async fn run(configuration: Configuration, api_version: &Version) -> Running {
let log_level = configuration.settings.read().await.log_level.clone();
let log_level = configuration.settings.read().await.logging.log_level.clone();

logging::setup(&log_level);

Expand All @@ -57,8 +57,7 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
let importer_torrent_info_update_interval = settings.tracker_statistics_importer.torrent_info_update_interval;
let importer_port = settings.tracker_statistics_importer.port;
// From [net] config
let net_ip = "0.0.0.0".to_string();
let net_port = settings.net.port;
let config_bind_address = settings.net.bind_address;
let opt_net_tsl = settings.net.tsl.clone();

// IMPORTANT: drop settings before starting server to avoid read locks that
Expand Down Expand Up @@ -181,7 +180,7 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
);

// Start API server
let running_api = web::api::start(app_data, &net_ip, net_port, opt_net_tsl, api_version).await;
let running_api = web::api::start(app_data, config_bind_address, opt_net_tsl, api_version).await;

// Full running application
Running {
Expand Down
20 changes: 3 additions & 17 deletions src/bootstrap/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use std::sync::Once;
use tracing::info;
use tracing::level_filters::LevelFilter;

use crate::config::v1::LogLevel;
use crate::config::LogLevel;

static INIT: Once = Once::new();

pub fn setup(log_level: &Option<LogLevel>) {
let tracing_level = config_level_or_default(log_level);
pub fn setup(log_level: &LogLevel) {
let tracing_level: LevelFilter = log_level.clone().into();

if tracing_level == LevelFilter::OFF {
return;
Expand All @@ -27,20 +27,6 @@ pub fn setup(log_level: &Option<LogLevel>) {
});
}

fn config_level_or_default(log_level: &Option<LogLevel>) -> LevelFilter {
match log_level {
None => LevelFilter::INFO,
Some(level) => match level {
LogLevel::Off => LevelFilter::OFF,
LogLevel::Error => LevelFilter::ERROR,
LogLevel::Warn => LevelFilter::WARN,
LogLevel::Info => LevelFilter::INFO,
LogLevel::Debug => LevelFilter::DEBUG,
LogLevel::Trace => LevelFilter::TRACE,
},
}
}

fn tracing_stdout_init(filter: LevelFilter, style: &TraceStyle) {
let builder = tracing_subscriber::fmt().with_max_level(filter);

Expand Down
136 changes: 77 additions & 59 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
pub mod v1;
pub mod validator;

use std::env;
use std::str::FromStr;
use std::sync::Arc;
use std::{env, fmt};

use camino::Utf8PathBuf;
use figment::providers::{Env, Format, Serialized, Toml};
Expand All @@ -19,16 +19,33 @@ use url::Url;
use crate::web::api::server::DynError;

pub type Settings = v1::Settings;

pub type Api = v1::api::Api;

pub type Auth = v1::auth::Auth;
pub type EmailOnSignup = v1::auth::EmailOnSignup;
pub type SecretKey = v1::auth::SecretKey;
pub type PasswordConstraints = v1::auth::PasswordConstraints;

pub type Database = v1::database::Database;

pub type ImageCache = v1::image_cache::ImageCache;

pub type Mail = v1::mail::Mail;
pub type Smtp = v1::mail::Smtp;
pub type Credentials = v1::mail::Credentials;

pub type Network = v1::net::Network;

pub type TrackerStatisticsImporter = v1::tracker_statistics_importer::TrackerStatisticsImporter;

pub type Tracker = v1::tracker::Tracker;
pub type ApiToken = v1::tracker::ApiToken;

pub type Logging = v1::logging::Logging;
pub type LogLevel = v1::logging::LogLevel;

pub type Website = v1::website::Website;
pub type EmailOnSignup = v1::auth::EmailOnSignup;

/// Prefix for env vars that overwrite configuration options.
const CONFIG_OVERRIDE_PREFIX: &str = "TORRUST_INDEX_CONFIG_OVERRIDE_";
Expand Down Expand Up @@ -119,45 +136,29 @@ impl From<figment::Error> for Error {
}
}

/* todo:
Use https://crates.io/crates/torrust-tracker-primitives for TrackerMode.
// todo: use https://crates.io/crates/torrust-tracker-primitives for TrackerMode.

Enum variants:
In Index In Tracker
- `Public` -> `Public`
- `Private` -> `Private`
- `Whitelisted` -> `Listed`
- `PrivateWhitelisted` -> `PrivateListed`
Enum serialized values:
In Index In Tracker
- `Public` -> `public`
- `Private` -> `private`
- `Whitelisted` -> `listed`
- `PrivateWhitelisted` -> `private_listed`
It's a breaking change for the toml config file en the API.
*/

/// See `TrackerMode` in [`torrust-tracker-primitives`](https://docs.rs/torrust-tracker-primitives)
/// crate for more information.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
/// The mode the tracker will run in.
///
/// Refer to [Torrust Tracker Configuration](https://docs.rs/torrust-tracker-configuration)
/// to know how to configure the tracker to run in each mode.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub enum TrackerMode {
/// Will track every new info hash and serve every peer.
#[serde(rename = "public")]
Public,

/// Will only serve authenticated peers.
Private,

/// Will only track whitelisted info hashes.
Whitelisted,
#[serde(rename = "listed")]
Listed,

/// Will only track whitelisted info hashes and serve authenticated peers.
PrivateWhitelisted,
/// Will only serve authenticated peers
#[serde(rename = "private")]
Private,

/// Will only track whitelisted info hashes and serve authenticated peers
#[serde(rename = "private_listed")]
PrivateListed,
}

impl Default for TrackerMode {
Expand All @@ -166,26 +167,36 @@ impl Default for TrackerMode {
}
}

impl fmt::Display for TrackerMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let display_str = match self {
TrackerMode::Public => "public",
TrackerMode::Listed => "listed",
TrackerMode::Private => "private",
TrackerMode::PrivateListed => "private_listed",
};
write!(f, "{display_str}")
}
}

impl FromStr for TrackerMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Public" => Ok(TrackerMode::Public),
"Private" => Ok(TrackerMode::Private),
"Whitelisted" => Ok(TrackerMode::Whitelisted),
"PrivateWhitelisted" => Ok(TrackerMode::PrivateWhitelisted),
_ => Err(format!(
"{s} is not a valid tracker mode. Valid values: 'Public', 'Private', 'Whitelisted', 'PrivateWhitelisted' "
)),
match s.to_lowercase().as_str() {
"public" => Ok(TrackerMode::Public),
"listed" => Ok(TrackerMode::Listed),
"private" => Ok(TrackerMode::Private),
"private_listed" => Ok(TrackerMode::PrivateListed),
_ => Err(format!("Unknown tracker mode: {s}")),
}
}
}

impl TrackerMode {
#[must_use]
pub fn is_open(&self) -> bool {
matches!(self, TrackerMode::Public | TrackerMode::Whitelisted)
matches!(self, TrackerMode::Public | TrackerMode::Listed)
}

#[must_use]
Expand Down Expand Up @@ -325,57 +336,64 @@ mod tests {

use url::Url;

use crate::config::v1::auth::SecretKey;
use crate::config::v1::tracker::ApiToken;
use crate::config::{Configuration, ConfigurationPublic, Info, Settings};
use crate::config::{ApiToken, Configuration, ConfigurationPublic, Info, SecretKey, Settings};

#[cfg(test)]
fn default_config_toml() -> String {
let config = r#"[website]
let config = r#"[logging]
log_level = "info"
[website]
name = "Torrust"
[tracker]
url = "udp://localhost:6969"
mode = "Public"
api_url = "http://localhost:1212/"
mode = "public"
token = "MyAccessToken"
token_valid_seconds = 7257600
url = "udp://localhost:6969"
[net]
port = 3001
bind_address = "0.0.0.0:3001"
[auth]
email_on_signup = "Optional"
min_password_length = 6
max_password_length = 64
email_on_signup = "optional"
secret_key = "MaxVerstappenWC2021"
[auth.password_constraints]
max_password_length = 64
min_password_length = 6
[database]
connect_url = "sqlite://data.db?mode=rwc"
[mail]
email_verification_enabled = false
from = "[email protected]"
reply_to = "[email protected]"
username = ""
password = ""
server = ""
[mail.smtp]
port = 25
server = ""
[mail.smtp.credentials]
password = ""
username = ""
[image_cache]
max_request_timeout_ms = 1000
capacity = 128000000
entry_size_limit = 4000000
user_quota_period_seconds = 3600
max_request_timeout_ms = 1000
user_quota_bytes = 64000000
user_quota_period_seconds = 3600
[api]
default_torrent_page_size = 10
max_torrent_page_size = 30
[tracker_statistics_importer]
torrent_info_update_interval = 3600
port = 3002
torrent_info_update_interval = 3600
"#
.lines()
.map(str::trim_start)
Expand Down
1 change: 1 addition & 0 deletions src/config/v1/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub struct Api {
/// The default page size for torrent lists.
#[serde(default = "Api::default_default_torrent_page_size")]
pub default_torrent_page_size: u8,

/// The maximum page size for torrent lists.
#[serde(default = "Api::default_max_torrent_page_size")]
pub max_torrent_page_size: u8,
Expand Down
Loading

0 comments on commit fb4f199

Please sign in to comment.