commit 0428a219122d9eb36675c7a601b12a89ea539e76 Author: dobiadi Date: Sun Apr 21 23:02:59 2024 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bfcf9db --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "web-template" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +axum = "0" +libc = "0" +tokio = { version = "1", features = ["full"] } +tower = "0" +tower-http = { version = "0", features = ["trace", "timeout"] } +tracing = "0" +tracing-subscriber = { version = "0", features = ["json", "env-filter"] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e760935 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +ARG ALPINE_VERSION=latest +FROM alpine:$ALPINE_VERSION as builder + +COPY Cargo.lock /usr/src/ +COPY Cargo.toml /usr/src/ +COPY src/ /usr/src/src/ + +WORKDIR /usr/src + +RUN apk add --no-cache rustup build-base && \ + rustup-init -qy --profile=minimal && \ + source "$HOME/.cargo/env" && \ + cargo build --release --target=x86_64-unknown-linux-musl + +FROM alpine:$ALPINE_VERSION + +COPY --from=builder /usr/src/target/x86_64-unknown-linux-musl/release/web-template /usr/bin/ + +CMD [ "/usr/bin/web-template" ] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..13822f7 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,64 @@ +use std::time::Duration; + +use axum::{routing, Router}; +use tokio::{ + net::TcpListener, + signal::{self, unix::SignalKind}, + task::JoinSet, +}; +use tower_http::{timeout::TimeoutLayer, trace::TraceLayer}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + tracing_subscriber::registry() + .with(tracing_subscriber::EnvFilter::from_env("LOG_LEVEL")) + .with(tracing_subscriber::fmt::layer().json()) + .init(); + + let app = Router::new() + .route("/", routing::get(|| async { "Hello world!" })) + .layer(( + TraceLayer::new_for_http(), + TimeoutLayer::new(Duration::from_secs(10)), + )); + + let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap(); + + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await +} + +const SIGNALS: &'static [(i32, &'static str)] = &[ + (libc::SIGTERM, "SIGTERM"), + (libc::SIGQUIT, "SIGQUIT"), + (libc::SIGINT, "SIGINT"), +]; + +async fn shutdown_signal() { + let term_signals = [ + SignalKind::terminate(), + SignalKind::quit(), + SignalKind::interrupt(), + ]; + + let mut futures = JoinSet::new(); + + for term_signal in term_signals { + futures.spawn(async move { + signal::unix::signal(term_signal) + .expect("failed to install signal handler") + .recv() + .await; + term_signal.as_raw_value() + }); + } + + let kind = futures.join_next().await; + + if let Some(Ok(kind)) = kind { + let name = SIGNALS.iter().find(|e| e.0 == kind).unwrap().1; + tracing::info!("{} received, shutting down gracefully", name); + } +}