diff --git a/Cargo.lock b/Cargo.lock index d08f087..4c39ad2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,60 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -1700,6 +1754,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md5" version = "0.7.0" @@ -2769,6 +2829,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2862,6 +2932,7 @@ dependencies = [ name = "siranga" version = "0.0.0" dependencies = [ + "axum", "bytes", "clap", "clio", @@ -3231,6 +3302,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3251,6 +3323,7 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index 4345403..a9f6d26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ default-run = "siranga" license = "AGPL-3.0-only" [dependencies] +axum = "0.8.3" bytes = "1.10.1" clap = { version = "4.5.35", features = ["derive"] } clio = { version = "0.3.5", features = ["clap-parse"] } diff --git a/manifests/deployment.yaml b/manifests/deployment.yaml index d4b6399..d3b276e 100644 --- a/manifests/deployment.yaml +++ b/manifests/deployment.yaml @@ -35,6 +35,8 @@ spec: containerPort: 2222 - name: http containerPort: 3000 + - name: metrics + containerPort: 4000 volumeMounts: - name: credentials readOnly: true @@ -59,6 +61,12 @@ spec: value: /secrets/credentials/password - name: PRIVATE_KEY_FILE value: /secrets/key/private.pem + livenessProbe: + httpGet: + path: /healthz + port: metrics + initialDelaySeconds: 3 + periodSeconds: 3 volumes: - name: credentials secret: diff --git a/src/main.rs b/src/main.rs index 2d2f1ed..4da7610 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ use std::net::SocketAddr; use std::path::Path; use std::time::Duration; +use axum::Router; +use axum::routing::get; use color_eyre::eyre::Context; use dotenvy::dotenv; use rand::rngs::OsRng; @@ -54,6 +56,10 @@ async fn shutdown_task(token: CancellationToken) { } } +async fn axum_graceful_shutdown(token: CancellationToken) { + token.cancelled().await; +} + #[tokio::main] async fn main() -> color_eyre::Result<()> { color_eyre::install()?; @@ -85,12 +91,18 @@ async fn main() -> color_eyre::Result<()> { russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519)? }; - let http_port = std::env::var("HTTP_PORT") - .map(|port| port.parse().wrap_err_with(|| format!("HTTP_PORT={port}"))) - .unwrap_or(Ok(3000))?; let ssh_port = std::env::var("SSH_PORT") .map(|port| port.parse().wrap_err_with(|| format!("SSH_PORT={port}"))) .unwrap_or(Ok(2222))?; + let http_port = std::env::var("HTTP_PORT") + .map(|port| port.parse().wrap_err_with(|| format!("HTTP_PORT={port}"))) + .unwrap_or(Ok(3000))?; + let metrics_port = std::env::var("METRICS_PORT") + .map(|port| { + port.parse() + .wrap_err_with(|| format!("METRICS_PORT={port}")) + }) + .unwrap_or(Ok(4000))?; let domain = std::env::var("TUNNEL_DOMAIN").unwrap_or_else(|_| format!("localhost:{http_port}")); @@ -114,13 +126,24 @@ async fn main() -> color_eyre::Result<()> { let http_task = service.serve(http_listener, token.clone()); info!("HTTP is available on {http_addr}"); + let metrics_app = Router::new().route("/health", get(async || "UP")); + let metrics_addr = SocketAddr::from(([0, 0, 0, 0], metrics_port)); + let metrics_listener = TcpListener::bind(metrics_addr).await?; + let metrics = axum::serve(metrics_listener, metrics_app) + .with_graceful_shutdown(axum_graceful_shutdown(token.clone())); + info!("Metrics are available on {http_addr}"); + select! { - _ = join!(ldap_handle, ssh_task, http_task) => { + _ = join!(ldap_handle, ssh_task, http_task, metrics.into_future()) => { info!("Shutdown gracefully"); } _ = shutdown_task(token.clone()) => { error!("Failed to shutdown gracefully"); } + _ = tokio::time::sleep(std::time::Duration::from_secs(30)) => { + debug!("Simulating issue"); + std::future::pending::<()>().await; + } }; Ok(())