Skip to content

Commit

Permalink
dev: extract config from core::tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
da2ce7 committed Jan 17, 2024
1 parent dde3d8d commit 3b49257
Show file tree
Hide file tree
Showing 19 changed files with 202 additions and 184 deletions.
18 changes: 6 additions & 12 deletions packages/configuration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
//! [health_check_api]
//! bind_address = "127.0.0.1:1313"
//!```
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
Expand Down Expand Up @@ -337,6 +337,8 @@ pub struct HttpTracker {
pub ssl_key_path: Option<String>,
}

pub type AccessTokens = HashMap<String, String>;

/// Configuration for the HTTP API.
#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
Expand All @@ -360,21 +362,13 @@ pub struct HttpApi {
/// token and the value is the token itself. The token is used to
/// authenticate the user. All tokens are valid for all endpoints and have
/// the all permissions.
pub access_tokens: HashMap<String, String>,
pub access_tokens: AccessTokens,
}

impl HttpApi {
fn override_admin_token(&mut self, api_admin_token: &str) {
self.access_tokens.insert("admin".to_string(), api_admin_token.to_string());
}

/// Checks if the given token is one of the token in the configuration.
#[must_use]
pub fn contains_token(&self, token: &str) -> bool {
let tokens: HashMap<String, String> = self.access_tokens.clone();
let tokens: HashSet<String> = tokens.into_values().collect();
tokens.contains(token)
}
}

/// Configuration for the Health Check API.
Expand Down Expand Up @@ -804,7 +798,7 @@ mod tests {
fn http_api_configuration_should_check_if_it_contains_a_token() {
let configuration = Configuration::default();

assert!(configuration.http_api.contains_token("MyAccessToken"));
assert!(!configuration.http_api.contains_token("NonExistingToken"));
assert!(configuration.http_api.access_tokens.values().any(|t| t == "MyAccessToken"));
assert!(!configuration.http_api.access_tokens.values().any(|t| t == "NonExistingToken"));
}
}
12 changes: 6 additions & 6 deletions src/bootstrap/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use crate::shared::crypto::ephemeral_instance_keys;

