refactor: changes
This commit is contained in:
parent
476eed4559
commit
f4deaceb27
7 changed files with 137 additions and 12 deletions
1
.env
Normal file
1
.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
DATABASE_URL="postgres://postgres:password@localhost:5432/newsletter"
|
||||
94
Dockerfile
Normal file
94
Dockerfile
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Global ARGs
|
||||
|
||||
ARG WORKDIR_ROOT=/usr/src
|
||||
|
||||
ARG PROJECT_NAME=learn_axum
|
||||
|
||||
ARG BUILD_TARGET=x86_64-unknown-linux-musl
|
||||
|
||||
ARG BUILD_MODE=release
|
||||
|
||||
ARG BUILD_BIN=${PROJECT_NAME}
|
||||
|
||||
###############################################################################
|
||||
|
||||
FROM clux/muslrust:stable AS build
|
||||
|
||||
# Import global ARGs
|
||||
ARG WORKDIR_ROOT
|
||||
ARG PROJECT_NAME
|
||||
ARG BUILD_TARGET
|
||||
ARG BUILD_MODE
|
||||
ARG BUILD_BIN
|
||||
|
||||
WORKDIR ${WORKDIR_ROOT}
|
||||
|
||||
# Docker build cache: Create and build an empty dummy project with all
|
||||
# external dependencies to avoid redownloading them on subsequent builds
|
||||
# if unchanged.
|
||||
RUN USER=root \
|
||||
cargo new --bin ${PROJECT_NAME}
|
||||
WORKDIR ${WORKDIR_ROOT}/${PROJECT_NAME}
|
||||
|
||||
COPY [ \
|
||||
"Cargo.toml", \
|
||||
"Cargo.lock", \
|
||||
"./" ]
|
||||
|
||||
# Build the dummy project(s), then delete all build artefacts that must(!) not be cached
|
||||
# RUN cargo build --${BUILD_MODE} --target ${BUILD_TARGET}
|
||||
#\
|
||||
# && \
|
||||
# rm -f ./target/${BUILD_TARGET}/${BUILD_MODE}/${PROJECT_NAME}* \
|
||||
# && \
|
||||
# rm -f ./target/${BUILD_TARGET}/${BUILD_MODE}/deps/${PROJECT_NAME}-* \
|
||||
# && \
|
||||
# rm -rf ./target/${BUILD_TARGET}/${BUILD_MODE}/.fingerprint/${PROJECT_NAME}-*
|
||||
|
||||
# Copy all project (re-)sources that are required for building (ordered alphabetically)
|
||||
COPY [ "migrations", "./migrations/" ]
|
||||
COPY [ "src", "./src/" ]
|
||||
|
||||
# Test and build the actual project
|
||||
RUN cargo test --${BUILD_MODE} --target ${BUILD_TARGET} --workspace \
|
||||
&& \
|
||||
cargo build --${BUILD_MODE} --target ${BUILD_TARGET} --bin ${BUILD_BIN} \
|
||||
&& \
|
||||
strip ./target/${BUILD_TARGET}/${BUILD_MODE}/${BUILD_BIN}
|
||||
|
||||
# Switch back to the root directory
|
||||
#
|
||||
# NOTE(2019-08-30, uklotzde): Otherwise copying from the build image fails
|
||||
# during all subsequent builds of the 2nd stage with an unchanged 1st stage
|
||||
# image. Tested with podman 1.5.x on Fedora 30.
|
||||
WORKDIR /
|
||||
|
||||
|
||||
###############################################################################
|
||||
# 2nd Build Stage
|
||||
FROM scratch
|
||||
|
||||
# Import global ARGs
|
||||
ARG WORKDIR_ROOT
|
||||
ARG PROJECT_NAME
|
||||
ARG BUILD_TARGET
|
||||
ARG BUILD_MODE
|
||||
ARG BUILD_BIN
|
||||
|
||||
ARG DATA_VOLUME="/volume"
|
||||
|
||||
ARG EXPOSE_PORT=8080
|
||||
|
||||
# Copy the statically-linked executable into the minimal scratch image
|
||||
COPY --from=build [ \
|
||||
"${WORKDIR_ROOT}/${PROJECT_NAME}/target/${BUILD_TARGET}/${BUILD_MODE}/${BUILD_BIN}", \
|
||||
"./entrypoint" ]
|
||||
|
||||
EXPOSE ${EXPOSE_PORT}
|
||||
|
||||
VOLUME [ ${DATA_VOLUME} ]
|
||||
|
||||
# Bind the exposed port to Rocket that is used as the web framework
|
||||
ENV SERVER_PORT ${EXPOSE_PORT}
|
||||
|
||||
ENTRYPOINT [ "./entrypoint" ]
|
||||
|
|
@ -4,4 +4,4 @@ database:
|
|||
port: 5432
|
||||
username: "postgres"
|
||||
password: "password"
|
||||
database_name: "newsletter"
|
||||
name: "newsletter"
|
||||
|
|
|
|||
|
|
@ -15,13 +15,15 @@ pub struct Settings {
|
|||
/// * `password`: the DB pasword
|
||||
/// * `port`: the DB port
|
||||
/// * `host`: the DB host address
|
||||
/// * `database_name`: the DB name
|
||||
/// * `name`: the DB name
|
||||
pub struct DatabaseSettings {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub port: u16,
|
||||
pub host: String,
|
||||
pub database_name: String,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub require_ssl: bool,
|
||||
}
|
||||
|
||||
/// Provides the application settings
|
||||
|
|
@ -39,7 +41,7 @@ impl DatabaseSettings {
|
|||
pub fn connection_string(&self) -> String {
|
||||
format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
self.username, self.password, self.host, self.port, self.database_name
|
||||
self.username, self.password, self.host, self.port, self.name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
src/main.rs
11
src/main.rs
|
|
@ -1,5 +1,8 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use learn_axum::configuration::get_configuration;
|
||||
use learn_axum::startup;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -7,5 +10,11 @@ async fn main() {
|
|||
let configuration = get_configuration().expect("Failed to read configuration.");
|
||||
let addr = format!("127.0.0.1:{}", configuration.application_port);
|
||||
let listener = TcpListener::bind(addr).await.unwrap(); //.expect("Unable to bind to port");
|
||||
startup::run(listener).await.unwrap();
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.acquire_timeout(Duration::from_secs(3))
|
||||
.connect(&configuration.database.connection_string())
|
||||
.await
|
||||
.expect("can't connect to database");
|
||||
startup::run(listener, pool).await.unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
use axum::routing::IntoMakeService;
|
||||
use axum::serve::Serve;
|
||||
use axum::Router;
|
||||
use sqlx::PgPool;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
/// API routing
|
||||
pub fn app() -> Router {
|
||||
pub fn app(connection: PgPool) -> Router {
|
||||
Router::new()
|
||||
.with_state(connection)
|
||||
.merge(crate::routes::routes_health_check())
|
||||
.merge(crate::routes::routes_subscriptions())
|
||||
}
|
||||
|
||||
/// Start the server
|
||||
pub fn run(listener: TcpListener) -> Serve<IntoMakeService<Router>, Router> {
|
||||
axum::serve(listener, app().into_make_service())
|
||||
pub fn run(listener: TcpListener, connection: PgPool) -> Serve<IntoMakeService<Router>, Router> {
|
||||
axum::serve(listener, app(connection).into_make_service())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use learn_axum::configuration::get_configuration;
|
||||
use sqlx::{Connection, PgConnection};
|
||||
use std::net::SocketAddr;
|
||||
use sqlx::{Connection, PgConnection, postgres::PgPoolOptions};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
struct TestApp {
|
||||
|
|
@ -33,7 +34,7 @@ async fn subscribe_returns_a_200_for_valid_form_data() {
|
|||
let connection_string = configuration.database.connection_string();
|
||||
// The `Connection` trait MUST be in scope for us to invoke
|
||||
// `PgConnection::connect` - it is not an inherent method of the struct!
|
||||
let connection = PgConnection::connect(&connection_string)
|
||||
let mut connection = PgConnection::connect(&connection_string)
|
||||
.await
|
||||
.expect("Failed to connect to Postgres.");
|
||||
let client = reqwest::Client::new();
|
||||
|
|
@ -50,6 +51,14 @@ async fn subscribe_returns_a_200_for_valid_form_data() {
|
|||
|
||||
// Assert
|
||||
assert_eq!(200, response.status().as_u16());
|
||||
|
||||
let saved = sqlx::query!("SELECT email, name FROM subscriptions",)
|
||||
.fetch_one(&mut connection)
|
||||
.await
|
||||
.expect("Failed to fetch saved subscription.");
|
||||
|
||||
assert_eq!(saved.email, "ursula_le_guin@gmail.com");
|
||||
assert_eq!(saved.name, "le guin");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -87,8 +96,16 @@ async fn spawn_app() -> TestApp {
|
|||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let address = format!("http://{}", listener.local_addr().unwrap());
|
||||
|
||||
let configuration = get_configuration().expect("Failed to read configuration.");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.acquire_timeout(Duration::from_secs(3))
|
||||
.connect(&configuration.database.connection_string())
|
||||
.await
|
||||
.expect("can't connect to database");
|
||||
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, learn_axum::startup::app())
|
||||
axum::serve(listener, learn_axum::startup::app(pool))
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue