diff --git a/.dockerignore b/.dockerignore index 54864cfb3f..15bbb41e6e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -20,3 +20,4 @@ resources/public/js/ target/ test/ out/ +data/ diff --git a/.gitignore b/.gitignore index 572a1c40a2..247ef79d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ pom.xml .idea .eastwood .cache +*.archive /resources/public/js/ /resources/public/cljs/ diff --git a/docker/Dockerfile b/docker/Dockerfile index be80cd6688..023b659161 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,3 +1,4 @@ +# Dockerfile for local development FROM clojure:latest RUN mkdir -p /usr/src/app diff --git a/docker/Dockerfile.cards b/docker/Dockerfile.cards new file mode 100644 index 0000000000..eb00eebe18 --- /dev/null +++ b/docker/Dockerfile.cards @@ -0,0 +1,14 @@ +# Create an image to serve card images with nginx. +# +# To work around the .dockerignore file that excludes cards, you must use the +# resources directory as the docker context. E.g. +# +# docker buildx build -f docker/Dockerfile.cards ./resources +# +# This image must be generated after card images are downloaded while +# populating a local database with 'lein fetch'. See also Dockerfile.initdb for +# how the local database is used as a template to seed a production database. + +FROM nginx:alpine + +COPY ./public/img/cards /usr/share/nginx/html/img/cards diff --git a/docker/Dockerfile.initdb b/docker/Dockerfile.initdb new file mode 100644 index 0000000000..3de3b20c00 --- /dev/null +++ b/docker/Dockerfile.initdb @@ -0,0 +1,39 @@ +# This image bundles mongotools along with a mongodump of the initial database +# collections and indexes. When it runs, it restores the collections and +# indexes to a target database. The MONGO_CONNECTION_URI environment variable +# must be set (including a database name) when it runs for it to work. +# +# E.g. docker run -e MONGO_CONNECTION_URI --rm dagolden/jnet-initdb +# +# It expects a mongodump of a locally initialized database to exist in the root +# directory as 'jnet-tmpl.archive'. Here is one way to generate it: +# +# $ export MONGO_CONNECTION_URI="mongodb://localhost/jnet-tmpl" +# $ lein fetch +# $ lein create-indexes +# $ mongodump --uri="$MONGO_CONNECTION_URI" --archive=jnet-tmpl.archive -d jnet-tmpl + +FROM amazonlinux:2023 + +# Set up the working directory +WORKDIR /app + +# Install mongotools, including mongorestore + +ENV MONGO_ARCHIVE=jnet-tmpl.archive + +RUN yum install -y tar gzip \ + && mkdir mongotools \ + && curl -L https://fastdl.mongodb.org/tools/db/mongodb-database-tools-amazon2023-x86_64-100.10.0.tgz | tar -xz --strip-components=1 -C mongotools + +# The database must be initialized locally with `lein fetch` (including card +# images!) and then dumped with mongodump: +# mongodump --uri="mongodb://localhost/" --archive=jnet-tmpl.archive --db="jnet-tmpl" +COPY ./$MONGO_ARCHIVE . + +# Create an entrypoint script +COPY docker/init-db.sh /app/init-db.sh +RUN chmod +x /app/init-db.sh + +# Set the entrypoint +ENTRYPOINT ["/app/init-db.sh"] diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod new file mode 100644 index 0000000000..3dddf9eefb --- /dev/null +++ b/docker/Dockerfile.prod @@ -0,0 +1,49 @@ +# Production Dockerfile with separate build and deploy images. + +# Runtime requires card images provided in the image generated by +# Dockerfile.cards to be served at /img/cards/. + +FROM clojure:latest AS builder + +WORKDIR /app + +# Install Node.js +ENV NODE_VERSION=19.6.0 +RUN apt update && apt install -y curl +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash +ENV NVM_DIR=/root/.nvm +RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} +RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} +RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} +ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}" + +# Copy project files +COPY . . + +# Install dependencies +RUN npm ci +RUN lein deps + +# Compile CSS +RUN npm run css:release + +# Compile ClojureScript +RUN npm run cljs:release + +# Build the application +RUN lein uberjar + +# Create a smaller runtime image +FROM openjdk:11-jre-slim + +WORKDIR /app + +# Copy the built artifact from the builder stage +COPY --from=builder /app/target/netrunner-standalone.jar . + +# Set the startup command. Custom config with 'prod.edn' can +# be mapped as a volume at runtime to '/app/config/prod.edn'. +CMD ["java", "-cp", "/app/netrunner-standalone.jar:/app/config", "web.core"] + +# Expose port to nginx-proxy +EXPOSE 1042 diff --git a/docker/init-db.sh b/docker/init-db.sh new file mode 100644 index 0000000000..5284d085ce --- /dev/null +++ b/docker/init-db.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +# Check if MONGO_CONNECTION_URI is set +if [ -z "$MONGO_CONNECTION_URI" ]; then + echo "Error: MONGO_CONNECTION_URI environment variable is not set" + exit 1 +fi + +# Jnet needs the database name in the URI. Mongodump must not have a database +# name in the URI when doing namespace transformation. Thus, we must parse the +# provided URI and reconstruct the parts we need. +MONGO_RESTORE_URI=$(echo $MONGO_CONNECTION_URI | sed -E 's/^(.*\/)[^?]*(\?.*)$/\1\2/') +MONGO_RESTORE_DB=$(echo $MONGO_CONNECTION_URI | sed -E 's/^.*\/([^?]*)\?.*/\1/') + +if [[ -z "$MONGO_RESTORE_URI" || -z "$MONGO_RESTORE_DB" ]]; then + echo "Error: MONGO_CONNECTION_URI environment variable could not be parsed" + exit 1 +fi + +# For local testing, mongorestore might be somewhere else in the path. In the +# generated image we expect it to be in 'mongotools'. +if [ -d "mongotools" ]; then + export PATH="$(pwd)/mongotools/bin:$PATH" +fi + +# Update the configuration file with the MongoDB connection URI +mongorestore --uri="${MONGO_RESTORE_URI}" --archive="$MONGO_ARCHIVE" --nsFrom='jnet-tmpl.*' --nsTo="${MONGO_RESTORE_DB}.*" + +echo "Database initialization completed successfully."