/// It loads the configuration from the environment and builds the main domain [`Tracker`] struct.
#[must_use]
pub fn setup() -> (Arc<Configuration>, Arc<Tracker>) {
let configuration = Arc::new(initialize_configuration());
pub fn setup() -> (Configuration, Arc<Tracker>) {
let configuration = initialize_configuration();
let tracker = initialize_with_configuration(&configuration);

(configuration, tracker)
Expand All @@ -35,7 +35,7 @@ pub fn setup() -> (Arc<Configuration>, Arc<Tracker>) {
///
/// The configuration may be obtained from the environment (via config file or env vars).
#[must_use]
pub fn initialize_with_configuration(configuration: &Arc<Configuration>) -> Arc<Tracker> {
pub fn initialize_with_configuration(configuration: &Configuration) -> Arc<Tracker> {
initialize_static();
initialize_logging(configuration);
Arc::new(initialize_tracker(configuration))
Expand All @@ -60,13 +60,13 @@ pub fn initialize_static() {
/// 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]
pub fn initialize_tracker(config: &Arc<Configuration>) -> Tracker {
tracker_factory(config.clone())
pub fn initialize_tracker(config: &Configuration) -> Tracker {
tracker_factory(config)
}

/// It initializes the log level, format and channel.
///
/// See [the logging setup](crate::bootstrap::logging::setup) for more info about logging.
pub fn initialize_logging(config: &Arc<Configuration>) {
pub fn initialize_logging(config: &Configuration) {
bootstrap::logging::setup(config);
}
2 changes: 1 addition & 1 deletion src/bootstrap/jobs/torrent_cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::core;
///
/// Refer to [`torrust-tracker-configuration documentation`](https://docs.rs/torrust-tracker-configuration) for more info about that option.
#[must_use]
pub fn start_job(config: &Arc<Configuration>, tracker: &Arc<core::Tracker>) -> JoinHandle<()> {
pub fn start_job(config: &Configuration, tracker: &Arc<core::Tracker>) -> JoinHandle<()> {
let weak_tracker = std::sync::Arc::downgrade(tracker);
let interval = config.inactive_peer_cleanup_interval;

Expand Down
15 changes: 11 additions & 4 deletions src/bootstrap/jobs/tracker_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use std::sync::Arc;
use axum_server::tls_rustls::RustlsConfig;
use log::info;
use tokio::task::JoinHandle;
use torrust_tracker_configuration::HttpApi;
use torrust_tracker_configuration::{AccessTokens, HttpApi};

use super::make_rust_tls;
use crate::core;
Expand Down Expand Up @@ -64,18 +64,25 @@ pub async fn start_job(config: &HttpApi, tracker: Arc<core::Tracker>, version: V
.await
.map(|tls| tls.expect("it should have a valid tracker api tls configuration"));

let access_tokens = Arc::new(config.access_tokens.clone());

match version {
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone()).await),
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone(), access_tokens).await),
}
} else {
info!("Note: Not loading Http Tracker Service, Not Enabled in Configuration.");
None
}
}

async fn start_v1(socket: SocketAddr, tls: Option<RustlsConfig>, tracker: Arc<core::Tracker>) -> JoinHandle<()> {
async fn start_v1(
socket: SocketAddr,
tls: Option<RustlsConfig>,
tracker: Arc<core::Tracker>,
access_tokens: Arc<AccessTokens>,
) -> JoinHandle<()> {
let server = ApiServer::new(Launcher::new(socket, tls))
.start(tracker)
.start(tracker, access_tokens)
.await
.expect("it should be able to start to the tracker api");

Expand Down
70 changes: 50 additions & 20 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ use std::time::Duration;

use derive_more::Constructor;
use futures::future::join_all;
use log::debug;
use tokio::sync::mpsc::error::SendError;
use torrust_tracker_configuration::{AnnouncePolicy, Configuration};
use torrust_tracker_primitives::TrackerMode;
Expand All @@ -472,17 +473,19 @@ pub const TORRENT_PEERS_LIMIT: usize = 74;
/// Typically, the `Tracker` is used by a higher application service that handles
/// the network layer.
pub struct Tracker {
/// `Tracker` configuration. See [`torrust-tracker-configuration`](torrust_tracker_configuration)
pub config: Arc<Configuration>,
announce_policy: AnnouncePolicy,
/// A database driver implementation: [`Sqlite3`](crate::core::databases::sqlite)
/// or [`MySQL`](crate::core::databases::mysql)
pub database: Arc<Box<dyn Database>>,
mode: TrackerMode,
policy: TrackerPolicy,
keys: tokio::sync::RwLock<std::collections::HashMap<Key, auth::ExpiringKey>>,
whitelist: tokio::sync::RwLock<std::collections::HashSet<InfoHash>>,
pub torrents: Arc<RepositoryAsyncSingle>,
stats_event_sender: Option<Box<dyn statistics::EventSender>>,
stats_repository: statistics::Repo,
external_ip: Option<IpAddr>,
on_reverse_proxy: bool,
}

/// Structure that holds general `Tracker` torrents metrics.
Expand All @@ -500,6 +503,12 @@ pub struct TorrentsMetrics {
pub torrents: u64,
}

#[derive(Copy, Clone, Debug, PartialEq, Default, Constructor)]
pub struct TrackerPolicy {
pub remove_peerless_torrents: bool,
pub max_peer_timeout: u32,
pub persistent_torrent_completed_stat: bool,
}
/// Structure that holds the data returned by the `announce` request.
#[derive(Clone, Debug, PartialEq, Constructor, Default)]
pub struct AnnounceData {
Expand Down Expand Up @@ -556,7 +565,7 @@ impl Tracker {
///
/// Will return a `databases::error::Error` if unable to connect to database. The `Tracker` is responsible for the persistence.
pub fn new(
config: Arc<Configuration>,
config: &Configuration,
stats_event_sender: Option<Box<dyn statistics::EventSender>>,
stats_repository: statistics::Repo,
) -> Result<Tracker, databases::error::Error> {
Expand All @@ -565,14 +574,22 @@ impl Tracker {
let mode = config.mode;

Ok(Tracker {
config,
//config,
announce_policy: AnnouncePolicy::new(config.announce_interval, config.min_announce_interval),
mode,
keys: tokio::sync::RwLock::new(std::collections::HashMap::new()),
whitelist: tokio::sync::RwLock::new(std::collections::HashSet::new()),
torrents: Arc::new(RepositoryAsyncSingle::new()),
stats_event_sender,
stats_repository,
database,
external_ip: config.get_ext_ip(),
policy: TrackerPolicy::new(
config.remove_peerless_torrents,
config.max_peer_timeout,
config.persistent_torrent_completed_stat,
),
on_reverse_proxy: config.on_reverse_proxy,
})
}

Expand All @@ -596,6 +613,19 @@ impl Tracker {
self.is_private()
}

/// Returns `true` is the tracker is in whitelisted mode.
pub fn is_behind_reverse_proxy(&self) -> bool {
self.on_reverse_proxy
}

pub fn get_announce_policy(&self) -> AnnouncePolicy {
self.announce_policy
}

pub fn get_maybe_external_ip(&self) -> Option<IpAddr> {
self.external_ip
}

/// It handles an announce request.
///
/// # Context: Tracker
Expand All @@ -617,18 +647,19 @@ impl Tracker {
// we are actually handling authentication at the handlers level. So I would extract that
// responsibility into another authentication service.

peer.change_ip(&assign_ip_address_to_peer(remote_client_ip, self.config.get_ext_ip()));
debug!("Before: {peer:?}");
peer.change_ip(&assign_ip_address_to_peer(remote_client_ip, self.external_ip));
debug!("After: {peer:?}");

let swarm_stats = self.update_torrent_with_peer_and_get_stats(info_hash, peer).await;
// we should update the torrent and get the stats before we get the peer list.
let stats = self.update_torrent_with_peer_and_get_stats(info_hash, peer).await;

let peers = self.get_torrent_peers_for_peer(info_hash, peer).await;

let policy = AnnouncePolicy::new(self.config.announce_interval, self.config.min_announce_interval);

AnnounceData {
peers,
stats: swarm_stats,
policy,
stats,
policy: self.get_announce_policy(),
}
}

Expand Down Expand Up @@ -727,7 +758,7 @@ impl Tracker {

let (stats, stats_updated) = self.torrents.update_torrent_with_peer_and_get_stats(info_hash, peer).await;

if self.config.persistent_torrent_completed_stat && stats_updated {
if self.policy.persistent_torrent_completed_stat && stats_updated {
let completed = stats.downloaded;
let info_hash = *info_hash;

Expand Down Expand Up @@ -788,17 +819,17 @@ impl Tracker {
let mut torrents_lock = self.torrents.get_torrents_mut().await;

// If we don't need to remove torrents we will use the faster iter
if self.config.remove_peerless_torrents {
if self.policy.remove_peerless_torrents {
let mut cleaned_torrents_map: BTreeMap<InfoHash, torrent::Entry> = BTreeMap::new();

for (info_hash, torrent_entry) in &mut *torrents_lock {
torrent_entry.remove_inactive_peers(self.config.max_peer_timeout);
torrent_entry.remove_inactive_peers(self.policy.max_peer_timeout);

if torrent_entry.peers.is_empty() {
continue;
}

if self.config.persistent_torrent_completed_stat && torrent_entry.completed == 0 {
if self.policy.persistent_torrent_completed_stat && torrent_entry.completed == 0 {
continue;
}

Expand All @@ -808,7 +839,7 @@ impl Tracker {
*torrents_lock = cleaned_torrents_map;
} else {
for torrent_entry in (*torrents_lock).values_mut() {
torrent_entry.remove_inactive_peers(self.config.max_peer_timeout);
torrent_entry.remove_inactive_peers(self.policy.max_peer_timeout);
}
}
}
Expand Down Expand Up @@ -1061,7 +1092,6 @@ mod tests {

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str::FromStr;
use std::sync::Arc;

use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};
use torrust_tracker_test_helpers::configuration;
Expand All @@ -1073,21 +1103,21 @@ mod tests {
use crate::shared::clock::DurationSinceUnixEpoch;

fn public_tracker() -> Tracker {
tracker_factory(configuration::ephemeral_mode_public().into())
tracker_factory(&configuration::ephemeral_mode_public())
}

fn private_tracker() -> Tracker {
tracker_factory(configuration::ephemeral_mode_private().into())
tracker_factory(&configuration::ephemeral_mode_private())
}

fn whitelisted_tracker() -> Tracker {
tracker_factory(configuration::ephemeral_mode_whitelisted().into())
tracker_factory(&configuration::ephemeral_mode_whitelisted())
}

pub fn tracker_persisting_torrents_in_database() -> Tracker {
let mut configuration = configuration::ephemeral();
configuration.persistent_torrent_completed_stat = true;
tracker_factory(Arc::new(configuration))
tracker_factory(&configuration)
}

fn sample_info_hash() -> InfoHash {
Expand Down
4 changes: 2 additions & 2 deletions src/core/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ use crate::core::Tracker;
///
/// Will panic if tracker cannot be instantiated.
#[must_use]
pub fn tracker_factory(config: Arc<Configuration>) -> Tracker {
pub fn tracker_factory(config: &Configuration) -> Tracker {
// Initialize statistics
let (stats_event_sender, stats_repository) = statistics::setup::factory(config.tracker_usage_statistics);

// Initialize Torrust tracker
match Tracker::new(config, stats_event_sender, stats_repository) {
match Tracker::new(&Arc::new(config), stats_event_sender, stats_repository) {
Ok(tracker) => tracker,
Err(error) => {
panic!("{}", error)
Expand Down
6 changes: 3 additions & 3 deletions src/core/services/statistics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ mod tests {
use crate::core::services::statistics::{get_metrics, TrackerMetrics};
use crate::core::services::tracker_factory;

pub fn tracker_configuration() -> Arc<Configuration> {
Arc::new(configuration::ephemeral())
pub fn tracker_configuration() -> Configuration {
configuration::ephemeral()
}

#[tokio::test]
async fn the_statistics_service_should_return_the_tracker_metrics() {
let tracker = Arc::new(tracker_factory(tracker_configuration()));
let tracker = Arc::new(tracker_factory(&tracker_configuration()));

let tracker_metrics = get_metrics(tracker.clone()).await;

Expand Down
Loading

0 comments on commit 3b49257

Please sign in to comment.