Skip to content

Commit 00c329c

Browse files
committed
dev: cleanup bootstrap of services
1 parent 38d81ee commit 00c329c

File tree

17 files changed

+233
-115
lines changed

17 files changed

+233
-115
lines changed

packages/configuration/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ use config::{Config, ConfigError, File, FileFormat};
239239
use serde::{Deserialize, Serialize};
240240
use serde_with::{serde_as, NoneAsEmptyString};
241241
use thiserror::Error;
242-
use torrust_tracker_located_error::{Located, LocatedError};
242+
use torrust_tracker_located_error::{DynError, Located, LocatedError};
243243
use torrust_tracker_primitives::{DatabaseDriver, TrackerMode};
244244

245245
/// Information required for loading config
@@ -289,7 +289,7 @@ impl Info {
289289

290290
fs::read_to_string(config_path)
291291
.map_err(|e| Error::UnableToLoadFromConfigFile {
292-
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
292+
source: (Arc::new(e) as DynError).into(),
293293
})?
294294
.parse()
295295
.map_err(|_e: std::convert::Infallible| Error::Infallible)?

packages/located-error/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ use std::error::Error;
3333
use std::panic::Location;
3434
use std::sync::Arc;
3535

36+
pub type DynError = Arc<dyn std::error::Error + Send + Sync>;
37+
3638
/// A generic wrapper around an error.
3739
///
3840
/// Where `E` is the inner error (source error).
@@ -96,7 +98,7 @@ where
9698
}
9799

