diff --git a/.dockerignore b/.dockerignore index 04c13db..d4f7068 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,5 +9,6 @@ !src !tests !web +!target/x86_64-unknown-linux-musl/release/rbd web/node_modules diff --git a/Cargo.lock b/Cargo.lock index b10ec77..5491786 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -939,6 +939,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" dependencies = [ + "pkg-config", "vcpkg", ] @@ -1221,6 +1222,7 @@ dependencies = [ "jwt", "mimalloc", "openssl", + "pq-sys", "rand", "rocket", "rocket_sync_db_pools", diff --git a/Cargo.toml b/Cargo.toml index c6f71fa..234fd01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ serde = { version = "1.0.127", features = [ "derive" ] } diesel = { version = "1.4.7", features = ["postgres", "uuidv07", "chrono"] } diesel_migrations = "1.4.0" # To properly compile libpq statically -openssl = "0.10.36" +openssl = "*" # For password hashing & verification rust-argon2 = "0.8.3" rand = "0.8.4" @@ -38,6 +38,11 @@ base64 = "0.13.0" figment = { version = "*", features = [ "yaml" ] } mimalloc = { version = "0.1.26", default_features = false } +[dependencies.pq-sys] +version = "*" +default-features = false +features = ["pkg-config"] + [profile.release] lto = "fat" panic = "abort" diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..89eb9f2 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,3 @@ +[target.x86_64-unknown-linux-musl] +image = "rusty-builder:x86_64-unknown-linux" + diff --git a/Dockerfile b/Dockerfile index 7a7ee23..e2a507b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,4 @@ -FROM rust:1.54 +FROM scratch -ENV PREFIX="/usr/src/out/prefix" \ - CC="musl-gcc -fPIC -pie -static" \ - LD_LIBRARY_PATH="$PREFIX" \ - PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" \ - PATH="/usr/local/bin:/root/.cargo/bin:$PATH" - -RUN apt update && \ - apt install -y --no-install-recommends \ - musl-dev \ - musl-tools \ - libpq-dev \ - libssl-dev && \ - rustup target add x86_64-unknown-linux-musl && \ - mkdir "$PREFIX" && \ - echo "$PREFIX/lib" >> /etc/ld-musl-x86_64.path - -WORKDIR /usr/src/app +COPY target/x86_64-unknown-linux-musl/release/rbd / +RUN ["/rbd"] diff --git a/Dockerfile.build b/Dockerfile.build new file mode 100644 index 0000000..10e83d5 --- /dev/null +++ b/Dockerfile.build @@ -0,0 +1,68 @@ +# Cross-compile for a specific target triplet (x86_64 by default) +ARG TARGET +ARG CORES=4 +FROM rustembedded/cross:${TARGET}-musl + +# Create download directory +RUN mkdir /src + +### Environment +# Configure compiler +ENV MAKE="make -j$CORES" \ + CC="musl-gcc -fPIE -pie -static" \ + PREFIX=/usr/local/x86_64-linux-musl \ + RUSTFLAGS="-C relocation-model=static" +# Configure paths +ENV PATH=$PREFIX/bin:$PATH \ + C_INCLUDE_PATH=$PREFIX/include \ + LD_LIBRARY_PATH=$PREFIX/lib +# Configure pkg-config +ENV PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig \ + PKG_CONFIG_ALLOW_CROSS=true \ + PKG_CONFIG_ALL_STATIC=true + +# Install development libraries +RUN apt-get update && apt-get install -y \ + bison \ + flex \ + musl-dev \ + musl-tools + +### OpenSSL +ARG SSL_VER +# Download OpenSSL +RUN curl -sSL "https://www.openssl.org/source/openssl-${SSL_VER}.tar.gz" | tar -xzC /src +# Build OpenSSL statically +RUN cd "/src/openssl-${SSL_VER}" \ + && ./Configure \ + no-shared \ + no-zlib \ + -fPIC \ + --prefix=$PREFIX \ + --openssldir=$PREFIX/ssl \ + linux-x86_64 \ + && $MAKE depend \ + && $MAKE \ + && $MAKE install +# Configure OpenSSL crate +ENV OPENSSL_STATIC=true \ + OPENSSL_NO_VENDOR=true + +### PostgreSQL +ARG PQ_VER +# Download PostgreSQL +RUN curl -sSL "https://ftp.postgresql.org/pub/source/v${PQ_VER}/postgresql-${PQ_VER}.tar.gz" | tar -xzC /src +# Build PostgreSQL statically +RUN cd "/src/postgresql-${PQ_VER}" \ + && CPPFLAGS=-I$PREFIX/include LDFLAGS="-L$PREFIX/lib" \ + ./configure \ + --with-openssl \ + --without-readline \ + --without-zlib \ + --prefix=$PREFIX \ + --host=$TARGET \ + && ${MAKE} -C src/interfaces/libpq all-static-lib \ + && ${MAKE} -C src/interfaces/libpq install-lib-pc \ + && ${MAKE} -C src/interfaces/libpq install-lib-static \ + && ${MAKE} -C src/bin/pg_config \ + && ${MAKE} -C src/bin/pg_config install diff --git a/Makefile b/Makefile index 62cb5c4..32fa8b3 100644 --- a/Makefile +++ b/Makefile @@ -6,133 +6,36 @@ SSL_VER ?= 1.1.1k # Dumb-init version DI_VER ?= 1.2.5 +# Compilation target triplet +# Supported targets: https://github.com/rust-embedded/cross#supported-targets +TARGET = x86_64-unknown-linux +CORES != nproc -# =====AUTO-GENERATED VARIABLES===== -# This is such a lovely oneliner -# NOTE: $(dir PATH) outputs a trailing slash -OUT_DIR ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST))))out - -PREFIX := $(OUT_DIR)/prefix -OPENSSL_DIR := $(OUT_DIR)/openssl-$(SSL_VER) -PQ_DIR := $(OUT_DIR)/postgresql-$(PQ_VER) -DI_DIR := $(OUT_DIR)/dumb-init-$(DI_VER) - -# Used in various make calls to specify parallel recipes -CORES != nproc - - -# =====ENVIRONMENT VARIABLES===== -export CC := musl-gcc -fPIC -pie -static -export LD_LIBRARY_PATH := $(PREFIX) -export PKG_CONFIG_PATH := /usr/local/lib/pkgconfig -export PATH := /usr/local/bin:/root/.cargo/bin:$(PATH) - - -# TODO check for header files (openssl-dev, libpq-dev) both for Arch & Ubuntu - -# Create the out dir -$(shell mkdir -p "$(PREFIX)") - - -# =====BUILDING THE STATIC BINARY===== .PHONY: all -all: build +all: build-debug .PHONY: builder builder: docker build \ - -t rusty-builder:latest - < docker/Dockerfile.builder + --build-arg TARGET=$(TARGET) \ + --build-arg CORES=$(CORES) \ + --build-arg SSL_VER=$(SSL_VER) \ + --build-arg PQ_VER=$(PQ_VER) \ + --tag rusty-builder:$(TARGET) \ + --file Dockerfile.build \ + . -.PHONY: docker -docker: builder - docker run \ - --rm \ - -v "$$PWD:/usr/src" \ - --workdir "/usr/src" \ - -it \ - rusty-builder:latest \ - bash build.sh +.PHONY: build-debug +build-debug: builder + cross build --target "$(TARGET)-musl" +.PHONY: run +run: builder + docker-compose up -d --build && docker-compose logs -f app -# libpq builds openssl as a dependency -.PHONY: build -build: libpq - -.PHONY: clean -clean: clean-openssl clean-libpq clean-di - @ echo "Note: this only cleans the C dependencies, not the Cargo cache." - rm -rf "$(PREFIX)" - -# This is used inside the Dockerfile -.PHONY: pathfile -pathfile: - echo "$(PREFIX)/lib" >> /etc/ld-musl-x86_64.path - - -## =====OPENSSL===== -# Download the source code & configure the project -$(OPENSSL_DIR)/Configure: - curl -sSL "https://www.openssl.org/source/openssl-$(SSL_VER).tar.gz" | \ - tar -xzC "$(OUT_DIR)" - cd "$(OPENSSL_DIR)" && \ - CC="$(CC) -idirafter /usr/include -idirafter /usr/include/x86_64-linux-gnu/" ./Configure \ - no-zlib \ - no-shared \ - --prefix="$(PREFIX)" \ - --openssldir="$(PREFIX)/ssl" \ - linux-x86_64 - -# Build OpenSSL -.PHONY: openssl -openssl: $(OPENSSL_DIR)/Configure - cd "$(OPENSSL_DIR)" && env C_INCLUDE_PATH="$(PREFIX)/include" $(MAKE) depend 2> /dev/null - cd "$(OPENSSL_DIR)" && $(MAKE) -j$(CORES) - cd "$(OPENSSL_DIR)" && $(MAKE) install_sw - -.PHONY: clean-openssl -clean-openssl: - rm -rf "$(OPENSSL_DIR)" - - -## =====LIBPQ===== -# Download the source code & configure the project -$(PQ_DIR)/configure: - curl -sSL "https://ftp.postgresql.org/pub/source/v$(PQ_VER)/postgresql-$(PQ_VER).tar.gz" | \ - tar -xzC "$(OUT_DIR)" - cd "$(PQ_DIR)" && \ - LDFLAGS="-L$(PREFIX)/lib" CFLAGS="-I$(PREFIX)/include" ./configure \ - --without-readline \ - --with-openssl \ - --without-zlib \ - --prefix="$(PREFIX)" \ - --host=x86_64-unknown-linux-musl - -.PHONY: libpq -libpq: openssl $(PQ_DIR)/configure - cd "$(PQ_DIR)/src/interfaces/libpq" && $(MAKE) -j$(CORES) all-static-lib - cd "$(PQ_DIR)/src/interfaces/libpq" && $(MAKE) install install-lib-static - cd "$(PQ_DIR)/src/bin/pg_config" && $(MAKE) -j$(CORES) - cd "$(PQ_DIR)/src/bin/pg_config" && $(MAKE) install - -.PHONY: clean-libpq -clean-libpq: - rm -rf "$(PQ_DIR)" - - -# =====DUMB-INIT===== -# NOTE: this is only used inside the Docker image, but it's here for completeness. -$(DI_DIR)/Makefile: - curl -sSL "https://github.com/Yelp/dumb-init/archive/refs/tags/v$(DI_VER).tar.gz" | \ - tar -C "$(OUT_DIR)" -xz - -.PHONY: di -di: $(DI_DIR)/Makefile - make -C "$(DI_DIR)" build - -.PHONY: clean-di -clean-di: - rm -rf "$(DI_DIR)" - +.PHONY: release +build-release: builder + cross build --target "$(TARGET)-musl" --release # ====UTILITIES FOR DEVELOPMENT===== ## The tests require a database, so we run them like this diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6a316d5 --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +fn main() { + println!("cargo:rustc-link-lib=static=c"); + println!("cargo:rustc-link-lib=static=dl"); + println!("cargo:rustc-link-lib=static=ssl"); + println!("cargo:rustc-link-lib=static=pq"); +} + diff --git a/docker-compose.yml b/docker-compose.yml index 522fe33..6481bcf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,10 +2,11 @@ version: '3' services: + app: + build: '.' db: image: 'postgres:13-alpine' restart: 'always' - environment: - 'POSTGRES_DB=rb' - 'POSTGRES_USER=rb' @@ -14,6 +15,5 @@ services: - '5432:5432' volumes: - 'db-data:/var/lib/postgresql/data' - volumes: db-data: diff --git a/docker/Dockerfile.builder b/docker/Dockerfile.builder deleted file mode 100644 index 5c87d30..0000000 --- a/docker/Dockerfile.builder +++ /dev/null @@ -1,28 +0,0 @@ -# vim: ft=dockerfile -FROM rust:1.54 - -ENV PREFIX="/usr/src/out/prefix" \ - CC="musl-gcc -fPIC -pie -static" \ - LD_LIBRARY_PATH="$PREFIX" \ - PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" \ - PATH="/usr/local/bin:/root/.cargo/bin:$PATH" - -WORKDIR /usr/src/app - -RUN groupadd -g 1000 builder && \ - useradd -u 1000 -g 1000 builder && \ - mkdir -p "$PREFIX" && \ - chown -R builder:builder /usr/src/app && \ - apt update && \ - apt install -y --no-install-recommends \ - musl-dev \ - musl-tools \ - libpq-dev \ - libssl-dev && \ - rustup target add x86_64-unknown-linux-musl && \ - echo "$PREFIX/lib" >> /etc/ld-musl-x86_64.path - - -USER builder - -CMD ["cargo", "test"] diff --git a/src/main.rs b/src/main.rs index 2c0c7c4..c7ab3fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,11 @@ // compilation succeeds in the release Docker image. extern crate openssl; #[macro_use] -extern crate rocket; +extern crate diesel; #[macro_use] extern crate diesel_migrations; #[macro_use] -extern crate diesel; +extern crate rocket; use figment::{ providers::{Env, Format, Yaml},