Skip to content

Commit 42d5c89

Browse files
committed
feat: [torrust#508] add container healthcheck for API
1 parent 3141296 commit 42d5c89

File tree

10 files changed

+94
-8
lines changed

10 files changed

+94
-8
lines changed

Containerfile

+4-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ COPY --from=build \
8585
RUN cargo nextest run --workspace-remap /test/src/ --extract-to /test/src/ --no-run --archive-file /test/torrust-tracker.tar.zst
8686
RUN cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json
8787

88-
RUN mkdir -p /app/bin/; cp -l /test/src/target/release/torrust-tracker /app/bin/torrust-tracker
88+
RUN mkdir -p /app/bin/; cp -l /test/src/target/release/torrust-tracker /app/bin/torrust-tracker; cp -l /test/src/target/release/http_health_check /app/bin/http_health_check
8989
RUN mkdir -p /app/lib/; cp -l $(realpath $(ldd /app/bin/torrust-tracker | grep "libz\.so\.1" | awk '{print $3}')) /app/lib/libz.so.1
9090
RUN chown -R root:root /app; chmod -R u=rw,go=r,a+X /app; chmod -R a+x /app/bin
9191

@@ -136,5 +136,7 @@ CMD ["sh"]
136136
FROM runtime as release
137137
ENV RUNTIME="release"
138138
COPY --from=test /app/ /usr/
139-
# HEALTHCHECK CMD ["/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "localhost:${API_PORT}/version"]
139+
HEALTHCHECK --interval=5s --timeout=5s --start-period=3s --retries=3 \
140+
CMD /usr/bin/http_health_check http://localhost:${API_PORT}/health_check \
141+
|| exit 1
140142
CMD ["/usr/bin/torrust-tracker"]

src/bin/http_health_check.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use std::{env, process};
1010
async fn main() {
1111
let args: Vec<String> = env::args().collect();
1212
if args.len() != 2 {
13-
eprintln!("Usage: cargo run --bin health_check <HEALTH_URL>");
14-
eprintln!("Example: cargo run --bin http_health_check http://localhost:1212/api/v1/stats?token=MyAccessToken");
13+
eprintln!("Usage: cargo run --bin http_health_check <HEALTH_URL>");
14+
eprintln!("Example: cargo run --bin http_health_check http://127.0.0.1:1212/health_check");
1515
std::process::exit(1);
1616
}
1717

@@ -34,4 +34,4 @@ async fn main() {
3434
process::exit(1);
3535
}
3636
}
37-
}
37+
}

src/servers/apis/routes.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,28 @@
77
//! first path segment. For example: `/api/v1/torrents`.
88
use std::sync::Arc;
99

10+
use axum::routing::get;
1011
use axum::{middleware, Router};
1112
use tower_http::compression::CompressionLayer;
1213

1314
use super::v1;
15+
use super::v1::context::health_check::handlers::health_check_handler;
1416
use crate::tracker::Tracker;
1517

1618
/// Add all API routes to the router.
1719
#[allow(clippy::needless_pass_by_value)]
1820
pub fn router(tracker: Arc<Tracker>) -> Router {
1921
let router = Router::new();
2022

21-
let prefix = "/api";
23+
let api_url_prefix = "/api";
2224

23-
let router = v1::routes::add(prefix, router, tracker.clone());
25+
let router = v1::routes::add(api_url_prefix, router, tracker.clone());
2426

2527
router
2628
.layer(middleware::from_fn_with_state(
2729
tracker.config.clone(),
2830
v1::middlewares::auth::auth,
2931
))
32+
.route("/health_check", get(health_check_handler))
3033
.layer(CompressionLayer::new())
3134
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//! API handlers for the [`stats`](crate::servers::apis::v1::context::health_check)
2+
//! API context.
3+
4+
use axum::Json;
5+
6+
use super::resources::{Report, Status};
7+
8+
/// Endpoint for container health check.
9+
pub async fn health_check_handler() -> Json<Report> {
10+
Json(Report { status: Status::Ok })
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! API health check endpoint.
2+
//!
3+
//! It is used to check is the service is running. Especially for containers.
4+
//!
5+
//! # Endpoints
6+
//!
7+
//! - [Health Check](#health-check)
8+
//!
9+
//! # Health Check
10+
//!
11+
//! `GET /health_check`
12+
//!
13+
//! Returns the API status.
14+
//!
15+
//! **Example request**
16+
//!
17+
//! ```bash
18+
//! curl "http://127.0.0.1:1212/health_check"
19+
//! ```
20+
//!
21+
//! **Example response** `200`
22+
//!
23+
//! ```json
24+
//! {
25+
//! "status": "Ok",
26+
//! }
27+
//! ```
28+
//!
29+
//! **Resource**
30+
//!
31+
//! Refer to the API [`Stats`](crate::servers::apis::v1::context::stats::resources::health_check)
32+
//! resource for more information about the response attributes.
33+
pub mod handlers;
34+
pub mod resources;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! API resources for the [`stats`](crate::servers::apis::v1::context::health_check)
2+
//! API context.
3+
use serde::{Deserialize, Serialize};
4+
5+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
6+
pub enum Status {
7+
Ok,
8+
Error,
9+
}
10+
11+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
12+
pub struct Report {
13+
pub status: Status,
14+
}

src/servers/apis/v1/context/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! Each context is a module that contains the API endpoints related to a
44
//! specific resource group.
55
pub mod auth_key;
6+
pub mod health_check;
67
pub mod stats;
78
pub mod torrent;
89
pub mod whitelist;

tests/servers/api/v1/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl Client {
101101
}
102102
}
103103

104-
async fn get(path: &str, query: Option<Query>) -> Response {
104+
pub async fn get(path: &str, query: Option<Query>) -> Response {
105105
match query {
106106
Some(params) => reqwest::Client::builder()
107107
.build()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use torrust_tracker::servers::apis::v1::context::health_check::resources::{Report, Status};
2+
use torrust_tracker_test_helpers::configuration;
3+
4+
use crate::servers::api::test_environment::running_test_environment;
5+
use crate::servers::api::v1::client::get;
6+
7+
#[tokio::test]
8+
async fn health_check_endpoint_should_return_status_ok_if_api_is_running() {
9+
let test_env = running_test_environment(configuration::ephemeral()).await;
10+
11+
let url = format!("http://{}/health_check", test_env.get_connection_info().bind_address);
12+
13+
let response = get(&url, None).await;
14+
15+
assert_eq!(response.status(), 200);
16+
assert_eq!(response.headers().get("content-type").unwrap(), "application/json");
17+
assert_eq!(response.json::<Report>().await.unwrap(), Report { status: Status::Ok });
18+
19+
test_env.stop().await;
20+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod auth_key;
2+
pub mod health_check;
23
pub mod stats;
34
pub mod torrent;
45
pub mod whitelist;

0 commit comments

Comments
 (0)