Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul Configuration and Settings for Torrust #401

Closed
da2ce7 opened this issue Sep 6, 2023 · 9 comments
Closed

Overhaul Configuration and Settings for Torrust #401

da2ce7 opened this issue Sep 6, 2023 · 9 comments
Assignees
Labels
- Admin - Enjoyable to Install and Setup our Software Code Cleanup / Refactoring Tidying and Making Neat Dependencies Related to Dependencies EPIC Contains several subissues Needs Research We Need to Know More About This Question / Discussion Community Feedback
Milestone

Comments

@da2ce7
Copy link
Contributor

da2ce7 commented Sep 6, 2023

Our current configuration system is a mess and not sustainable.

I suggest that we move to the Figment configuration library and conceptually rework our entire approach to configuration for the tracker.

@da2ce7 da2ce7 removed the rust label Sep 21, 2023
@da2ce7 da2ce7 added Code Cleanup / Refactoring Tidying and Making Neat Dependencies Related to Dependencies - Admin - Enjoyable to Install and Setup our Software Needs Research We Need to Know More About This Question / Discussion Community Feedback and removed Enhancement / Feature Request Something New Help Wanted More Contributions are Appreciated and extra attention is needed. labels Oct 10, 2023
@cgbosse cgbosse added this to the v3.0.0 milestone Jan 16, 2024
@josecelano
Copy link
Member

Some ideas related to configuration:

  • Create an installer with a flag (argument) #134
  • Configuration should be passed with a file or env vars. It can be passed as a whole file. Regarding env vars there is one for the whole file contents and others used to overwrite some values.
  • We are allowing user to overwrite some options after loading the configuration file.
  • It would be nice to validate the configuration before running the services when possible, in order to fail as soon as possible. We are doing that for one case. That implies to parse invalid configuration from a config file and create a secondary internal structure that is valid.
  • It would be nice to allow users to define only the config options they want to change from the default configuration instead. See https://github.com/greatest-ape/aquatic/pull/191/files/c5843eedce6ccbc8f69e3b3f48ade84f705e70ba#r1527138893. This could be a problem if default values change. The user could not realise it's using different values.

@josecelano josecelano self-assigned this May 9, 2024
@josecelano josecelano added the EPIC Contains several subissues label May 9, 2024
josecelano added a commit that referenced this issue May 9, 2024
0252f30 feat: allow users not to provide config option with default values (Jose Celano)
43942ce tests: add test for configuration with deprecated env var name (Jose Celano)
b0c2f9f docs: update env var name in toml config template files (Jose Celano)
69d7939 refactor: implement Default for Configuration sections (Jose Celano)
caae725 feat: use double underscore to split config env var names (Jose Celano)
b3a1442 refactor!: remove unused method in Configuration (Jose Celano)
632c8ba refactor: move Configuration unit test to inner mods (Jose Celano)
146b77d feat: enable overwrite Configuration values using env vars (Jose Celano)
5bd9494 chore: remove unused config dependenciy (Jose Celano)
265d89d refactor: replace Config by Figment in Configuration implementation (Jose Celano)
002fb30 refactor: reexport config versioned config types (Jose Celano)
e7d344c refactor: create new configuration v1 mod with figment (Jose Celano)
636e779 refactor: create new configuration v1 mod with figment (Jose Celano)
157807c chore(deps): enable figment features: env, toml, test (Jose Celano)
f0e0721 test: remove broken example in rustdoc (Jose Celano)
7da52b1 chore(deps): add dependency figment (Jose Celano)

