Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add npg_porch 2.0.0 server #118

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 68 additions & 48 deletions docker/Makefile

Large diffs are not rendered by default.

111 changes: 111 additions & 0 deletions docker/porch/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@

ARG BASE_IMAGE=python:3.10-slim
FROM $BASE_IMAGE as builder

ENV DEBIAN_FRONTEND="noninteractive"

RUN apt-get update && \
apt-get install -q -y --no-install-recommends \
build-essential \
gcc \
git \
libsqlite3-dev \
unattended-upgrades && \
unattended-upgrade -v

WORKDIR /app

COPY ./scripts/*.sh /app/docker/scripts/

COPY ./logging.json /app/docker/logging.json

ARG PORCH_VERSION=2.0.0

# The last 2 lines are a workaround for porch's pip install not copying the scripts
RUN python -m venv /app && \
. /app/bin/activate && \
pip install --no-cache-dir --upgrade pip && \
git clone --branch "v${PORCH_VERSION}" --single-branch https://github.com/wtsi-npg/npg_porch.git /tmp/npg_porch && \
cd /tmp/npg_porch && \
pip install --no-cache-dir . && \
mkdir /app/scripts && \
cp ./scripts/*.py /app/scripts/

FROM $BASE_IMAGE

ARG DEBIAN_FRONTEND

RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -q -y --no-install-recommends \
curl \
libsqlite3-0 \
postgresql \
sudo \
tini \
locales && \
locale-gen en_GB en_GB.UTF-8 && \
localedef -i en_GB -c -f UTF-8 -A /usr/share/locale/locale.alias en_GB.UTF-8

RUN apt-get install -q -y --no-install-recommends \
unattended-upgrades && \
unattended-upgrade -v && \
apt-get remove -q -y unattended-upgrades && \
apt-get autoremove -q -y && \
apt-get clean -q -y && \
rm -rf /var/lib/apt/lists/*

ENV LANG=en_GB.UTF-8 \
LANGUAGE=en_GB \
LC_ALL=en_GB.UTF-8 \
TZ="Etc/UTC"

ARG APP_USER=appuser
ARG APP_UID=1000
ARG APP_GID=$APP_UID

WORKDIR /app

RUN groupadd --gid $APP_GID $APP_USER && \
useradd --uid $APP_UID --gid $APP_GID --shell /bin/bash --create-home $APP_USER

COPY --from=builder --chown=$APP_USER:$APP_GID /app /app

ARG DB_HOST=localhost
ARG DB_PORT=5432
ARG DB_SCHEMA=porch_dev
ARG DB_NAME=porch_dev_db
ARG DB_USER=porch_admin
ARG DB_PASS=porch
ARG URL_SLUG="$DB_USER:$DB_PASS@$DB_HOST:$DB_PORT/$DB_NAME"

ENV DB_HOST=$DB_HOST \
DB_PORT=$DB_PORT \
DB_SCHEMA=$DB_SCHEMA \
DB_NAME=$DB_NAME \
DB_USER=$DB_USER \
DB_PASS=$DB_PASS \
DB_URL="postgresql+psycopg2://$URL_SLUG"

RUN service postgresql start && \
/app/docker/scripts/create_database.sh && \
/app/docker/scripts/configure_database_service.sh && \
. /app/bin/activate && \
/app/scripts/deploy_schema.py && \
/app/docker/scripts/insert_admin_token.sh && \
service postgresql stop

USER $APP_USER

ARG PORT=8081

ENV DB_URL="postgresql+asyncpg://$URL_SLUG" \
PORT=${PORT}

EXPOSE ${PORT}

HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 CMD curl -f http://localhost:${PORT} || exit 1

ENTRYPOINT ["/usr/bin/tini", "--"]

CMD ["/app/docker/scripts/entrypoint.sh"]
27 changes: 27 additions & 0 deletions docker/porch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# NPG Porch Server

**Not for use in production!**

This is a Docker image hosting both a Porch server and its PostgreSQL database
that works out of the box. To be used for running tests only.

The application is populated with a hard-coded administrator user, password and
administration token and is configured log to STDERR and STDOUT.

## Using the container
### Running

To run the container (with the Porch port published to the host network):

`docker run -d -name porch -p 8081:8081 wsinpg/python-3.10-npg-porch-[VERSION]:latest`

where [VERSION] is the required npg_porch release e.g. 2.0.0

### Connecting

The Porch server is configured to use HTTP on port 8081 and an admin token of

`00000000000000000000000000000000`

has been set in the database. See the Dockerfile for the configuration of the
backend database.
51 changes: 51 additions & 0 deletions docker/porch/logging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"version": 1,
"formatters": {
"default": {
"()": "uvicorn.logging.DefaultFormatter",
"fmt": "%(levelprefix)s %(message)s",
"use_colors": null
},
"access": {
"()": "uvicorn.logging.AccessFormatter",
"fmt": "%(levelprefix)s %(client_addr)s - \"%(request_line)s\" %(status_code)s"
}
},
"handlers": {
"stderr": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr"
},
"stdout": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
}
},
"loggers": {
"uvicorn": {
"handlers": ["access"],
"level": "INFO",
"propagate": false
},
"uvicorn.error": {
"handlers": ["stderr"],
"level": "DEBUG",
"propagate": false
},
"fastapi": {
"handlers": ["stderr"],
"level": "INFO"
}
},
"root": {
"handlers": ["stdout"],
"level": "DEBUG"
}
}
12 changes: 12 additions & 0 deletions docker/porch/scripts/configure_database_service.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -eo pipefail
set -x

APP_USER=${APP_USER:? The APP_USER environment variable must be set}

cat > "/etc/sudoers.d/$APP_USER" << EOF
$APP_USER ALL= NOPASSWD: /usr/sbin/service postgresql start
$APP_USER ALL= NOPASSWD: /usr/sbin/service postgresql restart
$APP_USER ALL= NOPASSWD: /usr/sbin/service postgresql stop
EOF
30 changes: 30 additions & 0 deletions docker/porch/scripts/create_database.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

set -eo pipefail
set -x

pg_isready --quiet || {
echo "PostgreSQL is not ready" >&2
exit 1
}

DB_SCHEMA=${DB_SCHEMA:? The DB_SCHEMA environment variable must be set}
DB_NAME=${DB_NAME:? The DB_NAME environment variable must be set}
DB_USER=${DB_USER:? The DB_USER environment variable must be set}
DB_PASS=${DB_PASS:? The DB_PASS environment variable must be set}

sudo -u postgres createuser -D -R -S ${DB_USER}
sudo -u postgres createdb -O ${DB_USER} ${DB_NAME}

sudo -u postgres psql -d ${DB_NAME} << EOF
ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';

CREATE SCHEMA ${DB_SCHEMA};

SET search_path TO ${DB_SCHEMA}, public;

GRANT ALL PRIVILEGES ON SCHEMA ${DB_SCHEMA} TO ${DB_USER};
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ${DB_SCHEMA} TO ${DB_USER};
GRANT USAGE ON ALL SEQUENCES IN SCHEMA ${DB_SCHEMA} TO ${DB_USER};
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ${DB_SCHEMA} TO ${DB_USER};
EOF
16 changes: 16 additions & 0 deletions docker/porch/scripts/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -eo pipefail

sudo service postgresql start

pg_isready --quiet --timeout=30 || {
echo "PostgreSQL is not ready" >&2
exit 1
}

PORT=${PORT:? The PORT environment variable must be set}

source /app/bin/activate

exec uvicorn npg_porch.server:app --host 0.0.0.0 --port ${PORT} --reload --log-config /app/docker/logging.json
18 changes: 18 additions & 0 deletions docker/porch/scripts/insert_admin_token.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -eo pipefail
set -x

pg_isready --quiet || {
echo "PostgreSQL is not ready" >&2
exit 1
}

DB_SCHEMA=${DB_SCHEMA:? The DB_SCHEMA environment variable must be set}
DB_NAME=${DB_NAME:? The DB_NAME environment variable must be set}

ADMIN_TOKEN=${ADMIN_TOKEN:="00000000000000000000000000000000"}

sudo -u postgres psql -d ${DB_NAME} << EOF
INSERT INTO ${DB_SCHEMA}."token" (token, description, date_issued) VALUES ('${ADMIN_TOKEN}', 'Admin token', NOW());
EOF