diff --git a/.dockerignore b/.dockerignore index 4c3d8b0ab..5feb2bae6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,6 @@ .git +docs/en_US/_build/html/_sources +docs/en_US/_build/html/_static/*.png web/node_modules web/*.log web/regression diff --git a/Dockerfile b/Dockerfile index 1413bb10c..f28bac787 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,20 +12,12 @@ # and clean up the web/ source code ######################################################################### -FROM node:14-alpine3.12 AS app-builder - -RUN apk add --no-cache \ - autoconf \ - automake \ - bash \ - g++ \ - libc6-compat \ - libjpeg-turbo-dev \ - libpng-dev \ - make \ - nasm \ - git \ - zlib-dev +FROM registry.redhat.io/ubi8/ubi AS app-builder + +# Install dependencies +RUN dnf module install -y nodejs:14 +RUN dnf install -y git +RUN npm install --global yarn # Create the /pgadmin4 directory and copy the source into it. Explicitly # remove the node_modules directory as we'll recreate a clean version, as well @@ -60,145 +52,92 @@ RUN npm install && \ karma.conf.js \ ./pgadmin/static/js/generated/.cache -######################################################################### -# Now, create a documentation build container for the Sphinx docs -######################################################################### +########################################################################## +## Now, create the virtual environment +########################################################################## -FROM python:3.9-alpine3.12 as docs-builder +FROM registry.redhat.io/ubi8/ubi AS env-builder # Install dependencies +RUN dnf install -y gcc make python3-devel python3-pip postgresql-devel krb5-devel + COPY requirements.txt / -RUN apk add --no-cache \ - make \ - build-base \ - openssl-dev \ - libffi-dev \ - postgresql-dev \ - krb5-dev && \ - pip install --no-cache-dir \ - sphinx && \ - pip install --no-cache-dir -r requirements.txt +RUN python3 -m venv /venv +RUN /venv/bin/pip3 install --upgrade pip +RUN /venv/bin/pip3 install -r requirements.txt +RUN /venv/bin/pip3 install gunicorn + +########################################################################## +## Now, create a documentation build container for the Sphinx docs +########################################################################## + +FROM registry.redhat.io/ubi8/ubi AS doc-builder + +# Install dependencies +RUN dnf install -y make python3 +COPY --from=env-builder /venv /venv +RUN /venv/bin/pip3 install sphinx # Copy the docs from the local tree. Explicitly remove any existing builds that -# may be present +# may be present. We don't use the .dockerignore for this as we need to copy +# the _build directory later COPY docs /pgadmin4/docs COPY web /pgadmin4/web RUN rm -rf /pgadmin4/docs/en_US/_build # Build the docs -RUN LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 make -C /pgadmin4/docs/en_US -f Makefile.sphinx html +RUN source /venv/bin/activate && \ + LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 make -C /pgadmin4/docs/en_US -f Makefile.sphinx html -# Cleanup unwanted files -RUN rm -rf /pgadmin4/docs/en_US/_build/html/_sources -RUN rm -rf /pgadmin4/docs/en_US/_build/html/_static/*.png +########################################################################## +## Now, create the base image +########################################################################## -######################################################################### -# Create additional builders to get all of the PostgreSQL utilities -######################################################################### +FROM registry.redhat.io/ubi8/ubi AS base-env -FROM postgres:9.6-alpine as pg96-builder -FROM postgres:10-alpine as pg10-builder -FROM postgres:11-alpine as pg11-builder -FROM postgres:12-alpine as pg12-builder -FROM postgres:13-alpine as pg13-builder - -FROM alpine:3.11 as tool-builder - -# Copy the PG binaries - -COPY --from=pg96-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-9.6/ -COPY --from=pg96-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-9.6/ -COPY --from=pg96-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-9.6/ -COPY --from=pg96-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-9.6/ - -COPY --from=pg10-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-10/ -COPY --from=pg10-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-10/ -COPY --from=pg10-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-10/ -COPY --from=pg10-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-10/ - -COPY --from=pg11-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-11/ -COPY --from=pg11-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-11/ -COPY --from=pg11-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-11/ -COPY --from=pg11-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-11/ - -COPY --from=pg12-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-12/ -COPY --from=pg12-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-12/ -COPY --from=pg12-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-12/ -COPY --from=pg12-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-12/ - -COPY --from=pg13-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-13/ -COPY --from=pg13-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-13/ -COPY --from=pg13-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-13/ -COPY --from=pg13-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-13/ +# Build the base OS +RUN mkdir /output +RUN rpm -i https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm +RUN dnf -y install --downloadonly --downloaddir=/tmp --releasever 8 --setopt=install_weak_deps=false --nodocs glibc-minimal-langpack coreutils-single postfix postgresql13 postgresql12 postgresql11 postgresql10 postgresql96 python3 sed sudo +RUN dnf -y install --installroot=/output --releasever 8 --setopt=install_weak_deps=false --nodocs /tmp/*.rpm +RUN dnf -y --installroot /output --releasever 8 clean all ######################################################################### # Assemble everything into the final container. ######################################################################### -FROM python:3.9-alpine3.12 - -COPY --from=tool-builder /usr/local/pgsql /usr/local/ +FROM scratch WORKDIR /pgadmin4 ENV PYTHONPATH=/pgadmin4 # Copy in the code and docs +COPY --from=base-env /output / COPY --from=app-builder /pgadmin4/web /pgadmin4 -COPY --from=docs-builder /pgadmin4/docs/en_US/_build/html/ /pgadmin4/docs -COPY requirements.txt /pgadmin4/requirements.txt - -# License files -COPY LICENSE /pgadmin4/LICENSE -COPY DEPENDENCIES /pgadmin4/DEPENDENCIES - -# Install build-dependencies, build & install C extensions and purge deps in -# one RUN step -RUN apk add --no-cache --virtual \ - build-deps \ - build-base \ - postgresql-dev \ - libffi-dev \ - krb5-dev \ - e2fsprogs-dev \ - krb5-server-ldap \ - linux-headers && \ - apk add \ - postfix \ - postgresql-client \ - postgresql-libs \ - krb5-libs \ - shadow \ - sudo \ - libcap && \ - pip install --upgrade pip && \ - pip install --no-cache-dir -r requirements.txt && \ - pip install --no-cache-dir gunicorn && \ - apk del --no-cache build-deps && \ - echo "pgadmin ALL = NOPASSWD: /usr/sbin/postfix start" > /etc/sudoers.d/postfix - -# We need the v13 libpq -COPY --from=pg13-builder /usr/local/lib/libpq.so.5.13 /usr/lib/ -RUN ln -sf /usr/lib/libpq.so.5.13 /usr/lib/libpq.so.5 - -# Copy the various scripts +COPY --from=doc-builder /pgadmin4/docs/en_US/_build/html/ /pgadmin4/docs +COPY --from=env-builder /venv /venv COPY pkg/docker/run_pgadmin.py /pgadmin4 COPY pkg/docker/gunicorn_config.py /pgadmin4 COPY pkg/docker/entrypoint.sh /entrypoint.sh -# Precompile and optimize python code to save time and space on startup -RUN python -O -m compileall -x node_modules /pgadmin4 - -RUN groupadd -g 5050 pgadmin && \ - useradd -r -u 5050 -g pgadmin pgadmin && \ +# Perform all the OS-level setup. Do this in one RUN command to minimise the +# number of layers +RUN echo "pgadmin:x:5050:" >> /etc/group && \ + echo "pgadmin:!::" >> /etc/gshadow && \ + echo "pgadmin:x:5050:5050::/pgadmin4:/bin/bash" >> /etc/passwd && \ + echo "pgadmin:!!:18608::::::" >> /etc/shadow && \ mkdir -p /var/lib/pgadmin && \ chown pgadmin:pgadmin /var/lib/pgadmin && \ touch /pgadmin4/config_distro.py && \ - chown pgadmin:pgadmin /pgadmin4/config_distro.py && \ - setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3.9 + chown pgadmin:pgadmin /pgadmin4/config_distro.py # && \ + setcap CAP_NET_BIND_SERVICE=+eip /usr/libexec/platform-python3.6 && \ + sed -i 's/inet_protocols = .*/inet_protocols = ipv4/g' /etc/postfix/main.cf && \ + echo "pgadmin ALL = NOPASSWD: /usr/sbin/postfix start" > /etc/sudoers.d/postfix && \ + USER pgadmin # Finish up VOLUME /var/lib/pgadmin EXPOSE 80 443 -ENTRYPOINT ["/entrypoint.sh"] +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 84457d379..a9314bccf 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -34,7 +34,7 @@ if [ ! -f /var/lib/pgadmin/pgadmin4.db ]; then # Initialize DB before starting Gunicorn # Importing pgadmin4 (from this script) is enough - python run_pgadmin.py + /venv/bin/python run_pgadmin.py export PGADMIN_SERVER_JSON_FILE=${PGADMIN_SERVER_JSON_FILE:-/pgadmin4/servers.json} # Pre-load any required servers @@ -42,9 +42,9 @@ if [ ! -f /var/lib/pgadmin/pgadmin4.db ]; then # When running in Desktop mode, no user is created # so we have to import servers anonymously if [ "${PGADMIN_CONFIG_SERVER_MODE}" = "False" ]; then - /usr/local/bin/python /pgadmin4/setup.py --load-servers "${PGADMIN_SERVER_JSON_FILE}" + /venv/bin/python /pgadmin4/setup.py --load-servers "${PGADMIN_SERVER_JSON_FILE}" else - /usr/local/bin/python /pgadmin4/setup.py --load-servers "${PGADMIN_SERVER_JSON_FILE}" --user ${PGADMIN_DEFAULT_EMAIL} + /venv/bin/python /pgadmin4/setup.py --load-servers "${PGADMIN_SERVER_JSON_FILE}" --user ${PGADMIN_DEFAULT_EMAIL} fi fi fi @@ -56,13 +56,13 @@ fi # Get the session timeout from the pgAdmin config. We'll use this (in seconds) # to define the Gunicorn worker timeout -TIMEOUT=$(cd /pgadmin4 && python -c 'import config; print(config.SESSION_EXPIRATION_TIME * 60 * 60 * 24)') +TIMEOUT=$(cd /pgadmin4 && /venv/bin/python -c 'import config; print(config.SESSION_EXPIRATION_TIME * 60 * 60 * 24)') # NOTE: currently pgadmin can run only with 1 worker due to sessions implementation # Using --threads to have multi-threaded single-process worker if [ ! -z ${PGADMIN_ENABLE_TLS} ]; then - exec gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app + exec /venv/bin/gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app else - exec gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} -c gunicorn_config.py run_pgadmin:app + exec /venv/bin/gunicorn --timeout ${TIMEOUT} --bind ${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80} -w 1 --threads ${GUNICORN_THREADS:-25} --access-logfile ${GUNICORN_ACCESS_LOGFILE:--} -c gunicorn_config.py run_pgadmin:app fi diff --git a/web/config.py b/web/config.py index 8fd8b0391..84792f58c 100644 --- a/web/config.py +++ b/web/config.py @@ -350,7 +350,7 @@ SESSION_COOKIE_NAME = 'pga4_session' # These settings are used when running in web server mode for confirming # and resetting passwords etc. # See: http://pythonhosted.org/Flask-Mail/ for more info -MAIL_SERVER = 'localhost' +MAIL_SERVER = '127.0.0.1' MAIL_PORT = 25 MAIL_USE_SSL = False MAIL_USE_TLS = False