diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..d696ce99 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.github/ +.vscode/ +target/ +recipe.json diff --git a/.gitignore b/.gitignore index c41cc9e3..84aeaabf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/target \ No newline at end of file +/target +recipe.json diff --git a/Cargo.lock b/Cargo.lock index cf0567be..bf7d1235 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,6 +203,19 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elegant-departure" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259261d0b72dc618a177af380b2944fa44e22fedbdabb920f7b47edffb1df337" +dependencies = [ + "futures", + "lazy_static", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -736,6 +749,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.174" @@ -1397,6 +1416,7 @@ dependencies = [ name = "server" version = "0.1.0" dependencies = [ + "elegant-departure", "proto-codegen", "service", "tokio", diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..4af47d48 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +FROM rust:slim-bookworm AS build-chef + +WORKDIR /work +RUN cargo install cargo-chef --locked + +FROM build-chef AS build-planner + +COPY . . +RUN cargo chef prepare --recipe-path recipe.json --bin server + +FROM build-chef AS build-server + +ARG CARGO_FEATURES="" +ENV CARGO_FEATURES=${CARGO_FEATURES} + +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends protobuf-compiler \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=build-planner /work/recipe.json recipe.json + +RUN cargo chef cook --release --features=${CARGO_FEATURES} --recipe-path recipe.json + +COPY . . + +# NOTE: Simplified build that leaves debug info in the binary. As the binary grows, we will want to +# move this out and instead upload it directly to the registry and Sentry. +RUN cargo build --release --features=${CARGO_FEATURES} --bin server + +FROM debian:bookworm-slim + +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends ca-certificates gosu \ + && rm -rf /var/lib/apt/lists/* + +ENV \ + SERVER_UID=10001 \ + SERVER_GID=10001 + +# Create a new user and group with fixed uid/gid +RUN groupadd --system fss --gid $SERVER_GID \ + && useradd --system --gid fss --uid $SERVER_UID fss + +VOLUME ["/data"] +EXPOSE 50051 + +COPY --from=build-server /work/target/release/server /bin +COPY docker-entrypoint.sh /docker-entrypoint.sh + +ENTRYPOINT ["/bin/bash", "/docker-entrypoint.sh"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 00000000..85a1e773 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -eu + +if [ "$(id -u)" == "0" ]; then + exec gosu fss /bin/server "$@" +else + exec /bin/server "$@" +fi diff --git a/server/Cargo.toml b/server/Cargo.toml index 14d903e0..e4c8d2a8 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] -tokio = { version = "1.45.1", features = ["full"] } -tonic = { version = "0.13.1" } +elegant-departure = { version = "0.3.1", features = ["tokio"] } proto-codegen = { path = "../proto-codegen" } service = { path = "../service" } +tokio = { version = "1.45.1", features = ["full"] } +tonic = { version = "0.13.1" } diff --git a/server/src/main.rs b/server/src/main.rs index 4557a2f2..018554f0 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -5,11 +5,12 @@ //! an `HTTP` layer which can serve files directly to *external clients*. use proto_codegen::storage::{AllocateBlobRequest, AllocateBlobResponse, StorageId}; +use tokio::signal::unix::SignalKind; use tonic::{Request, Response, Status, transport::Server}; use proto_codegen::storage::storage_server::{Storage, StorageServer}; -#[derive(Default)] +#[derive(Debug, Default)] pub struct MockStorage {} #[tonic::async_trait] @@ -37,13 +38,19 @@ impl Storage for MockStorage { #[tokio::main] async fn main() -> Result<(), Box> { let addr = "0.0.0.0:50051".parse().unwrap(); - let greeter = MockStorage::default(); + let storage = MockStorage::default(); println!("Server listening on {addr}"); + let shutdown = elegant_departure::tokio::depart() + .on_termination() + .on_sigint() + .on_signal(SignalKind::hangup()) + .on_signal(SignalKind::quit()); + Server::builder() - .add_service(StorageServer::new(greeter)) - .serve(addr) + .add_service(StorageServer::new(storage)) + .serve_with_shutdown(addr, shutdown) .await?; Ok(())