Pull request description:

  Relates to: #790

  This PR migrates the configuration implementation to use [Figment](https://crates.io/crates/figment) as suggested by @da2ce7 [here](#401).

  ### Context

  I was working on a configuration validation in this [PR](#790). I wanted to validate things like socket addresses earlier when we parse the configuration instead of waiting until the app launches the service. For example, the UDP tracker configuration contains the `bind_address`:

  ```toml
  [[udp_trackers]]
  bind_address = "0.0.0.0:6969"
  enabled = false
  ```

  That configuration maps to a `String`:

  ```rust
  pub struct UdpTracker {
      pub enabled: bool,
      pub bind_address: String,
  }
  ```

  I realized that kind of very basic validation could be actually done just changing the types in the configuration. For example:

  ```rust
  pub struct UdpTracker {
      pub enabled: bool,
      pub bind_address: ScoketAddr,
  }
  ```

  There are other functionalities like overwritting values in the config file with env vars that can be implemented with Figment (merge).

  So I decided to migrate to Figment the current version and update the configuration after the migration.

  ### Subtasks without changing current config API (toml or struct)

  - [x] Implement a new configuration mod with Figment.
  - [x] Reorganize configuration mods creating new submods for config file sections.
  - [x] Introduce versioning for configuration, so that we can make breaking changes to the configuration in the future.
  - [x] Replace in production the configuration with the new Figment implementation.
  - [x] Use `Default` trait for config sections (not only root config).
  - [x] Allow users not to provide values when defaults are OK for them.

  ### FUTURE PR: Subtasks changing current config API (structs)

  These changes do not change the toml file configuration, only the parsed type.

  - Use reach types like SockeAddr to validate some types without even starting the services.

  ### FUTURE PR: Subtasks changing current config API (toml and structs)

  At this point, an extra validation could be needed like the one described [here](#790). For example, if  you enable TSL for the tracker API you must provide both the certificate path and the certificate key path:

  ```toml
  [http_api]
  bind_address = "127.0.0.1:1212"
  enabled = true
  ssl_cert_path = ""
  ssl_enabled = false
  ssl_key_path = ""
  ```

  I think that type of validation could be implementented after parsing the inyected config or maybe just reorganizcing the toml file structure. For example:

  No API enabled:

  ```toml
  # No section [http_api]
  ```

  API enabled but no TSL enabled:

  ```toml
  [http_api]
  bind_address = "127.0.0.1:1212"
  ```

  API enabled with TSL enabled:

  ```toml
  [http_api]
  bind_address = "127.0.0.1:1212"

  [http_api.tsl_config]
  ssl_cert_path = "./storage/tracker/lib/tls/localhost.crt"
  ssl_key_path = "./storage/tracker/lib/tls/localhost.key"
  ```

  We would not need the `enabled` field. If the section is present the feature is enabled. If it's not it means that feature is disabled.

  These breaking changes will be added in a new `v2` configuration in a new PR.

ACKs for top commit:
  josecelano:
    ACK 0252f30

Tree-SHA512: 94c7baa8566744c9e79bc9c56aae836d7a9b4272c42965c2dc88ec04b6a297b830c8eb7cb65d318c74e00aa5e27cc7c56784d6b083af04e98aadff4c82af5239
@josecelano josecelano removed this from the v3.0.0 milestone May 9, 2024
@josecelano
Copy link
Member

josecelano commented May 9, 2024

I've just realized we don't have the toml file path in Figment errors because we always pass Figment the toml file contents:

pub fn load(info: &Info) -> Result<Configuration, Error> {
    let figment = Figment::from(Serialized::defaults(Configuration::default()))
        .merge(Toml::string(&info.tracker_toml))
        .merge(Env::prefixed("TORRUST_TRACKER__").split("__"));

    let mut config: Configuration = figment.extract()?;

    // Deprecated manual overwritting of config options
    if let Some(ref token) = info.api_admin_token {
        config.override_api_admin_token(token);
    };

    Ok(config)
}

See: #854

Since we always pass Figment the contents of the toml file it does not know the location for the original toml file. We have to change the Info struct to provide the original info: contents or file and let Fgment load the file if needed. I will open a new issue for this when I finish the current open PR.

Error message:

$ cargo run
    Finished `dev` profile [optimized + debuginfo] target(s) in 0.10s
     Running `target/debug/torrust-tracker`
Loading default configuration file: `./share/default/config/tracker.development.sqlite3.toml` ...
thread 'main' panicked at src/bootstrap/config.rs:45:32:
called `Result::unwrap()` on an `Err` value: ConfigError { source: LocatedError { source: Error { tag: Tag(Default, 2), profile: Some(Profile(Uncased { string: "default" })), metadata: Some(Metadata { name: "TOML source string", source: None, provide_location: Some(Location { file: "packages/configuration/src/v1/mod.rs", line: 397, col: 14 }), interpolater:  }), path: ["health_check_api", "bind_address"], kind: Message("invalid socket address syntax"), prev: None }, location: Location { file: "packages/configuration/src/v1/mod.rs", line: 400, col: 41 } } }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The error should show the file location ./share/default/config/tracker.development.sqlite3.toml.

josecelano added a commit that referenced this issue May 10, 2024
ae77ebc refactor: tracker core service only needs the core config (Jose Celano)

Pull request description:

  Relates to: #401

  After [extracting the `Core` config type](b545b33), we don't need to pass the whole configuration to the tracker core service. We can pass only its config.

ACKs for top commit:
  josecelano:
    ACK ae77ebc

Tree-SHA512: dfff87307fdf764291e0fd1c9441c0b8e971c8398294c3b1d99fd673618d117c557dea56e768cca39fd51509253895c105ed16d356f710ae7e4e7b923d9da39e
@josecelano
Copy link
Member

Hi @da2ce7, I've finished the migration to Figment and also renamed the env vars. Is there anything else you wanted to refactor when you opened this issue?

I have also opened a new discussion to define the version 2 for the configuration:

#853

I guess we can define that for the milestone v.3.2.0 like the API v2.

PLease, reopen the issue if you miss something you want to refactor before releasing v3.0.0.

@josecelano
Copy link
Member

josecelano commented Jun 20, 2024

Today, we had a meeting, and we decided to include some more changes.

Keep the version 1 for the TOML file

I'm using the internal version 1 v1 for the new version since I have totally removed the previous one. Since the config file is not too big and changes are easy to apply manually, I decided to start using versioning from the current one.

@da2ce7 wants to introduce versioning before the next major release v3.0.0 so we need to:

  • Rename the namespace for the current one from v1 to v2.
  • Add a field with the version in the toml file at the top of the files: version = 2.
  • Recover the previous version and call it v1.
  • Allow users to use the version 1 and migrate automatically? @da2ce7, If yes manually, automatically or with an update config script? I would suggest:
  • Warning the user if it's using an old version. And ...
  • Provide an update config script to update configuration from v1 to v2.

Some fields should not have a default value

In the current version, all config values have a default value. YOu can run the app without providing any config value at all.

@da2ce7 suggested forcing the admin to provide at least some critical options like:

  • version
  • logging level
  • tracker mode

Add a namespace to the configuration

[torrust_tracker]
version = "1.0.0"

[torrust_tracker.logging]
log_level = "info"
{
  "torrust_tracker": {
    "version": "1.0.0",
    "logging": {
      "log_level": "info"
    }
  }
}

Rename some fields

For example. From

[logging]
log_level = "info"

To:

[logging]
threshold = "info"

Provide explicit values for enums

I see two ways of doing it.

First option:

[logging.threshold]
error = false
info = true
warm = false
debug = false
trace = false

Second option, provide a schema from TOML and JSON.

See: https://docs.rs/toml_schema/latest/toml_schema/

Third option:

[logging]
#threshold = "error"
threshold = "info"
#threshold = "warm"
#threshold = "debug"
#threshold = "trace"

Add all options in the temapltes uncommenting the default one.

Improve UX (for admins)

  • Add a warning when the configuration does not enable any service (UDP tracker, HTTP tracker, or tracker API). It does not make sense to run the app without services enabled.
  • Write the final config used to the logs when the tracker starts. We are only writing the config values when the source is an env var. We should do it always and print out the final configuration after emerging sources (defaults -> TOML file -> env vars).

@josecelano
Copy link
Member

I forget to reopen the issue ☝🏼 to implement those extra changes.

@josecelano
Copy link
Member

Hi @da2ce7, I'm also considering a change I have wanted to do for a long time.

I think tracker privacy (public and private) and torrent restrictions (whitelisted or not) are totally independent options.

Current

pub enum TrackerMode {
    Public,
    Listed,
    Private,
    PrivateListed,
}
[core]
mode = "public"

New Proposal

pub enum Privacy {
    Public,
    Private,
}
[core]
privacy = "public"
whitelisted = "true"

I think it's the right moment to do it. Any other suggestions for names @da2ce7 ?

Maybe an alternative to "whitelist"? See:

@josecelano
Copy link
Member

@da2ce7's proposal and I agree.

[core]
private = true
listed = true

@josecelano
Copy link
Member

When you run the tracker with the default development config, you see the configuration with all services enabled in JSON:

Loading extra configuration from default configuration file: `./share/default/config/tracker.development.sqlite3.toml` ...
2024-07-03T16:11:27.971946Z  INFO torrust_tracker::bootstrap::logging: Logging initialized
2024-07-03T16:11:27.972578Z  INFO torrust_tracker::bootstrap::app: Configuration:
{
  "version": "2",
  "logging": {
    "threshold": "info"
  },
  "core": {
    "announce_policy": {
      "interval": 120,
      "interval_min": 120
    },
    "database": {
      "driver": "Sqlite3",
      "path": "./storage/tracker/lib/database/sqlite3.db"
    },
    "inactive_peer_cleanup_interval": 600,
    "listed": false,
    "net": {
      "external_ip": "0.0.0.0",
      "on_reverse_proxy": false
    },
    "private": false,
    "tracker_policy": {
      "max_peer_timeout": 900,
      "persistent_torrent_completed_stat": false,
      "remove_peerless_torrents": true
    },
    "tracker_usage_statistics": true
  },
  "udp_trackers": [
    {
      "bind_address": "0.0.0.0:6969"
    }
  ],
  "http_trackers": [
    {
      "bind_address": "0.0.0.0:7070",
      "tsl_config": null
    }
  ],
  "http_api": {
    "bind_address": "0.0.0.0:1212",
    "tsl_config": null,
    "access_tokens": {
      "admin": "MyAccessToken"
    }
  },
  "health_check_api": {
    "bind_address": "127.0.0.1:1313"
  }
}
2024-07-03T16:11:27.972620Z  INFO UDP TRACKER: Starting on: 0.0.0.0:6969
2024-07-03T16:11:27.972662Z  INFO UDP TRACKER: Started on: udp://0.0.0.0:6969
2024-07-03T16:11:27.972694Z  INFO HTTP TRACKER: Starting on: http://0.0.0.0:7070
2024-07-03T16:11:27.972786Z  INFO HTTP TRACKER: Started on: http://0.0.0.0:7070
2024-07-03T16:11:27.972900Z  INFO API: Starting on http://0.0.0.0:1212
2024-07-03T16:11:27.972905Z  INFO API: Started on http://0.0.0.0:1212
2024-07-03T16:11:27.972919Z  INFO HEALTH CHECK API: Starting on: http://127.0.0.1:1313
2024-07-03T16:11:27.972951Z  INFO HEALTH CHECK API: Started on: http://127.0.0.1:1313

In toml:

version = "2"

[logging]
threshold = "info"

[core]
inactive_peer_cleanup_interval = 600
listed = false
private = false
tracker_usage_statistics = true

  [core.announce_policy]
  interval = 120
  interval_min = 120

  [core.database]
  driver = "Sqlite3"
  path = "./storage/tracker/lib/database/sqlite3.db"

  [core.net]
  external_ip = "0.0.0.0"
  on_reverse_proxy = false

  [core.tracker_policy]
  max_peer_timeout = 900
  persistent_torrent_completed_stat = false
  remove_peerless_torrents = true

[[udp_trackers]]
bind_address = "0.0.0.0:6969"

[[http_trackers]]
bind_address = "0.0.0.0:7070"

[http_api]
bind_address = "0.0.0.0:1212"

  [http_api.access_tokens]
  admin = "MyAccessToken"

[health_check_api]
bind_address = "127.0.0.1:1313"

That's the current state. Some more changes are pending to confirm or get feedback. See the list above. @da2ce7 @mario-nt @cgbosse

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- Admin - Enjoyable to Install and Setup our Software Code Cleanup / Refactoring Tidying and Making Neat Dependencies Related to Dependencies EPIC Contains several subissues Needs Research We Need to Know More About This Question / Discussion Community Feedback
Projects
Status: Done
Development

No branches or pull requests

3 participants