From 22fac1e74d4ae2c4fc309c8a2c9264475e678c7b Mon Sep 17 00:00:00 2001 From: Yago Iglesias Date: Sat, 2 Sep 2023 00:19:06 +0200 Subject: [PATCH] feat(database-config): Allow ssl and refactor configuration --- configuration/local.yml | 2 ++ configuration/production.yml | 2 ++ spec.yml | 19 ++++++++++++++++ src/configurations.rs | 43 ++++++++++++++++++++---------------- src/main.rs | 9 +------- tests/health_check.rs | 10 ++++----- 6 files changed, 52 insertions(+), 33 deletions(-) diff --git a/configuration/local.yml b/configuration/local.yml index ab6d434..764c4a7 100644 --- a/configuration/local.yml +++ b/configuration/local.yml @@ -1,2 +1,4 @@ application: host: 127.0.0.1 +database: + require_ssl: false diff --git a/configuration/production.yml b/configuration/production.yml index b936a88..cd4608a 100644 --- a/configuration/production.yml +++ b/configuration/production.yml @@ -1,2 +1,4 @@ application: host: 0.0.0.0 +database: + require_ssl: true diff --git a/spec.yml b/spec.yml index 47704db..9874a17 100644 --- a/spec.yml +++ b/spec.yml @@ -36,6 +36,25 @@ services: # All incoming requests should be routed to our app routes: - path: / + envs: + - key: APP_APPLICATION__BASE_URL + scope: RUN_TIME + value: ${APP_URL} + - key: APP_DATABASE__USERNAME + scope: RUN_TIME + value: ${newsletter.USERNAME} + - key: APP_DATABASE__PASSWORD + scope: RUN_TIME + value: ${newsletter.PASSWORD} + - key: APP_DATABASE__HOST + scope: RUN_TIME + value: ${newsletter.HOSTNAME} + - key: APP_DATABASE__PORT + scope: RUN_TIME + value: ${newsletter.PORT} + - key: APP_DATABASE__DATABASE_NAME + scope: RUN_TIME + value: ${newsletter.DATABASE} databases: # PG = Postgres diff --git a/src/configurations.rs b/src/configurations.rs index ada0459..3f72bb3 100644 --- a/src/configurations.rs +++ b/src/configurations.rs @@ -1,6 +1,10 @@ use config::Config; use secrecy::{ExposeSecret, Secret}; use serde_aux::field_attributes::deserialize_number_from_string; +use sqlx::{ + postgres::{PgConnectOptions, PgSslMode}, + ConnectOptions, +}; #[derive(serde::Deserialize)] pub struct Settings { pub database: DatabaseSettings, @@ -15,6 +19,7 @@ pub struct DatabaseSettings { pub password: Secret, pub host: String, pub database_name: String, + pub require_ssl: bool, } #[derive(serde::Deserialize)] @@ -25,25 +30,25 @@ pub struct ApplicationSettings { } impl DatabaseSettings { - pub fn get_connnection_string(&self) -> Secret { - Secret::new(format!( - "postgres://{}:{}@{}:{}/{}", - self.username, - self.password.expose_secret(), - self.host, - self.port, - self.database_name - )) + pub fn without_db(&self) -> PgConnectOptions { + let ssl_mode = if self.require_ssl { + PgSslMode::Require + } else { + PgSslMode::Prefer + }; + + PgConnectOptions::new() + .host(&self.host) + .username(&self.username) + .password(self.password.expose_secret()) + .port(self.port) + .ssl_mode(ssl_mode) } - pub fn get_connnection_string_without_db(&self) -> Secret { - Secret::new(format!( - "postgres://{}:{}@{}:{}", - self.username, - self.password.expose_secret(), - self.host, - self.port - )) + pub fn with_db(&self) -> PgConnectOptions { + let mut options = self.without_db().database(&self.database_name); + options.log_statements(tracing_log::log::LevelFilter::Trace); + options } } @@ -55,10 +60,10 @@ pub fn get_configuration() -> Result { .unwrap_or_else(|_| "local".into()) .try_into() .expect("Failed to parse APP_ENVIRONMENT."); - let environment_filename = format!("{}.yaml", environment.as_str()); + let environment_filename = format!("{}.yml", environment.as_str()); let settings = Config::builder() - .add_source(config::File::from(configuration_directory.join("base")).required(true)) + .add_source(config::File::from(configuration_directory.join("base.yml")).required(true)) .add_source( config::File::from(configuration_directory.join(environment_filename)).required(true), ) diff --git a/src/main.rs b/src/main.rs index 6237f16..1e4236d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use secrecy::ExposeSecret; use sqlx::postgres::PgPoolOptions; use std::net::TcpListener; use tracing::subscriber::set_global_default; @@ -18,13 +17,7 @@ async fn main() -> Result<(), std::io::Error> { let configuration = get_configuration().expect("Failed to read configurations"); let connection = PgPoolOptions::new() .acquire_timeout(std::time::Duration::from_secs(2)) - .connect_lazy( - configuration - .database - .get_connnection_string() - .expose_secret(), - ) - .expect("Failed to connect to Postgres"); + .connect_lazy_with(configuration.database.with_db()); // Bind the TCP listener socket address with the configuration port let address = format!( diff --git a/tests/health_check.rs b/tests/health_check.rs index 7b35179..74905f9 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,5 +1,4 @@ use once_cell::sync::Lazy; -use secrecy::ExposeSecret; use sqlx::{Connection, Executor, PgConnection, PgPool}; use std::net::TcpListener; use uuid::Uuid; @@ -54,17 +53,16 @@ async fn spawn_app() -> TestApp { /// This is important in order to avoid polluting an existing database with test data. pub async fn configure_database(config: &zero2prod::configurations::DatabaseSettings) -> PgPool { // Create database - let mut connection = - PgConnection::connect(&config.get_connnection_string_without_db().expose_secret()) - .await - .expect("Failed to connect to Postgres"); + let mut connection = PgConnection::connect_with(&config.without_db()) + .await + .expect("Failed to connect to Postgres"); connection .execute(format!(r#"CREATE DATABASE "{}";"#, config.database_name).as_str()) .await .expect("Failed to create database"); // Migrate database - let connection_pool = PgPool::connect(&config.get_connnection_string().expose_secret()) + let connection_pool = PgPool::connect_with(config.with_db()) .await .expect("Failed to connect to Postgres"); sqlx::migrate!("./migrations")