98100
#[allow(clippy::from_over_into)]
99-
impl<'a> Into<LocatedError<'a, dyn std::error::Error + Send + Sync>> for Arc<dyn std::error::Error + Send + Sync> {
101+
impl<'a> Into<LocatedError<'a, dyn std::error::Error + Send + Sync>> for DynError {
100102
#[track_caller]
101103
fn into(self) -> LocatedError<'a, dyn std::error::Error + Send + Sync> {
102104
LocatedError {

src/bootstrap/jobs/http_tracker.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use log::info;
1818
use tokio::task::JoinHandle;
1919
use torrust_tracker_configuration::HttpTracker;
2020

21+
use super::make_rust_tls;
2122
use crate::core;
2223
use crate::servers::http::server::{HttpServer, HttpServerLauncher};
2324
use crate::servers::http::v1::launcher::Launcher;
@@ -39,17 +40,9 @@ pub async fn start_job(config: &HttpTracker, tracker: Arc<core::Tracker>, versio
3940
.parse::<std::net::SocketAddr>()
4041
.expect("Tracker API bind_address invalid.");
4142

42-
let tls = if let (true, Some(cert), Some(key)) = (&config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path) {
43-
let tls = RustlsConfig::from_pem_file(cert, key)
44-
.await
45-
.expect("Could not read tls cert.");
46-
info!("Using https: cert path: {cert}.");
47-
info!("Using https: key path: {cert}.");
48-
Some(tls)
49-
} else {
50-
info!("Loading HTTP tracker without TLS.");
51-
None
52-
};
43+
let tls = make_rust_tls(config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path)
44+
.await
45+
.map(|tls| tls.expect("tls config failed"));
5346

5447
match version {
5548
Version::V1 => Some(start_v1(socket, tls, tracker.clone()).await),

src/bootstrap/jobs/mod.rs

+46
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,49 @@ pub mod udp_tracker;
1919
pub struct Started {
2020
pub address: std::net::SocketAddr,
2121
}
22+
23+
pub async fn make_rust_tls(enabled: bool, cert: &Option<String>, key: &Option<String>) -> Option<Result<RustlsConfig, Error>> {
24+
if enabled {
25+
if let (Some(cert), Some(key)) = (cert, key) {
26+
info!("Using https: cert path: {cert}.");
27+
info!("Using https: key path: {cert}.");
28+
29+
Some(
30+
RustlsConfig::from_pem_file(cert, key)
31+
.await
32+
.map_err(|err| Error::BadTlsConfig {
33+
source: (Arc::new(err) as DynError).into(),
34+
}),
35+
)
36+
} else {
37+
Some(Err(Error::MissingTlsConfig {
38+
location: Location::caller(),
39+
}))
40+
}
41+
} else {
42+
info!("tls not enabled");
43+
None
44+
}
45+
}
46+
47+
use std::panic::Location;
48+
use std::sync::Arc;
49+
50+
use axum_server::tls_rustls::RustlsConfig;
51+
use log::info;
52+
use thiserror::Error;
53+
use torrust_tracker_located_error::{DynError, LocatedError};
54+
55+
/// Error returned by the Bootstrap Process.
56+
#[derive(Error, Debug)]
57+
pub enum Error {
58+
/// Enabled tls but missing config.
59+
#[error("tls config missing")]
60+
MissingTlsConfig { location: &'static Location<'static> },
61+
62+
/// Unable to parse tls Config.
63+
#[error("bad tls config: {source}")]
64+
BadTlsConfig {
65+
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
66+
},
67+
}

src/bootstrap/jobs/tracker_apis.rs

+6-13
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use log::info;
2828
use tokio::task::JoinHandle;
2929
use torrust_tracker_configuration::HttpApi;
3030

31+
use super::make_rust_tls;
3132
use crate::core;
3233
use crate::servers::apis::server::{ApiServer, Launcher};
3334
use crate::servers::apis::Version;
@@ -54,25 +55,17 @@ pub struct ApiServerJobStarted();
5455
///
5556
pub async fn start_job(config: &HttpApi, tracker: Arc<core::Tracker>, version: Version) -> Option<JoinHandle<()>> {
5657
if config.enabled {
57-
let socket = config
58+
let bind_to = config
5859
.bind_address
5960
.parse::<std::net::SocketAddr>()
6061
.expect("Tracker API bind_address invalid.");
6162

62-
let tls = if let (true, Some(cert), Some(key)) = (&config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path) {
63-
let tls = RustlsConfig::from_pem_file(cert, key)
64-
.await
65-
.expect("Could not read tls cert.");
66-
info!("Using https: cert path: {cert}.");
67-
info!("Using https: key path: {cert}.");
68-
Some(tls)
69-
} else {
70-
info!("Loading HTTP tracker without TLS.");
71-
None
72-
};
63+
let tls = make_rust_tls(config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path)
64+
.await
65+
.map(|tls| tls.expect("tls config failed"));
7366

7467
match version {
75-
Version::V1 => Some(start_v1(socket, tls, tracker.clone()).await),
68+
Version::V1 => Some(start_v1(bind_to, tls, tracker.clone()).await),
7669
}
7770
} else {
7871
info!("Note: Not loading Http Tracker Service, Not Enabled in Configuration.");

src/core/auth.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use rand::distributions::Alphanumeric;
4747
use rand::{thread_rng, Rng};
4848
use serde::{Deserialize, Serialize};
4949
use thiserror::Error;
50-
use torrust_tracker_located_error::LocatedError;
50+
use torrust_tracker_located_error::{DynError, LocatedError};
5151

5252
use crate::shared::bit_torrent::common::AUTH_KEY_LENGTH;
5353
use crate::shared::clock::{convert_from_timestamp_to_datetime_utc, Current, DurationSinceUnixEpoch, Time, TimeNow};
@@ -185,7 +185,7 @@ pub enum Error {
185185
impl From<r2d2_sqlite::rusqlite::Error> for Error {
186186
fn from(e: r2d2_sqlite::rusqlite::Error) -> Self {
187187
Error::KeyVerificationError {
188-
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
188+
source: (Arc::new(e) as DynError).into(),
189189
}
190190
}
191191
}

src/core/databases/error.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::panic::Location;
55
use std::sync::Arc;
66

77
use r2d2_mysql::mysql::UrlError;
8-
use torrust_tracker_located_error::{Located, LocatedError};
8+
use torrust_tracker_located_error::{DynError, Located, LocatedError};
99
use torrust_tracker_primitives::DatabaseDriver;
1010

1111
#[derive(thiserror::Error, Debug, Clone)]
@@ -59,11 +59,11 @@ impl From<r2d2_sqlite::rusqlite::Error> for Error {
5959
fn from(err: r2d2_sqlite::rusqlite::Error) -> Self {
6060
match err {
6161
r2d2_sqlite::rusqlite::Error::QueryReturnedNoRows => Error::QueryReturnedNoRows {
62-
source: (Arc::new(err) as Arc<dyn std::error::Error + Send + Sync>).into(),
62+
source: (Arc::new(err) as DynError).into(),
6363
driver: DatabaseDriver::Sqlite3,
6464
},
6565
_ => Error::InvalidQuery {
66-
source: (Arc::new(err) as Arc<dyn std::error::Error + Send + Sync>).into(),
66+
source: (Arc::new(err) as DynError).into(),
6767
driver: DatabaseDriver::Sqlite3,
6868
},
6969
}
@@ -73,7 +73,7 @@ impl From<r2d2_sqlite::rusqlite::Error> for Error {
7373
impl From<r2d2_mysql::mysql::Error> for Error {
7474
#[track_caller]
7575
fn from(err: r2d2_mysql::mysql::Error) -> Self {
76-
let e: Arc<dyn std::error::Error + Send + Sync> = Arc::new(err);
76+
let e: DynError = Arc::new(err);
7777
Error::InvalidQuery {
7878
source: e.into(),
7979
driver: DatabaseDriver::MySQL,

src/core/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ use std::time::Duration;
448448
use futures::future::join_all;
449449
use tokio::sync::mpsc::error::SendError;
450450
use torrust_tracker_configuration::Configuration;
451+
use torrust_tracker_located_error::DynError;
451452
use torrust_tracker_primitives::TrackerMode;
452453

453454
use self::auth::Key;
@@ -933,7 +934,7 @@ impl Tracker {
933934
if let Err(e) = self.verify_auth_key(key).await {
934935
return Err(Error::PeerKeyNotValid {
935936
key: key.clone(),
936-
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
937+
source: (Arc::new(e) as DynError).into(),
937938
});
938939
}
939940
}

src/servers/apis/server.rs

+25-34
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use tokio::sync::oneshot::{Receiver, Sender};
3535
use super::routes::router;
3636
use crate::bootstrap::jobs::Started;
3737
use crate::core::Tracker;
38-
use crate::servers::signals::{shutdown_signal_with_message, Halted};
38+
use crate::servers::signals::{graceful_shutdown, Halted};
3939

4040
/// Errors that can occur when starting or stopping the API server.
4141
#[derive(Debug)]
@@ -97,10 +97,7 @@ impl ApiServer<Stopped> {
9797
let launcher = self.state.launcher;
9898

9999
let task = tokio::spawn(async move {
100-
let server = launcher.start(tracker, tx_start, rx_halt);
101-
102-
server.await;
103-
100+
launcher.start(tracker, tx_start, rx_halt).await;
104101
launcher
105102
});
106103

@@ -153,30 +150,30 @@ impl Launcher {
153150
/// Will panic if unable to bind to the socket, or unable to get the address of the bound socket.
154151
/// Will also panic if unable to send message regarding the bound socket address.
155152
pub fn start(&self, tracker: Arc<Tracker>, tx_start: Sender<Started>, rx_halt: Receiver<Halted>) -> BoxFuture<'static, ()> {
156-
let app = router(tracker);
153+
let router = router(tracker);
157154
let socket = std::net::TcpListener::bind(self.bind_to).expect("Could not bind tcp_listener to address.");
158155
let address = socket.local_addr().expect("Could not get local_addr from tcp_listener.");
159156

160157
let handle = Handle::new();
161-
let handel_cloned = handle.clone();
162158

163-
tokio::task::spawn(async move {
164-
shutdown_signal_with_message(rx_halt, format!("Halting API Service Bound to Socket: {address}")).await;
165-
handel_cloned.graceful_shutdown(None);
166-
});
159+
tokio::task::spawn(graceful_shutdown(
160+
handle.clone(),
161+
rx_halt,
162+
format!("shutting down http server on socket address: {address}"),
163+
));
167164

168165
let tls = self.tls.clone();
169166

170167
let running = Box::pin(async {
171168
match tls {
172169
Some(tls) => axum_server::from_tcp_rustls(socket, tls)
173170
.handle(handle)
174-
.serve(app.into_make_service_with_connect_info::<std::net::SocketAddr>())
171+
.serve(router.into_make_service_with_connect_info::<std::net::SocketAddr>())
175172
.await
176173
.expect("Axum server crashed."),
177174
None => axum_server::from_tcp(socket)
178175
.handle(handle)
179-
.serve(app.into_make_service_with_connect_info::<std::net::SocketAddr>())
176+
.serve(router.into_make_service_with_connect_info::<std::net::SocketAddr>())
180177
.await
181178
.expect("Axum server crashed."),
182179
}
@@ -194,37 +191,31 @@ impl Launcher {
194191
mod tests {
195192
use std::sync::Arc;
196193

197-
use torrust_tracker_configuration::Configuration;
198-
use torrust_tracker_test_helpers::configuration;
194+
use torrust_tracker_test_helpers::configuration::ephemeral_mode_public;
199195

200-
use crate::core;
201-
use crate::core::statistics;
196+
use crate::bootstrap::app::initialize_with_configuration;
197+
use crate::bootstrap::jobs::make_rust_tls;
202198
use crate::servers::apis::server::{ApiServer, Launcher};
203199

204-
fn tracker_configuration() -> Arc<Configuration> {
205-
Arc::new(configuration::ephemeral())
206-
}
207-
208200
#[tokio::test]
209-
async fn it_should_be_able_to_start_from_stopped_state_and_then_stop_again() {
210-
let cfg = tracker_configuration();
211-
212-
let tracker = Arc::new(core::Tracker::new(cfg.clone(), None, statistics::Repo::new()).unwrap());
201+
async fn it_should_be_able_to_start_and_stop() {
202+
let cfg = Arc::new(ephemeral_mode_public());
203+
let tracker = initialize_with_configuration(&cfg);
204+
let config = &cfg.http_trackers[0];
213205

214-
let bind_to = cfg
215-
.http_api
206+
let bind_to = config
216207
.bind_address
217208
.parse::<std::net::SocketAddr>()
218209
.expect("Tracker API bind_address invalid.");
219210

220-
let stopped_api_server = ApiServer::new(Launcher::new(bind_to, None));
221-
222-
let running_api_server_result = stopped_api_server.start(tracker).await;
223-
224-
assert!(running_api_server_result.is_ok());
211+
let tls = make_rust_tls(config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path)
212+
.await
213+
.map(|tls| tls.expect("tls config failed"));
225214

226-
let running_api_server = running_api_server_result.unwrap();
215+
let stopped = ApiServer::new(Launcher::new(bind_to, tls));
216+
let started = stopped.start(tracker).await.expect("it should start the server");
217+
let stopped = started.stop().await.expect("it should stop the server");
227218

228-
assert!(running_api_server.stop().await.is_ok());
219+
assert_eq!(stopped.state.launcher.bind_to, bind_to);
229220
}
230221
}

src/servers/http/server.rs

+34
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,37 @@ impl<I: HttpServerLauncher> HttpServer<Running<I>> {
144144
})
145145
}
146146
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
use std::sync::Arc;
151+
152+
use torrust_tracker_test_helpers::configuration::ephemeral_mode_public;
153+
154+
use crate::bootstrap::app::initialize_with_configuration;
155+
use crate::bootstrap::jobs::make_rust_tls;
156+
use crate::servers::http::server::{HttpServer, HttpServerLauncher};
157+
use crate::servers::http::v1::launcher::Launcher;
158+
159+
#[tokio::test]
160+
async fn it_should_be_able_to_start_and_stop() {
161+
let cfg = Arc::new(ephemeral_mode_public());
162+
let tracker = initialize_with_configuration(&cfg);
163+
let config = &cfg.http_trackers[0];
164+
165+
let bind_to = config
166+
.bind_address
167+
.parse::<std::net::SocketAddr>()
168+
.expect("Tracker API bind_address invalid.");
169+
170+
let tls = make_rust_tls(config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path)
171+
.await
172+
.map(|tls| tls.expect("tls config failed"));
173+
174+
let stopped = HttpServer::new(Launcher::new(bind_to, tls));
175+
let started = stopped.start(tracker).await.expect("it should start the server");
176+
let stopped = started.stop().await.expect("it should stop the server");
177+
178+
assert_eq!(stopped.state.launcher.bind_to, bind_to);
179+
}
180+
}

0 commit comments

Comments
 (0)