Skip to content

Commit

Permalink
Merge #1188: Overhaul core Tracker: extract IoC Container
Browse files Browse the repository at this point in the history
20018ab fix: [#1187] doc link error (Jose Celano)
3d0f4f8 refactor: [#1187] use AppContainer in test environments (Jose Celano)
c45a12b refactor: [#1187] rename fn tracker_factory to initialize_tracker (Jose Celano)
aa9f1c3 refactor: [#1187] move fn initialize_tracker_dependencies (Jose Celano)
a4d8da0 refactor: [#1187] inline fn initialize_tracker (Jose Celano)
36db088 refactor: [#1187] inline fn initialize_globals_and_tracker (Jose Celano)
4aea9db refactor: [#1187] extract fn initialize_app_container (Jose Celano)
747b58d refactor: [#1187] extract one function and rename another one (Jose Celano)
8bea521 refactor: [#1187] extract IoC Container (Jose Celano)

Pull request description:

  Overhaul core Tracker: extract IoC Container

ACKs for top commit:
  josecelano:
    ACK 20018ab

Tree-SHA512: 5bb7d6fc2f3e577547b1532b3267f3f1d7eddd938b883a6a3fb31a3a3bd0e3b8001325e3c6d45261da46c34fd2c4312b627abc0054efd7097e1721375b325b5f
  • Loading branch information
josecelano committed Jan 17, 2025
2 parents 5b46cea + 20018ab commit 765bd6f
Show file tree
Hide file tree
Showing 24 changed files with 205 additions and 187 deletions.
51 changes: 21 additions & 30 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,23 @@
//! - UDP trackers: the user can enable multiple UDP tracker on several ports.
//! - HTTP trackers: the user can enable multiple HTTP tracker on several ports.
//! - Tracker REST API: the tracker API can be enabled/disabled.
use std::sync::Arc;

use tokio::sync::RwLock;
use tokio::task::JoinHandle;
use torrust_tracker_configuration::Configuration;
use tracing::instrument;

use crate::bootstrap::jobs::{health_check_api, http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
use crate::core::statistics::event::sender::Sender;
use crate::core::statistics::repository::Repository;
use crate::container::AppContainer;
use crate::servers;
use crate::servers::registar::Registar;
use crate::servers::udp::server::banning::BanService;
use crate::{core, servers};

/// # Panics
///
/// Will panic if:
///
/// - Can't retrieve tracker keys from database.
/// - Can't load whitelist from database.
#[instrument(skip(config, tracker, ban_service, stats_event_sender, stats_repository))]
pub async fn start(
config: &Configuration,
tracker: Arc<core::Tracker>,
ban_service: Arc<RwLock<BanService>>,
stats_event_sender: Arc<Option<Box<dyn Sender>>>,
stats_repository: Arc<Repository>,
) -> Vec<JoinHandle<()>> {
#[instrument(skip(config, app_container))]
pub async fn start(config: &Configuration, app_container: &AppContainer) -> Vec<JoinHandle<()>> {
if config.http_api.is_none()
&& (config.udp_trackers.is_none() || config.udp_trackers.as_ref().map_or(true, std::vec::Vec::is_empty))
&& (config.http_trackers.is_none() || config.http_trackers.as_ref().map_or(true, std::vec::Vec::is_empty))
Expand All @@ -61,16 +50,18 @@ pub async fn start(
let registar = Registar::default();

// Load peer keys
if tracker.is_private() {
tracker
if app_container.tracker.is_private() {
app_container
.tracker
.load_keys_from_database()
.await
.expect("Could not retrieve keys from database.");
}

// Load whitelisted torrents
if tracker.is_listed() {
tracker
if app_container.tracker.is_listed() {
app_container
.tracker
.whitelist_manager
.load_whitelist_from_database()
.await
Expand All @@ -80,7 +71,7 @@ pub async fn start(
// Start the UDP blocks
if let Some(udp_trackers) = &config.udp_trackers {
for udp_tracker_config in udp_trackers {
if tracker.is_private() {
if app_container.tracker.is_private() {
tracing::warn!(
"Could not start UDP tracker on: {} while in private mode. UDP is not safe for private trackers!",
udp_tracker_config.bind_address
Expand All @@ -89,9 +80,9 @@ pub async fn start(
jobs.push(
udp_tracker::start_job(
udp_tracker_config,
tracker.clone(),
stats_event_sender.clone(),
ban_service.clone(),
app_container.tracker.clone(),
app_container.stats_event_sender.clone(),
app_container.ban_service.clone(),
registar.give_form(),
)
.await,
Expand All @@ -107,8 +98,8 @@ pub async fn start(
for http_tracker_config in http_trackers {
if let Some(job) = http_tracker::start_job(
http_tracker_config,
tracker.clone(),
stats_event_sender.clone(),
app_container.tracker.clone(),
app_container.stats_event_sender.clone(),
registar.give_form(),
servers::http::Version::V1,
)
Expand All @@ -125,10 +116,10 @@ pub async fn start(
if let Some(http_api_config) = &config.http_api {
if let Some(job) = tracker_apis::start_job(
http_api_config,
tracker.clone(),
ban_service.clone(),
stats_event_sender.clone(),
stats_repository.clone(),
app_container.tracker.clone(),
app_container.ban_service.clone(),
app_container.stats_event_sender.clone(),
app_container.stats_repository.clone(),
registar.give_form(),
servers::apis::Version::V1,
)
Expand All @@ -142,7 +133,7 @@ pub async fn start(

// Start runners to remove torrents without peers, every interval
if config.core.inactive_peer_cleanup_interval > 0 {
jobs.push(torrent_cleanup::start_job(&config.core, &tracker));
jobs.push(torrent_cleanup::start_job(&config.core, &app_container.tracker));
}

// Start Health Check API
Expand Down
18 changes: 18 additions & 0 deletions src/app_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! This file contains only functions used for testing.
use std::sync::Arc;

use torrust_tracker_configuration::Configuration;

use crate::core::databases::Database;
use crate::core::services::{initialize_database, initialize_whitelist};
use crate::core::whitelist::WhiteListManager;

/// Initialize the tracker dependencies.
#[allow(clippy::type_complexity)]
#[must_use]
pub fn initialize_tracker_dependencies(config: &Configuration) -> (Arc<Box<dyn Database>>, Arc<WhiteListManager>) {
let database = initialize_database(config);
let whitelist_manager = initialize_whitelist(database.clone());

(database, whitelist_manager)
}
82 changes: 30 additions & 52 deletions src/bootstrap/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,21 @@ use tracing::instrument;

use super::config::initialize_configuration;
use crate::bootstrap;
use crate::core::databases::Database;
use crate::core::services::{initialize_database, initialize_whitelist, statistics, tracker_factory};
use crate::core::statistics::event::sender::Sender;
use crate::core::statistics::repository::Repository;
use crate::core::whitelist::WhiteListManager;
use crate::core::Tracker;
use crate::container::AppContainer;
use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
use crate::servers::udp::server::banning::BanService;
use crate::servers::udp::server::launcher::MAX_CONNECTION_ID_ERRORS_PER_IP;
use crate::shared::crypto::ephemeral_instance_keys;
use crate::shared::crypto::keys::{self, Keeper as _};

/// It loads the configuration from the environment and builds the main domain [`Tracker`] struct.
/// It loads the configuration from the environment and builds app container.
///
/// # Panics
///
/// Setup can file if the configuration is invalid.
#[must_use]
#[allow(clippy::type_complexity)]
#[instrument(skip())]
pub fn setup() -> (
Configuration,
Arc<Tracker>,
Arc<RwLock<BanService>>,
Arc<Option<Box<dyn Sender>>>,
Arc<Repository>,
) {
pub fn setup() -> (Configuration, AppContainer) {
#[cfg(not(test))]
check_seed();

Expand All @@ -56,19 +45,13 @@ pub fn setup() -> (
panic!("Configuration error: {e}");
}

// Initialize services

let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
let stats_event_sender = Arc::new(stats_event_sender);
let stats_repository = Arc::new(stats_repository);

let udp_ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));

let tracker = initialize_with_configuration(&configuration);
initialize_global_services(&configuration);

tracing::info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json());

(configuration, tracker, udp_ban_service, stats_event_sender, stats_repository)
let app_container = initialize_app_container(&configuration);

(configuration, app_container)
}

/// checks if the seed is the instance seed in production.
Expand All @@ -83,15 +66,31 @@ pub fn check_seed() {
assert_eq!(seed, instance, "maybe using zeroed seed in production!?");
}

/// It initializes the application with the given configuration.
///
/// The configuration may be obtained from the environment (via config file or env vars).
#[must_use]
/// It initializes the global services.
#[instrument(skip())]
pub fn initialize_with_configuration(configuration: &Configuration) -> Arc<Tracker> {
pub fn initialize_global_services(configuration: &Configuration) {
initialize_static();
initialize_logging(configuration);
Arc::new(initialize_tracker(configuration))
}

/// It initializes the IoC Container.
#[instrument(skip())]
pub fn initialize_app_container(configuration: &Configuration) -> AppContainer {
let (stats_event_sender, stats_repository) = statistics::setup::factory(configuration.core.tracker_usage_statistics);
let stats_event_sender = Arc::new(stats_event_sender);
let stats_repository = Arc::new(stats_repository);
let ban_service = Arc::new(RwLock::new(BanService::new(MAX_CONNECTION_ID_ERRORS_PER_IP)));
let database = initialize_database(configuration);
let whitelist_manager = initialize_whitelist(database.clone());
let tracker = Arc::new(initialize_tracker(configuration, &database, &whitelist_manager));

AppContainer {
tracker,
ban_service,
stats_event_sender,
stats_repository,
whitelist_manager,
}
}

/// It initializes the application static values.
Expand All @@ -115,27 +114,6 @@ pub fn initialize_static() {
lazy_static::initialize(&ephemeral_instance_keys::ZEROED_TEST_CIPHER_BLOWFISH);
}

/// It builds the domain tracker
///
/// The tracker is the domain layer service. It's the entrypoint to make requests to the domain layer.
/// It's used by other higher-level components like the UDP and HTTP trackers or the tracker API.
#[must_use]
#[instrument(skip(config))]
pub fn initialize_tracker(config: &Configuration) -> Tracker {
let (database, whitelist_manager) = initialize_tracker_dependencies(config);

tracker_factory(config, &database, &whitelist_manager)
}

#[allow(clippy::type_complexity)]
#[must_use]
pub fn initialize_tracker_dependencies(config: &Configuration) -> (Arc<Box<dyn Database>>, Arc<WhiteListManager>) {
let database = initialize_database(config);
let whitelist_manager = initialize_whitelist(database.clone());

(database, whitelist_manager)
}

/// It initializes the log threshold, format and channel.
///
/// See [the logging setup](crate::bootstrap::logging::setup) for more info about logging.
Expand Down
13 changes: 10 additions & 3 deletions src/bootstrap/jobs/http_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ mod tests {

use torrust_tracker_test_helpers::configuration::ephemeral_public;

use crate::bootstrap::app::initialize_with_configuration;
use crate::bootstrap::app::initialize_global_services;
use crate::bootstrap::jobs::http_tracker::start_job;
use crate::core::services::statistics;
use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
use crate::servers::http::Version;
use crate::servers::registar::Registar;

Expand All @@ -97,9 +97,16 @@ mod tests {
let cfg = Arc::new(ephemeral_public());
let http_tracker = cfg.http_trackers.clone().expect("missing HTTP tracker configuration");
let config = &http_tracker[0];

let (stats_event_sender, _stats_repository) = statistics::setup::factory(cfg.core.tracker_usage_statistics);
let stats_event_sender = Arc::new(stats_event_sender);
let tracker = initialize_with_configuration(&cfg);

initialize_global_services(&cfg);

let database = initialize_database(&cfg);
let whitelist_manager = initialize_whitelist(database.clone());
let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));

let version = Version::V1;

start_job(config, tracker, stats_event_sender, Registar::default().give_form(), version)
Expand Down
10 changes: 7 additions & 3 deletions src/bootstrap/jobs/tracker_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ mod tests {
use tokio::sync::RwLock;
use torrust_tracker_test_helpers::configuration::ephemeral_public;

use crate::bootstrap::app::initialize_with_configuration;
use crate::bootstrap::app::initialize_global_services;
use crate::bootstrap::jobs::tracker_apis::start_job;
use crate::core::services::statistics;
use crate::core::services::{initialize_database, initialize_tracker, initialize_whitelist, statistics};
use crate::servers::apis::Version;
use crate::servers::registar::Registar;
use crate::servers::udp::server::banning::BanService;
Expand All @@ -158,7 +158,11 @@ mod tests {
let stats_event_sender = Arc::new(stats_event_sender);
let stats_repository = Arc::new(stats_repository);

let tracker = initialize_with_configuration(&cfg);
initialize_global_services(&cfg);

let database = initialize_database(&cfg);
let whitelist_manager = initialize_whitelist(database.clone());
let tracker = Arc::new(initialize_tracker(&cfg, &database, &whitelist_manager));

let version = Version::V1;

Expand Down
4 changes: 2 additions & 2 deletions src/console/profiling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ pub async fn run() {
return;
};

let (config, tracker, ban_service, stats_event_sender, stats_repository) = bootstrap::app::setup();
let (config, app_container) = bootstrap::app::setup();

let jobs = app::start(&config, tracker, ban_service, stats_event_sender, stats_repository).await;
let jobs = app::start(&config, &app_container).await;

// Run the tracker for a fixed duration
let run_duration = sleep(Duration::from_secs(duration_secs));
Expand Down
17 changes: 17 additions & 0 deletions src/container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::sync::Arc;

use tokio::sync::RwLock;

use crate::core::statistics::event::sender::Sender;
use crate::core::statistics::repository::Repository;
use crate::core::whitelist::WhiteListManager;
use crate::core::Tracker;
use crate::servers::udp::server::banning::BanService;

pub struct AppContainer {
pub tracker: Arc<Tracker>,
pub ban_service: Arc<RwLock<BanService>>,
pub stats_event_sender: Arc<Option<Box<dyn Sender>>>,
pub stats_repository: Arc<Repository>,
pub whitelist_manager: Arc<WhiteListManager>,
}
12 changes: 6 additions & 6 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,28 +1080,28 @@ mod tests {
use torrust_tracker_primitives::DurationSinceUnixEpoch;
use torrust_tracker_test_helpers::configuration;

use crate::bootstrap::app::initialize_tracker_dependencies;
use crate::app_test::initialize_tracker_dependencies;
use crate::core::peer::Peer;
use crate::core::services::tracker_factory;
use crate::core::services::initialize_tracker;
use crate::core::whitelist::WhiteListManager;
use crate::core::{TorrentsMetrics, Tracker};

fn public_tracker() -> Tracker {
let config = configuration::ephemeral_public();
let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
tracker_factory(&config, &database, &whitelist_manager)
initialize_tracker(&config, &database, &whitelist_manager)
}

fn private_tracker() -> Tracker {
let config = configuration::ephemeral_private();
let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
tracker_factory(&config, &database, &whitelist_manager)
initialize_tracker(&config, &database, &whitelist_manager)
}

fn whitelisted_tracker() -> (Tracker, Arc<WhiteListManager>) {
let config = configuration::ephemeral_listed();
let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
let tracker = tracker_factory(&config, &database, &whitelist_manager);
let tracker = initialize_tracker(&config, &database, &whitelist_manager);

(tracker, whitelist_manager)
}
Expand All @@ -1110,7 +1110,7 @@ mod tests {
let mut config = configuration::ephemeral_listed();
config.core.tracker_policy.persistent_torrent_completed_stat = true;
let (database, whitelist_manager) = initialize_tracker_dependencies(&config);
tracker_factory(&config, &database, &whitelist_manager)
initialize_tracker(&config, &database, &whitelist_manager)
}

fn sample_info_hash() -> InfoHash {
Expand Down
Loading

0 comments on commit 765bd6f

Please sign in to comment.