diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml index a27ed86..9412895 100644 --- a/.github/workflows/general.yml +++ b/.github/workflows/general.yml @@ -16,6 +16,9 @@ env: CARGO_TERM_COLOR: always SQLX_VERSION: 0.8.0 SQLX_FEATURES: "rustls,postgres" + APP_USER: app + APP_USER_PWD: secret + APP_DB_NAME: newsletter jobs: test: @@ -56,6 +59,18 @@ jobs: # This may be useful for ensuring reproducible builds, to use the exact same set of dependencies that were available when the package was published. # It may also be useful if a newer version of a dependency is published that no longer builds on your system, or has other problems + - name: Create app user in Postgres + run: | + sudo apt-get install postgresql-client + + # Create the application user + CREATE_QUERY="CREATE USER ${APP_USER} WITH PASSWORD '${APP_USER_PWD}';" + PGPASSWORD="password" psql -U "postgres" -h "localhost" -c "${CREATE_QUERY}" + + # Grant create db privileges to the app user + GRANT_QUERY="ALTER USER ${APP_USER} CREATEDB;" + PGPASSWORD="password" psql -U "postgres" -h "localhost" -c "${GRANT_QUERY}" + - name: Migrate database run: | SKIP_DOCKER=true ./scripts/init_db.sh @@ -117,6 +132,17 @@ jobs: --features ${{ env.SQLX_FEATURES }} --no-default-features --locked + - name: Create app user in Postgres + run: | + sudo apt-get install postgresql-client + + # Create the application user + CREATE_QUERY="CREATE USER ${APP_USER} WITH PASSWORD '${APP_USER_PWD}';" + PGPASSWORD="password" psql -U "postgres" -h "localhost" -c "${CREATE_QUERY}" + + # Grant create db privileges to the app user + GRANT_QUERY="ALTER USER ${APP_USER} CREATEDB;" + PGPASSWORD="password" psql -U "postgres" -h "localhost" -c "${GRANT_QUERY}" - name: Migrate database run: SKIP_DOCKER=true ./scripts/init_db.sh - name: Install cargo-llvm-cov diff --git a/scripts/init_db.sh b/scripts/init_db.sh index 8c91502..42625bf 100755 --- a/scripts/init_db.sh +++ b/scripts/init_db.sh @@ -10,16 +10,13 @@ if ! [ -x "$(command -v sqlx)" ]; then exit 1 fi -# Check if a custom user has been set, otherwise default to 'postgres' -DB_USER="${POSTGRES_USER:=postgres}" -# Check if a custom password has been set, otherwise default to 'password' -DB_PASSWORD="${POSTGRES_PASSWORD:=password}" -# Check if a custom database name has been set, otherwise default to 'newsletter' -DB_NAME="${POSTGRES_DB:=newsletter}" -# Check if a custom port has been set, otherwise default to '5432' -DB_PORT="${POSTGRES_PORT:=5432}" -# Check if a custom host has been set, otherwise default to 'localhost' -DB_HOST="${POSTGRES_HOST:=localhost}" +# Check if a custom parameter has been set, otherwise use default values +DB_PORT="${DB_PORT:=5432}" +SUPERUSER="${SUPERUSER:=postgres}" +SUPERUSER_PWD="${SUPERUSER_PWD:=password}" +APP_USER="${APP_USER:=app}" +APP_USER_PWD="${APP_USER_PWD:=secret}" +APP_DB_NAME="${APP_DB_NAME:=newsletter}" # Allow to skip Docker if a dockerized Postgres database is already running if [[ -z "${SKIP_DOCKER}" ]] @@ -34,15 +31,14 @@ then CONTAINER_NAME="postgres_$(date '+%s')" # Launch postgres using Docker docker run \ - -e POSTGRES_USER=${DB_USER} \ - -e POSTGRES_PASSWORD=${DB_PASSWORD} \ - -e POSTGRES_DB=${DB_NAME} \ - --health-cmd="pg_isready -U ${DB_USER} || exit 1" \ + --env POSTGRES_USER=${SUPERUSER} \ + --env POSTGRES_PASSWORD=${SUPERUSER_PWD} \ + --health-cmd="pg_isready -U ${SUPERUSER} || exit 1" \ --health-interval=1s \ --health-timeout=5s \ --health-retries=5 \ - -p "${DB_PORT}":5432 \ - -d \ + --publish "${DB_PORT}":5432 \ + --detach \ --name "${CONTAINER_NAME}" \ postgres -N 1000 # ^ Increased maximum number of connections for testing purposes @@ -54,11 +50,21 @@ then >&2 echo "Postgres is still unavailable - sleeping" sleep 1 done + + # Create the application user + CREATE_QUERY="CREATE USER ${APP_USER} WITH PASSWORD '${APP_USER_PWD}';" + docker exec -it "${CONTAINER_NAME}" psql -U "${SUPERUSER}" -c "${CREATE_QUERY}" + + # Grant create db privileges to the app user + GRANT_QUERY="ALTER USER ${APP_USER} CREATEDB;" + docker exec -it "${CONTAINER_NAME}" psql -U "${SUPERUSER}" -c "${GRANT_QUERY}" fi >&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!" -export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} +# Create the application database +DATABASE_URL=postgres://${APP_USER}:${APP_USER_PWD}@localhost:${DB_PORT}/${APP_DB_NAME} +export DATABASE_URL sqlx database create sqlx migrate run diff --git a/src/configuration.rs b/src/configuration.rs index 2c70df0..c81fd2d 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -31,7 +31,7 @@ pub struct DatabaseSettings { } impl DatabaseSettings { - pub fn without_db(&self) -> PgConnectOptions { + pub fn connect_options(&self) -> PgConnectOptions { let ssl_mode = if self.require_ssl { PgSslMode::Require } else { @@ -43,10 +43,7 @@ impl DatabaseSettings { .password(self.password.expose_secret()) .port(self.port) .ssl_mode(ssl_mode) - } - - pub fn with_db(&self) -> PgConnectOptions { - self.without_db().database(&self.database_name) + .database(&self.database_name) } } diff --git a/src/startup.rs b/src/startup.rs index e226fac..42c8c59 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -55,7 +55,7 @@ impl Application { } pub fn get_connection_pool(configuration: &DatabaseSettings) -> PgPool { - PgPoolOptions::new().connect_lazy_with(configuration.with_db()) + PgPoolOptions::new().connect_lazy_with(configuration.connect_options()) } pub struct ApplicationBaseUrl(pub String); diff --git a/tests/api/helpers.rs b/tests/api/helpers.rs index 7035382..adbc3b0 100644 --- a/tests/api/helpers.rs +++ b/tests/api/helpers.rs @@ -1,3 +1,4 @@ +use secrecy::Secret; use sqlx::{Connection, Executor, PgConnection, PgPool}; use std::sync::LazyLock; use uuid::Uuid; @@ -115,22 +116,27 @@ pub async fn spawn_app() -> TestApp { async fn configure_database(config: &DatabaseSettings) -> PgPool { // Create database - let mut connection = PgConnection::connect_with(&config.without_db()) + let maintenance_settings = DatabaseSettings { + database_name: "postgres".to_string(), + username: "postgres".to_string(), + password: Secret::new("password".to_string()), + ..config.clone() + }; + let mut connection = PgConnection::connect_with(&maintenance_settings.connect_options()) .await .expect("Failed to connect to Postgres"); connection - .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) + .execute(format!(r#"CREATE DATABASE "{}";"#, config.database_name).as_str()) .await .expect("Failed to create database."); // Migrate database - let connection_pool = PgPool::connect_with(config.with_db()) + let connection_pool = PgPool::connect_with(config.connect_options()) .await .expect("Failed to connect to Postgres."); sqlx::migrate!("./migrations") .run(&connection_pool) .await .expect("Failed to migrate the database"); - connection_pool }