From 236e6a47842692aba2676adb2f29b20787283180 Mon Sep 17 00:00:00 2001 From: RubberDuckShobe <42943070+RubberDuckShobe@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:31:48 +0200 Subject: [PATCH] Add files for Docker build + embed migrations --- .dockerignore | 12 +++++++++ Cargo.lock | 62 +++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 ++- Dockerfile | 26 ++++++++++++++++++ src/main.rs | 36 ++++++++++++++++++++++--- src/models/players.rs | 2 -- 6 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..59ff792 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.env +.cargo/ +.github/ +.vscode/ +radio/ +target/ +tests/ +Dockerfile +scripts/ +Wavebreaker.toml +WavebreakerRadio.toml +dump.rdb \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3f24a6b..7ecb0bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,6 +584,17 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "diesel_migrations" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + [[package]] name = "diesel_table_macro_syntax" version = "0.1.0" @@ -656,7 +667,7 @@ dependencies = [ "atomic", "pear", "serde", - "toml", + "toml 0.8.10", "uncased", "version_check", ] @@ -1194,6 +1205,27 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "migrations_internals" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +dependencies = [ + "serde", + "toml 0.7.8", +] + +[[package]] +name = "migrations_macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mime" version = "0.3.17" @@ -2321,6 +2353,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + [[package]] name = "toml" version = "0.8.10" @@ -2342,6 +2386,19 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "toml_edit" version = "0.21.1" @@ -2706,6 +2763,7 @@ dependencies = [ "deadpool-redis", "diesel", "diesel-async", + "diesel_migrations", "figment", "musicbrainz_rs", "num_enum", @@ -2718,7 +2776,7 @@ dependencies = [ "steam-rs", "time", "tokio", - "toml", + "toml 0.8.10", "tower-http", "tracing", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index 3244c87..9355999 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ tokio = "1.38" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diesel = { version = "2.1", features = ["time"] } -diesel-async = { version = "0.4", features = ["postgres", "deadpool"] } +diesel-async = { version = "0.4.1", features = ["postgres", "deadpool", "async-connection-wrapper"] } steam-rs = "0.4" time = { version = "0.3", features = ["serde"] } tower-http = { version = "0.5", features = ["fs", "trace"] } @@ -33,3 +33,4 @@ redis = { version = "0.25", features = ["aio"] } deadpool-redis = "0.15.1" musicbrainz_rs = "0.5.0" clap = { version = "4.5", features = ["derive"] } +diesel_migrations = { version = "2.1.0", features = ["postgres"] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c2f8f71 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# shoutout to Luca Palmieri +# https://www.lpalmieri.com/posts/2020-11-01-zero-to-production-5-how-to-deploy-a-rust-application + +FROM rust:latest AS builder + +# Let's switch our working directory to `app` (equivalent to `cd app`) +# The `app` folder will be created for us by Docker in case it does not +# exist already. +WORKDIR /app +# Install the required system dependencies for our linking configuration +RUN apt update && apt install lld clang -y +# Copy all files from our working environment to our Docker image +COPY . . +# Build in release mode +RUN cargo build --release + +# Runtime stage +FROM debian:bookworm-slim AS runtime + +WORKDIR /app +# Copy the compiled binary from the builder environment +# to our runtime environment +COPY --from=builder /app/target/release/wavebreaker wavebreaker +# OpenSSL isn't statically linked so we need to install it +RUN apt update && apt install openssl ca-certificates -y --no-install-recommends && apt autoremove -y && apt clean -y +ENTRYPOINT ["./wavebreaker"] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index cfba91e..d7a9839 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,12 @@ use axum::{ }; use clap::Parser; use deadpool_redis::Runtime; -use diesel_async::pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager}; +use diesel::pg::Pg; +use diesel_async::{ + async_connection_wrapper::AsyncConnectionWrapper, + pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager}, +}; +use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use figment::{ providers::{Env, Format, Toml}, Figment, @@ -40,6 +45,7 @@ use steam_rs::Steam; use tower_http::trace::TraceLayer; use tracing::{debug, info}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); use crate::game::{routes_as, routes_steam_doubleslash}; @@ -75,6 +81,18 @@ pub struct AppState { redis: deadpool_redis::Pool, } +fn run_migrations( + connection: &mut impl MigrationHarness, +) -> Result<(), Box> { + // This will run the necessary migrations. + // + // See the documentation for `MigrationHarness` for + // all available methods. + connection.run_pending_migrations(MIGRATIONS)?; + + Ok(()) +} + /// Reads the config, initializes database connections and the Steam API client /// /// # Returns @@ -82,7 +100,7 @@ pub struct AppState { /// /// # Errors /// This function can fail if the config file is missing or invalid, the connection to Postgres or Redis fails, or the Steam API key is invalid -fn init_state() -> anyhow::Result { +async fn init_state() -> anyhow::Result { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { @@ -109,6 +127,18 @@ fn init_state() -> anyhow::Result { .build() .context("Failed to build DB pool!")?; + // clone the url because moving the value will screw things up + let pg_url = wavebreaker_config.main.database.clone(); + tokio::task::spawn_blocking(move || { + use diesel::prelude::Connection; + use diesel_async::pg::AsyncPgConnection; + let mut conn = AsyncConnectionWrapper::::establish(&pg_url) + .expect("Failed to establish DB connection for migrations!"); + + run_migrations(&mut conn).expect("Failed to run migrations!"); + }) + .await?; + let redis_cfg = deadpool_redis::Config::from_url(&wavebreaker_config.main.redis); let redis_pool = redis_cfg .create_pool(Some(Runtime::Tokio1)) @@ -157,7 +187,7 @@ fn make_router(state: AppState) -> Router { #[tokio::main] async fn main() -> anyhow::Result<()> { - let state = init_state()?; + let state = init_state().await?; // Parse CLI arguments // and if we have a management command, don't spin up a server diff --git a/src/models/players.rs b/src/models/players.rs index 2dd31cb..c346267 100644 --- a/src/models/players.rs +++ b/src/models/players.rs @@ -11,11 +11,9 @@ use diesel::{ }; use diesel_async::{AsyncPgConnection, RunQueryDsl}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use redis::AsyncCommands; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; use steam_rs::steam_id::SteamId; -use tracing::info; use crate::{ models::{rivalries::Rivalry, scores::Score},