From c9e564d48f2cedda31d2d57c38edc10c7c94b62e Mon Sep 17 00:00:00 2001 From: Nick Bland Date: Tue, 16 Nov 2021 14:34:05 +1000 Subject: [PATCH] Make application dockerised Update mail_app to also have different forms of configuration outside of a single base yaml file. Allows local or production configurations to be established. --- .Dockerignore | 2 + .gitignore | 3 +- Cargo.lock | 7 +++ Cargo.toml | 3 +- Dockerfile | 11 ++++ configuration.yaml => configuration/base.yaml | 3 +- configuration/local.yaml | 2 + configuration/production.yaml | 2 + sqlx-data.json | 18 ++++++ src/configuration.rs | 55 ++++++++++++++++++- src/main.rs | 5 +- 11 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 .Dockerignore create mode 100644 Dockerfile rename configuration.yaml => configuration/base.yaml (81%) create mode 100644 configuration/local.yaml create mode 100644 configuration/production.yaml create mode 100644 sqlx-data.json diff --git a/.Dockerignore b/.Dockerignore new file mode 100644 index 0000000..ccb5166 --- /dev/null +++ b/.Dockerignore @@ -0,0 +1,2 @@ +/target +.vscode \ No newline at end of file diff --git a/.gitignore b/.gitignore index ccb5166..b5e703e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -.vscode \ No newline at end of file +.vscode +.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0f6e36d..26a8c6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,6 +547,9 @@ name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +dependencies = [ + "serde 1.0.130", +] [[package]] name = "encoding_rs" @@ -1631,6 +1634,7 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" dependencies = [ + "indexmap", "itoa", "ryu", "serde 1.0.130", @@ -1810,9 +1814,12 @@ dependencies = [ "dotenv", "either", "heck", + "hex", "once_cell", "proc-macro2", "quote", + "serde 1.0.130", + "serde_json", "sha2", "sqlx-core", "sqlx-rt", diff --git a/Cargo.toml b/Cargo.toml index e72c7e8..b5cf922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,8 @@ features = [ "postgres", "uuid", "chrono", - "migrate" + "migrate", + "offline" ] [dev-dependencies] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..111dfd0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM rust:1.56 + +WORKDIR /app + +COPY . . +ENV SQLX_OFFLINE true +RUN CARGO BUILD --release + +ENV APP_ENVIRONMENT production + +ENTRYPOINT ["./target/release/mail_app"] \ No newline at end of file diff --git a/configuration.yaml b/configuration/base.yaml similarity index 81% rename from configuration.yaml rename to configuration/base.yaml index 04dc5ef..82b4342 100644 --- a/configuration.yaml +++ b/configuration/base.yaml @@ -1,4 +1,5 @@ -application_port: 8000 +application: + port: 8000 database: host: "localhost" port: 5432 diff --git a/configuration/local.yaml b/configuration/local.yaml new file mode 100644 index 0000000..7176c31 --- /dev/null +++ b/configuration/local.yaml @@ -0,0 +1,2 @@ +application: + host: 127.0.0.1 \ No newline at end of file diff --git a/configuration/production.yaml b/configuration/production.yaml new file mode 100644 index 0000000..271bebd --- /dev/null +++ b/configuration/production.yaml @@ -0,0 +1,2 @@ +application: + host: 0.0.0.0 \ No newline at end of file diff --git a/sqlx-data.json b/sqlx-data.json new file mode 100644 index 0000000..f844615 --- /dev/null +++ b/sqlx-data.json @@ -0,0 +1,18 @@ +{ + "db": "PostgreSQL", + "5db1f9dfcdee685b02851f144e29d3e2726127d5a1d614880a0be89d8fa6904b": { + "query": "\n INSERT INTO subscriptions (id, email, name, subscribed_at)\n VALUES($1, $2, $3, $4)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text", + "Timestamptz" + ] + }, + "nullable": [] + } + } +} \ No newline at end of file diff --git a/src/configuration.rs b/src/configuration.rs index 23e583d..97addb1 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,7 +1,15 @@ +use std::convert::{TryFrom, TryInto}; + #[derive(serde::Deserialize)] pub struct Settings { pub database: DatabaseSettings, - pub application_port: u16 + pub application: ApplicationSettings, +} + +#[derive(serde::Deserialize)] +pub struct ApplicationSettings { + pub port: u16, + pub host: String, } #[derive(serde::Deserialize)] @@ -16,14 +24,55 @@ pub struct DatabaseSettings { pub fn get_configuration() -> Result { // Initialise configuration reader let mut settings = config::Config::default(); + let base_path = std::env::current_dir().expect("Failed to determine the current directory"); + let configuration_directory = base_path.join("configuration"); - // Add config from `configuration` file (yaml, json, etc.) - settings.merge(config::File::with_name("configuration"))?; + // Read default config file + settings.merge(config::File::from(configuration_directory.join("base")).required(true))?; + + let environment: Environment = std::env::var("APP_ENVIRONMENT") + .unwrap_or_else(|_| "local".into()) + .try_into() + .expect("Failed to parse APP_ENVIRONMENT."); + + settings.merge( + config::File::from(configuration_directory.join(environment.as_str())).required(true) + )?; + + settings.merge(config::Environment::with_prefix("app").separator("__"))?; // Try convert into Settings type settings.try_into() } +pub enum Environment { + Local, + Production, +} + +impl Environment { + pub fn as_str(&self) -> &'static str { + match self { + Environment::Local => "local", + Environment::Production => "production", + } + } +} + +impl TryFrom for Environment { + type Error = String; + + fn try_from(s: String) -> Result { + match s.to_lowercase().as_str() { + "local" => Ok(Self::Local), + "production" => Ok(Self::Production), + other => Err(format!( + "{} is nto a supported environment. Use either `local` or `production`.", other + )), + } + } +} + impl DatabaseSettings { pub fn connection_string(&self) -> String { format!( diff --git a/src/main.rs b/src/main.rs index 1074c73..6a26967 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,12 +13,11 @@ async fn main() -> std::io::Result<()> { let configuration = get_configuration().expect("Failed to read configuration data."); // Configure connection to database for our startup - let connection_pool = PgPool::connect(&configuration.database.connection_string()) - .await + let connection_pool = PgPool::connect_lazy(&configuration.database.connection_string()) .expect("Failed to connect to Postgres."); // Take port from settings file - let address = format!("127.0.0.1:{}", configuration.application_port); + let address = format!("{}:{}", configuration.application.host, configuration.application.port); let listener = TcpListener::bind(address)?; run(listener, connection_pool)?.await?; Ok(())