From f8f99903bd893fb681088ce273d2b3e8434c803f Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Wed, 14 Aug 2024 08:15:44 +0200 Subject: [PATCH] Distgen generated content --- 3.12-minimal/Dockerfile.c9s | 110 ++++++ 3.12-minimal/Dockerfile.fedora | 110 ++++++ 3.12-minimal/Dockerfile.rhel8 | 110 ++++++ 3.12-minimal/README.md | 124 +++++++ .../opt/app-root/etc/generate_container_user | 19 ++ 3.12-minimal/root/opt/app-root/etc/scl_enable | 9 + 3.12-minimal/root/opt/wheels | 1 + 3.12-minimal/root/usr/bin/cgroup-limits | 192 +++++++++++ 3.12-minimal/root/usr/bin/fix-permissions | 27 ++ .../root/usr/bin/rpm-file-permissions | 21 ++ 3.12-minimal/s2i/bin/assemble | 134 ++++++++ 3.12-minimal/s2i/bin/init-wrapper | 18 + 3.12-minimal/s2i/bin/run | 157 +++++++++ 3.12-minimal/s2i/bin/usage | 18 + 3.12-minimal/test/app-home-test-app | 1 + 3.12-minimal/test/app-module-test-app | 1 + 3.12-minimal/test/check_imagestreams.py | 1 + .../test/django-different-port-test-app | 1 + 3.12-minimal/test/django-test-app | 1 + .../from-dockerfile/mod_wsgi.Dockerfile.tpl | 1 + .../test/from-dockerfile/uwsgi.Dockerfile.tpl | 20 ++ .../gunicorn-config-different-port-test-app | 1 + .../test/gunicorn-different-port-test-app | 1 + ...-python-configfile-different-port-test-app | 1 + 3.12-minimal/test/imagestreams | 1 + 3.12-minimal/test/locale-test-app | 1 + .../test/micropipenv-requirements-test-app | 1 + .../test/micropipenv-test-app/.gitignore | 57 ++++ .../micropipenv-test-app/.s2i/environment | 5 + .../test/micropipenv-test-app/Pipfile | 15 + .../test/micropipenv-test-app/Pipfile.lock | 211 ++++++++++++ .../test/micropipenv-test-app/setup.py | 10 + .../test/micropipenv-test-app/testapp.py | 12 + 3.12-minimal/test/numpy-test-app | 1 + ...ipenv-and-micropipenv-should-fail-test-app | 1 + 3.12-minimal/test/pipenv-test-app/.gitignore | 57 ++++ .../test/pipenv-test-app/.s2i/environment | 2 + 3.12-minimal/test/pipenv-test-app/Pipfile | 15 + .../test/pipenv-test-app/Pipfile.lock | 211 ++++++++++++ 3.12-minimal/test/pipenv-test-app/setup.py | 10 + 3.12-minimal/test/pipenv-test-app/testapp.py | 12 + 3.12-minimal/test/poetry-src-layout-test-app | 1 + .../pyuwsgi-pipenv-test-app/.s2i/environment | 2 + .../test/pyuwsgi-pipenv-test-app/app.sh | 23 ++ .../pyuwsgi-pipenv-test-app/requirements.txt | 2 + .../test/pyuwsgi-pipenv-test-app/wsgi.py | 9 + 3.12-minimal/test/run | 323 ++++++++++++++++++ .../test/run-openshift-remote-cluster | 1 + 3.12-minimal/test/setup-cfg-test-app | 1 + 3.12-minimal/test/setup-requirements-test-app | 1 + 3.12-minimal/test/setup-test-app | 1 + .../standalone-custom-pypi-index-test-app | 1 + 3.12-minimal/test/standalone-test-app | 1 + 3.12-minimal/test/test-lib-openshift.sh | 1 + 3.12-minimal/test/test-lib-python.sh | 1 + .../test/test-lib-remote-openshift.sh | 1 + 3.12-minimal/test/test-lib.sh | 1 + 3.12-minimal/test/test-openshift.yaml | 1 + 3.12-minimal/test/uwsgi-test-app | 1 + 59 files changed, 2073 insertions(+) create mode 100644 3.12-minimal/Dockerfile.c9s create mode 100644 3.12-minimal/Dockerfile.fedora create mode 100644 3.12-minimal/Dockerfile.rhel8 create mode 100644 3.12-minimal/README.md create mode 100644 3.12-minimal/root/opt/app-root/etc/generate_container_user create mode 100644 3.12-minimal/root/opt/app-root/etc/scl_enable create mode 120000 3.12-minimal/root/opt/wheels create mode 100755 3.12-minimal/root/usr/bin/cgroup-limits create mode 100755 3.12-minimal/root/usr/bin/fix-permissions create mode 100755 3.12-minimal/root/usr/bin/rpm-file-permissions create mode 100755 3.12-minimal/s2i/bin/assemble create mode 100755 3.12-minimal/s2i/bin/init-wrapper create mode 100755 3.12-minimal/s2i/bin/run create mode 100755 3.12-minimal/s2i/bin/usage create mode 120000 3.12-minimal/test/app-home-test-app create mode 120000 3.12-minimal/test/app-module-test-app create mode 120000 3.12-minimal/test/check_imagestreams.py create mode 120000 3.12-minimal/test/django-different-port-test-app create mode 120000 3.12-minimal/test/django-test-app create mode 120000 3.12-minimal/test/from-dockerfile/mod_wsgi.Dockerfile.tpl create mode 100644 3.12-minimal/test/from-dockerfile/uwsgi.Dockerfile.tpl create mode 120000 3.12-minimal/test/gunicorn-config-different-port-test-app create mode 120000 3.12-minimal/test/gunicorn-different-port-test-app create mode 120000 3.12-minimal/test/gunicorn-python-configfile-different-port-test-app create mode 120000 3.12-minimal/test/imagestreams create mode 120000 3.12-minimal/test/locale-test-app create mode 120000 3.12-minimal/test/micropipenv-requirements-test-app create mode 100644 3.12-minimal/test/micropipenv-test-app/.gitignore create mode 100644 3.12-minimal/test/micropipenv-test-app/.s2i/environment create mode 100644 3.12-minimal/test/micropipenv-test-app/Pipfile create mode 100644 3.12-minimal/test/micropipenv-test-app/Pipfile.lock create mode 100644 3.12-minimal/test/micropipenv-test-app/setup.py create mode 100644 3.12-minimal/test/micropipenv-test-app/testapp.py create mode 120000 3.12-minimal/test/numpy-test-app create mode 120000 3.12-minimal/test/pipenv-and-micropipenv-should-fail-test-app create mode 100644 3.12-minimal/test/pipenv-test-app/.gitignore create mode 100644 3.12-minimal/test/pipenv-test-app/.s2i/environment create mode 100644 3.12-minimal/test/pipenv-test-app/Pipfile create mode 100644 3.12-minimal/test/pipenv-test-app/Pipfile.lock create mode 100644 3.12-minimal/test/pipenv-test-app/setup.py create mode 100644 3.12-minimal/test/pipenv-test-app/testapp.py create mode 120000 3.12-minimal/test/poetry-src-layout-test-app create mode 100644 3.12-minimal/test/pyuwsgi-pipenv-test-app/.s2i/environment create mode 100755 3.12-minimal/test/pyuwsgi-pipenv-test-app/app.sh create mode 100644 3.12-minimal/test/pyuwsgi-pipenv-test-app/requirements.txt create mode 100644 3.12-minimal/test/pyuwsgi-pipenv-test-app/wsgi.py create mode 100755 3.12-minimal/test/run create mode 120000 3.12-minimal/test/run-openshift-remote-cluster create mode 120000 3.12-minimal/test/setup-cfg-test-app create mode 120000 3.12-minimal/test/setup-requirements-test-app create mode 120000 3.12-minimal/test/setup-test-app create mode 120000 3.12-minimal/test/standalone-custom-pypi-index-test-app create mode 120000 3.12-minimal/test/standalone-test-app create mode 120000 3.12-minimal/test/test-lib-openshift.sh create mode 120000 3.12-minimal/test/test-lib-python.sh create mode 120000 3.12-minimal/test/test-lib-remote-openshift.sh create mode 120000 3.12-minimal/test/test-lib.sh create mode 120000 3.12-minimal/test/test-openshift.yaml create mode 120000 3.12-minimal/test/uwsgi-test-app diff --git a/3.12-minimal/Dockerfile.c9s b/3.12-minimal/Dockerfile.c9s new file mode 100644 index 00000000..72359f80 --- /dev/null +++ b/3.12-minimal/Dockerfile.c9s @@ -0,0 +1,110 @@ +FROM quay.io/centos/centos:stream9-minimal + + +EXPOSE 8080 + +ENV PYTHON_VERSION=3.12 \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + CNB_STACK_ID=com.redhat.stacks.ubi9-python-312 \ + CNB_USER_ID=1001 \ + CNB_GROUP_ID=0 \ + PIP_NO_CACHE_DIR=off \ + # The following variables are usually available from parent s2i images \ + STI_SCRIPTS_PATH=/usr/libexec/s2i \ + APP_ROOT=/opt/app-root \ + HOME=/opt/app-root/src \ + PLATFORM="el9" + +# /opt/app-root/bin - the main venv +# /opt/app-root/src/bin - app-specific binaries +# /opt/app-root/src/.local/bin - tools like pipenv +ENV PATH=$APP_ROOT/bin:$HOME/bin:$HOME/.local/bin:$PATH + +# RHEL7 base images automatically set these envvars to run scl_enable. RHEl8 +# images, however, don't as most images don't need SCLs any more. But we want +# to run it even on RHEL8, because we set the virtualenv environment as part of +# that script +ENV BASH_ENV=${APP_ROOT}/etc/scl_enable \ + ENV=${APP_ROOT}/etc/scl_enable \ + PROMPT_COMMAND=". ${APP_ROOT}/etc/scl_enable" + +ENV SUMMARY="Minimal platform for building and running Python $PYTHON_VERSION applications" \ + DESCRIPTION="Python $PYTHON_VERSION available as container is a base platform for \ +building and running various Python $PYTHON_VERSION applications and frameworks. \ +Python is an easy to learn, powerful programming language. It has efficient high-level \ +data structures and a simple but effective approach to object-oriented programming. \ +Python's elegant syntax and dynamic typing, together with its interpreted nature, \ +make it an ideal language for scripting and rapid application development in many areas \ +on most platforms." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="Python 3.12" \ + io.openshift.expose-services="8080:http" \ + io.openshift.tags="builder,python,python312,python-312,rh-python312" \ + com.redhat.component="python-312-container" \ + name="sclorg/python-312-minimal-c9s" \ + version="1" \ + usage="s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.12-minimal/test/setup-test-app/ ubi9/python-312-minimal python-sample-app" \ + com.redhat.license_terms="https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI" \ + io.buildpacks.stack.id="com.redhat.stacks.ubi9-python-312-minimal" \ + maintainer="SoftwareCollections.org " + +# Very minimal set of packages +# Python is obvious in the Python container :) +# glibc-langpack-en is needed to set locale to en_US and disable warning about it +# findutils - find command is needed for fix-permissions script +# nss_wrapper - used in generate_container_user script +RUN INSTALL_PKGS="python3.12 glibc-langpack-en findutils nss_wrapper" && \ + microdnf -y --setopt=tsflags=nodocs --setopt=install_weak_deps=0 install $INSTALL_PKGS && \ + microdnf -y clean all --enablerepo='*' + +# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH. +COPY 3.12-minimal/s2i/bin/ $STI_SCRIPTS_PATH + +# Copy extra files to the image. +COPY 3.12-minimal/root/ / + +# Python 3.7+ only +# Yes, the directory below is already copied by the previous command. +# The problem here is that the wheels directory is copied as a symlink. +# Only if you specify symlink directly as a source, COPY copies all the +# files from the symlink destination. +COPY 3.12/root/opt/wheels /opt/wheels + +# This command sets (and also creates if necessary) +# the home directory - it has to be done here so the latter +# fix-permissions fixes this directory as well. +WORKDIR ${HOME} + +# - Create a Python virtual environment for use by any application to avoid +# potential conflicts with Python packages preinstalled in the main Python +# installation. +# - In order to drop the root user, we have to make some directories world +# writable as OpenShift default security model is to run the container +# under random UID. +RUN \ + python3.12 -m venv ${APP_ROOT} && \ + # Python 3 only code, Python 2 installs pip from PyPI in the assemble script. \ + # We have to upgrade pip to a newer verison because: \ + # * pip < 9 does not support different packages' versions for Python 2/3 \ + # * pip < 19.3 does not support manylinux2014 wheels. Only manylinux2014 (and later) wheels \ + # support platforms like ppc64le, aarch64 or armv7 \ + # We are newly using wheel from one of the latest stable Fedora releases (from RPM python-pip-wheel) \ + # because it's tested better then whatever version from PyPI and contains useful patches. \ + # We have to do it here so the permissions are correctly fixed and pip is able \ + # to reinstall itself in the next build phases in the assemble script if user wants the latest version \ + ${APP_ROOT}/bin/pip install /opt/wheels/pip-* && \ + rm -r /opt/wheels && \ + chown -R 1001:0 ${APP_ROOT} && \ + fix-permissions ${APP_ROOT} -P && \ + rpm-file-permissions + +USER 1001 + +# Set the default CMD to print the usage of the language image. +CMD $STI_SCRIPTS_PATH/usage diff --git a/3.12-minimal/Dockerfile.fedora b/3.12-minimal/Dockerfile.fedora new file mode 100644 index 00000000..66e8d0ee --- /dev/null +++ b/3.12-minimal/Dockerfile.fedora @@ -0,0 +1,110 @@ +FROM quay.io/fedora/fedora-minimal:39 + + +EXPOSE 8080 + +ENV PYTHON_VERSION=3.12 \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + CNB_STACK_ID=com.redhat.stacks.ubi-python-312 \ + CNB_USER_ID=1001 \ + CNB_GROUP_ID=0 \ + PIP_NO_CACHE_DIR=off \ + # The following variables are usually available from parent s2i images \ + STI_SCRIPTS_PATH=/usr/libexec/s2i \ + APP_ROOT=/opt/app-root \ + HOME=/opt/app-root/src \ + PLATFORM="el" + +# /opt/app-root/bin - the main venv +# /opt/app-root/src/bin - app-specific binaries +# /opt/app-root/src/.local/bin - tools like pipenv +ENV PATH=$APP_ROOT/bin:$HOME/bin:$HOME/.local/bin:$PATH + +# RHEL7 base images automatically set these envvars to run scl_enable. RHEl8 +# images, however, don't as most images don't need SCLs any more. But we want +# to run it even on RHEL8, because we set the virtualenv environment as part of +# that script +ENV BASH_ENV=${APP_ROOT}/etc/scl_enable \ + ENV=${APP_ROOT}/etc/scl_enable \ + PROMPT_COMMAND=". ${APP_ROOT}/etc/scl_enable" + +ENV SUMMARY="Minimal platform for building and running Python $PYTHON_VERSION applications" \ + DESCRIPTION="Python $PYTHON_VERSION available as container is a base platform for \ +building and running various Python $PYTHON_VERSION applications and frameworks. \ +Python is an easy to learn, powerful programming language. It has efficient high-level \ +data structures and a simple but effective approach to object-oriented programming. \ +Python's elegant syntax and dynamic typing, together with its interpreted nature, \ +make it an ideal language for scripting and rapid application development in many areas \ +on most platforms." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="Python 3.12" \ + io.openshift.expose-services="8080:http" \ + io.openshift.tags="builder,python,python312,python-312,rh-python312" \ + com.redhat.component="python-312-container" \ + name="fedora/python-312-minimal" \ + version="1" \ + usage="s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.12-minimal/test/setup-test-app/ ubi/python-312-minimal python-sample-app" \ + com.redhat.license_terms="https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI" \ + io.buildpacks.stack.id="com.redhat.stacks.ubi-python-312-minimal" \ + maintainer="SoftwareCollections.org " + +# Very minimal set of packages +# Python is obvious in the Python container :) +# glibc-langpack-en is needed to set locale to en_US and disable warning about it +# findutils - find command is needed for fix-permissions script +# nss_wrapper - used in generate_container_user script +RUN INSTALL_PKGS="python3.12 glibc-langpack-en findutils nss_wrapper" && \ + microdnf -y --setopt=tsflags=nodocs --setopt=install_weak_deps=0 install $INSTALL_PKGS && \ + microdnf -y clean all --enablerepo='*' + +# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH. +COPY 3.12-minimal/s2i/bin/ $STI_SCRIPTS_PATH + +# Copy extra files to the image. +COPY 3.12-minimal/root/ / + +# Python 3.7+ only +# Yes, the directory below is already copied by the previous command. +# The problem here is that the wheels directory is copied as a symlink. +# Only if you specify symlink directly as a source, COPY copies all the +# files from the symlink destination. +COPY 3.12/root/opt/wheels /opt/wheels + +# This command sets (and also creates if necessary) +# the home directory - it has to be done here so the latter +# fix-permissions fixes this directory as well. +WORKDIR ${HOME} + +# - Create a Python virtual environment for use by any application to avoid +# potential conflicts with Python packages preinstalled in the main Python +# installation. +# - In order to drop the root user, we have to make some directories world +# writable as OpenShift default security model is to run the container +# under random UID. +RUN \ + python3.12 -m venv ${APP_ROOT} && \ + # Python 3 only code, Python 2 installs pip from PyPI in the assemble script. \ + # We have to upgrade pip to a newer verison because: \ + # * pip < 9 does not support different packages' versions for Python 2/3 \ + # * pip < 19.3 does not support manylinux2014 wheels. Only manylinux2014 (and later) wheels \ + # support platforms like ppc64le, aarch64 or armv7 \ + # We are newly using wheel from one of the latest stable Fedora releases (from RPM python-pip-wheel) \ + # because it's tested better then whatever version from PyPI and contains useful patches. \ + # We have to do it here so the permissions are correctly fixed and pip is able \ + # to reinstall itself in the next build phases in the assemble script if user wants the latest version \ + ${APP_ROOT}/bin/pip install /opt/wheels/pip-* && \ + rm -r /opt/wheels && \ + chown -R 1001:0 ${APP_ROOT} && \ + fix-permissions ${APP_ROOT} -P && \ + rpm-file-permissions + +USER 1001 + +# Set the default CMD to print the usage of the language image. +CMD $STI_SCRIPTS_PATH/usage diff --git a/3.12-minimal/Dockerfile.rhel8 b/3.12-minimal/Dockerfile.rhel8 new file mode 100644 index 00000000..ca8bef4d --- /dev/null +++ b/3.12-minimal/Dockerfile.rhel8 @@ -0,0 +1,110 @@ +FROM ubi8/ubi-minimal:latest + + +EXPOSE 8080 + +ENV PYTHON_VERSION=3.12 \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + CNB_STACK_ID=com.redhat.stacks.ubi8-python-312 \ + CNB_USER_ID=1001 \ + CNB_GROUP_ID=0 \ + PIP_NO_CACHE_DIR=off \ + # The following variables are usually available from parent s2i images \ + STI_SCRIPTS_PATH=/usr/libexec/s2i \ + APP_ROOT=/opt/app-root \ + HOME=/opt/app-root/src \ + PLATFORM="el8" + +# /opt/app-root/bin - the main venv +# /opt/app-root/src/bin - app-specific binaries +# /opt/app-root/src/.local/bin - tools like pipenv +ENV PATH=$APP_ROOT/bin:$HOME/bin:$HOME/.local/bin:$PATH + +# RHEL7 base images automatically set these envvars to run scl_enable. RHEl8 +# images, however, don't as most images don't need SCLs any more. But we want +# to run it even on RHEL8, because we set the virtualenv environment as part of +# that script +ENV BASH_ENV=${APP_ROOT}/etc/scl_enable \ + ENV=${APP_ROOT}/etc/scl_enable \ + PROMPT_COMMAND=". ${APP_ROOT}/etc/scl_enable" + +ENV SUMMARY="Minimal platform for building and running Python $PYTHON_VERSION applications" \ + DESCRIPTION="Python $PYTHON_VERSION available as container is a base platform for \ +building and running various Python $PYTHON_VERSION applications and frameworks. \ +Python is an easy to learn, powerful programming language. It has efficient high-level \ +data structures and a simple but effective approach to object-oriented programming. \ +Python's elegant syntax and dynamic typing, together with its interpreted nature, \ +make it an ideal language for scripting and rapid application development in many areas \ +on most platforms." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="Python 3.12" \ + io.openshift.expose-services="8080:http" \ + io.openshift.tags="builder,python,python312,python-312,rh-python312" \ + com.redhat.component="python-312-container" \ + name="ubi8/python-312-minimal" \ + version="1" \ + usage="s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.12-minimal/test/setup-test-app/ ubi8/python-312-minimal python-sample-app" \ + com.redhat.license_terms="https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI" \ + io.buildpacks.stack.id="com.redhat.stacks.ubi8-python-312-minimal" \ + maintainer="SoftwareCollections.org " + +# Very minimal set of packages +# Python is obvious in the Python container :) +# glibc-langpack-en is needed to set locale to en_US and disable warning about it +# findutils - find command is needed for fix-permissions script +# nss_wrapper - used in generate_container_user script +RUN INSTALL_PKGS="python3.12 glibc-langpack-en findutils nss_wrapper" && \ + microdnf -y --setopt=tsflags=nodocs --setopt=install_weak_deps=0 install $INSTALL_PKGS && \ + microdnf -y clean all --enablerepo='*' + +# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH. +COPY 3.12-minimal/s2i/bin/ $STI_SCRIPTS_PATH + +# Copy extra files to the image. +COPY 3.12-minimal/root/ / + +# Python 3.7+ only +# Yes, the directory below is already copied by the previous command. +# The problem here is that the wheels directory is copied as a symlink. +# Only if you specify symlink directly as a source, COPY copies all the +# files from the symlink destination. +COPY 3.12/root/opt/wheels /opt/wheels + +# This command sets (and also creates if necessary) +# the home directory - it has to be done here so the latter +# fix-permissions fixes this directory as well. +WORKDIR ${HOME} + +# - Create a Python virtual environment for use by any application to avoid +# potential conflicts with Python packages preinstalled in the main Python +# installation. +# - In order to drop the root user, we have to make some directories world +# writable as OpenShift default security model is to run the container +# under random UID. +RUN \ + python3.12 -m venv ${APP_ROOT} && \ + # Python 3 only code, Python 2 installs pip from PyPI in the assemble script. \ + # We have to upgrade pip to a newer verison because: \ + # * pip < 9 does not support different packages' versions for Python 2/3 \ + # * pip < 19.3 does not support manylinux2014 wheels. Only manylinux2014 (and later) wheels \ + # support platforms like ppc64le, aarch64 or armv7 \ + # We are newly using wheel from one of the latest stable Fedora releases (from RPM python-pip-wheel) \ + # because it's tested better then whatever version from PyPI and contains useful patches. \ + # We have to do it here so the permissions are correctly fixed and pip is able \ + # to reinstall itself in the next build phases in the assemble script if user wants the latest version \ + ${APP_ROOT}/bin/pip install /opt/wheels/pip-* && \ + rm -r /opt/wheels && \ + chown -R 1001:0 ${APP_ROOT} && \ + fix-permissions ${APP_ROOT} -P && \ + rpm-file-permissions + +USER 1001 + +# Set the default CMD to print the usage of the language image. +CMD $STI_SCRIPTS_PATH/usage diff --git a/3.12-minimal/README.md b/3.12-minimal/README.md new file mode 100644 index 00000000..3bc43b3e --- /dev/null +++ b/3.12-minimal/README.md @@ -0,0 +1,124 @@ +Python 3.12 container image - minimal version +============================================ + +This container image is a special version of the [full Python 3.12 container image](https://github.com/sclorg/s2i-python-container/tree/master/3.12) +provided as a [S2I](https://github.com/openshift/source-to-image) base image for your Python 3.12 applications. + +Because the minimal and full images work similarly, we document here only the differences and limitations +of the minimal container image. For the documentation of common features see the [full container image docs](https://github.com/sclorg/s2i-python-container/tree/master/3.12). + +The Python 3.12 minimal container image is currently considered a tech-preview and only available on quay.io. +The image is built on top of the [Red Hat Universal Base Image 8 Micro image](https://catalog.redhat.com/software/containers/ubi8-micro/601a84aadd19c7786c47c8ea) +and therefore uses the RPM packages from Red Hat Enterprise Linux, the same that are used by Python 3.9 supported container image and are part of the [UBI registry](https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image). + +To pull the Python 3.12 minimal container image to build on, run + +``` +podman pull quay.io/sclorg/python-312-minimal-c9s +``` + +Description +----------- + +The full container image is a universal base image to build your containerized applications on top of. However, its universal nature +means that the resulting containers it produces consume a lot of disk space. This is caused mainly by the fact that the image contains +npm, compilers, header files and some other packages one might need to install and deploy their applications. + +Because size does matter for us and our customers, we have prepared this minimal container image with very limited subset +of installed packages. There are no compilers, no header files, no npm etc and the yum package manager is replaced with a minimalistic +reimplementation called microdnf, making the resulting container images much smaller. This creates some limitations +but we provide ways to workaround them. + +Limitations +----------- + +1. There is only a very limited subset of packages installed. They are choosen carefully to satisfy most of the Python apps but your app might have some special needs. +1. There is no npm and nodejs. +1. There are no compilers and header files. Installation from Python wheels should still work but compilation from a source code is not supported out of the box. + +In the next chapter, we provide three possible workarounds for the mentioned limitations of the minimal container image. + +Possible solutions for the limitations +-------------------------------------- + +### Use the full container image + +It's easy at that. If you don't want to write your own Dockerfile and disk space is not a problem, use +the full universal container image and you should be fine. + +### Build your own container image on top of the minimal container image + +Let's say that your application depends on uwsgi. uwsgi cannot be installed from Python wheel and has to be +compiled from source which requires some additional packages to be installed - namely gcc for the compilation +itself and python3.12-devel containing Python header files. + +To solve that problem, you can use all the pieces provided by the minimal container image and just add one more +step to install the missing dependencies: + +``` +FROM python-312-minimal + +# Add application sources to a directory that the assemble script expects them +# and set permissions so that the container runs without root access +USER 0 +ADD app-src /tmp/src +RUN /usr/bin/fix-permissions /tmp/src + +# Install packages necessary for compiling uwsgi from source +RUN microdnf install -y gcc python3.12-devel +USER 1001 + +# Install the dependencies +RUN /usr/libexec/s2i/assemble + +# Set the default command for the resulting image +CMD /usr/libexec/s2i/run +``` + +If you do it this way, your problem with the missing packages is solved. But there is also one disadvantage: the resulting +runtime image contains unnecessary compiler and Python header files. How to solve this? Uninstalling them at the end +of the Dockerfile is not really a solution but we have one. Keep reading. + +### Build on full image, run on minimal image + +Did you know that you can copy files from one image to another one during a build? That's the feature we are gonna use now. +We use the full container image with all compilers and other usefull packages installed to build our app and its dependencies +and we then move the result including the whole virtual environemnt to the minimal container image. + +This app needs mod_wsgi and to install (compile it from source) it, we'll need: httpd-devel for header files, gcc and redhat-rpm-config +as a compiler and configuratuion and finally python3.12-devel containing Python header files. There is no need to install those packages +manually because the full container image already contains them. However, the application needs httpd as a runtime dependency +so we need to install it to the minimal container image as well. + +``` +# Part 1 - build + +FROM python-312 as builder + +# Add application sources to a directory that the assemble script expects them +# and set permissions so that the container runs without root access +USER 0 +ADD app-src /tmp/src +RUN /usr/bin/fix-permissions /tmp/src +USER 1001 + +# Install the application's dependencies from PyPI +RUN /usr/libexec/s2i/assemble + +# Part 2 - deploy + +FROM python-312-minimal + +# Copy app sources together with the whole virtual environment from the builder image +COPY --from=builder $APP_ROOT $APP_ROOT + +# Install httpd package - runtime dependency of our application +USER 0 +RUN microdnf install -y httpd +USER 1001 + +# Set the default command for the resulting image +CMD /usr/libexec/s2i/run +``` + +This way, the resulting container image does contain only necessary dependencies and it's much lighter. diff --git a/3.12-minimal/root/opt/app-root/etc/generate_container_user b/3.12-minimal/root/opt/app-root/etc/generate_container_user new file mode 100644 index 00000000..a7fd74d6 --- /dev/null +++ b/3.12-minimal/root/opt/app-root/etc/generate_container_user @@ -0,0 +1,19 @@ +# Set current user in nss_wrapper +USER_ID=$(id -u) +GROUP_ID=$(id -g) + +if [ x"$USER_ID" != x"0" -a x"$USER_ID" != x"1001" ]; then + + NSS_WRAPPER_PASSWD=/opt/app-root/etc/passwd + NSS_WRAPPER_GROUP=/etc/group + + cat /etc/passwd | sed -e 's/^default:/builder:/' > $NSS_WRAPPER_PASSWD + + echo "default:x:${USER_ID}:${GROUP_ID}:Default Application User:${HOME}:/bin/bash" >> $NSS_WRAPPER_PASSWD + + export NSS_WRAPPER_PASSWD + export NSS_WRAPPER_GROUP + + LD_PRELOAD=libnss_wrapper.so + export LD_PRELOAD +fi diff --git a/3.12-minimal/root/opt/app-root/etc/scl_enable b/3.12-minimal/root/opt/app-root/etc/scl_enable new file mode 100644 index 00000000..05737ab4 --- /dev/null +++ b/3.12-minimal/root/opt/app-root/etc/scl_enable @@ -0,0 +1,9 @@ +# IMPORTANT: Do not add more content to this file unless you know what you are +# doing. This file is sourced everytime the shell session is opened. +# This will make scl collection binaries work out of box. +unset BASH_ENV PROMPT_COMMAND ENV +if head "/etc/redhat-release" | grep -q "^CentOS Linux release 7" || \ + head "/etc/redhat-release" | grep -q "^Red Hat Enterprise Linux\( Server\)\? release 7"; then + source scl_source enable httpd24 $NODEJS_SCL +fi +source /opt/app-root/bin/activate diff --git a/3.12-minimal/root/opt/wheels b/3.12-minimal/root/opt/wheels new file mode 120000 index 00000000..4eabc067 --- /dev/null +++ b/3.12-minimal/root/opt/wheels @@ -0,0 +1 @@ +../../../src/root/opt/wheels/ \ No newline at end of file diff --git a/3.12-minimal/root/usr/bin/cgroup-limits b/3.12-minimal/root/usr/bin/cgroup-limits new file mode 100755 index 00000000..6fa19153 --- /dev/null +++ b/3.12-minimal/root/usr/bin/cgroup-limits @@ -0,0 +1,192 @@ +#!/usr/libexec/platform-python + +""" +Script for parsing cgroup information + +This script will read some limits from the cgroup system and parse +them, printing out "VARIABLE=VALUE" on each line for every limit that is +successfully read. Output of this script can be directly fed into +bash's export command. Recommended usage from a bash script: + + set -o errexit + export_vars=$(cgroup-limits) ; export $export_vars + +Variables currently supported: + MAX_MEMORY_LIMIT_IN_BYTES + Maximum possible limit MEMORY_LIMIT_IN_BYTES can have. This is + currently constant value of 9223372036854775807. + MEMORY_LIMIT_IN_BYTES + Maximum amount of user memory in bytes. If this value is set + to the same value as MAX_MEMORY_LIMIT_IN_BYTES, it means that + there is no limit set. The value is taken from + /sys/fs/cgroup/memory/memory.limit_in_bytes for cgroups v1 + and from /sys/fs/cgroup/memory.max for cgroups v2 + NUMBER_OF_CORES + Number of CPU cores that can be used. If both the cpu and cpuset + controllers specify a limit, the controller with the lowest CPU + limit takes precedence. For the cpu controller, the value is + calculated from /sys/fs/cgroup/cpu/cpu.cfs_{quota,period}_us for + cgroups v1 and from /sys/fs/cgroup/cpuset.cpus.effective for cgroups v2. + For the cpuset controller, the value is taken from + /sys/fs/cgroup/cpuset/cpuset.cpus for cgroups v1 and from + /sys/fs/cgroup/cpuset.cpus.effective for cgroups v2 + NO_MEMORY_LIMIT + Set to "true" if MEMORY_LIMIT_IN_BYTES is so high that the caller + can act as if no memory limit was set. Undefined otherwise. + +Note about non-root containers: + + Per podman-run(1) man page, on some systems, changing the resource limits + may not be allowed for non-root users. For more details, see + https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-resource-limits-fails-with-a-permissions-error. + +How to test this script: + + Run a container as root and see whether the output of available memory + and CPUs match what is either available on the host or specified via + cgroups limits by the container runtime (podman). + + For example: + + # This should return NO_MEMORY_LIMIT=true, NUMBER_OF_CORES= + sudo podman run -ti --rm quay.io/fedora/s2i-core /usr/bin/cgroup-limits + + # This should return MEMORY_LIMIT_IN_BYTES=2147483648, NUMBER_OF_CORES=3 + # 3 CPUs despite --cpus 4 was given is correct, because the cpuset 2-4 means + # running on 3 concrete processors only, which is lower limit than --cpus + # and thus is preferred + sudo podman run -ti --rm --cpus 4 --cpuset-cpus=2-4 --memory 2G quay.io/fedora/s2i-core /usr/bin/cgroup-limits +""" + +from __future__ import division +from __future__ import print_function +import sys +import subprocess + + +def _read_file(path): + try: + with open(path, 'r') as f: + return f.read().strip() + except IOError: + return None + + +def get_memory_limit(): + """ + Read memory limit, in bytes. + """ + + limit = _read_file('/sys/fs/cgroup/memory/memory.limit_in_bytes') + # If first file does not exist, try cgroups v2 file + limit = limit or _read_file('/sys/fs/cgroup/memory.max') + if limit is None or not limit.isdigit(): + if limit == 'max': + return 9223372036854775807 + print("Warning: Can't detect memory limit from cgroups", + file=sys.stderr) + return None + return int(limit) + + +def get_number_of_cores(): + """ + Read number of CPU cores. + """ + + limits = [l for l in [_read_cpu_quota(), _read_cpuset_size(), _read_nproc()] if l] + if not limits: + return None + return min(limits) + + +def _read_cpu_quota(): + + quota, period = None, None + + quota = _read_file("/sys/fs/cgroup/cpu/cpu.cfs_quota_us") + if quota: + # cgroups v1 + quota = quota.strip() + if quota == "-1": + return None + + period = _read_file("/sys/fs/cgroup/cpu/cpu.cfs_period_us") + if period: + period = period.strip() + + else: + # cgroups v2 + line = _read_file("/sys/fs/cgroup/cpu.max") + if line: + fields = line.split() + + if len(fields) >= 2: + quota = fields[0] + if quota == "max": + return None + period = fields[1] + + + if quota and quota.isdigit() and period and period.isdigit(): + return int(quota)//int(period) + + print("Warning: Can't detect cpu quota from cgroups", + file=sys.stderr) + return None + + +def _read_cpuset_size(): + + core_count = 0 + + line = _read_file('/sys/fs/cgroup/cpuset/cpuset.cpus') + # If first file does not exist, try cgroups v2 file + line = line or _read_file('/sys/fs/cgroup/cpuset.cpus.effective') + if line is None: + # None of the files above exists when running podman as non-root, + # so in that case, this warning is printed every-time + print("Warning: Can't detect cpuset size from cgroups, will use nproc", + file=sys.stderr) + return None + + for group in line.split(','): + core_ids = list(map(int, group.split('-'))) + if len(core_ids) == 2: + core_count += core_ids[1] - core_ids[0] + 1 + else: + core_count += 1 + + return core_count + + +def _read_nproc(): + """ + Returns number of cores without looking at cgroup limits. + This might work as the last resort when running a container as non-root + where cgroups v2 resource limits cannot be set without a specific + configuration (per podman-run(1) man page). + """ + try: + stdout, stderr = subprocess.Popen('nproc', stdout=subprocess.PIPE).communicate() + return int(stdout) + except EnvironmentError as e: + if e.errno != errno.ENOENT: + raise + return None + + +if __name__ == "__main__": + env_vars = { + "MAX_MEMORY_LIMIT_IN_BYTES": 9223372036854775807, + "MEMORY_LIMIT_IN_BYTES": get_memory_limit(), + "NUMBER_OF_CORES": get_number_of_cores() + } + + env_vars = {k: v for k, v in env_vars.items() if v is not None} + + if env_vars.get("MEMORY_LIMIT_IN_BYTES", 0) >= 92233720368547: + env_vars["NO_MEMORY_LIMIT"] = "true" + + for key, value in env_vars.items(): + print("{0}={1}".format(key, value)) diff --git a/3.12-minimal/root/usr/bin/fix-permissions b/3.12-minimal/root/usr/bin/fix-permissions new file mode 100755 index 00000000..ddd33ace --- /dev/null +++ b/3.12-minimal/root/usr/bin/fix-permissions @@ -0,0 +1,27 @@ +#!/bin/sh + +# Allow this script to fail without failing a build +set +e + +SYMLINK_OPT=${2:--L} + +# Fix permissions on the given directory or file to allow group read/write of +# regular files and execute of directories. + +[ $(id -u) -ne 0 ] && CHECK_OWNER=" -uid $(id -u)" + +# If argument does not exist, script will still exit with 0, +# but at least we'll see something went wrong in the log +if ! [ -e "$1" ] ; then + echo "ERROR: File or directory $1 does not exist." >&2 + # We still want to end successfully + exit 0 +fi + +find $SYMLINK_OPT "$1" ${CHECK_OWNER} \! -gid 0 -exec chgrp 0 {} + +find $SYMLINK_OPT "$1" ${CHECK_OWNER} \! -perm -g+rw -exec chmod g+rw {} + +find $SYMLINK_OPT "$1" ${CHECK_OWNER} -perm /u+x -a \! -perm /g+x -exec chmod g+x {} + +find $SYMLINK_OPT "$1" ${CHECK_OWNER} -type d \! -perm /g+x -exec chmod g+x {} + + +# Always end successfully +exit 0 diff --git a/3.12-minimal/root/usr/bin/rpm-file-permissions b/3.12-minimal/root/usr/bin/rpm-file-permissions new file mode 100755 index 00000000..8be1fb0d --- /dev/null +++ b/3.12-minimal/root/usr/bin/rpm-file-permissions @@ -0,0 +1,21 @@ +#!/bin/sh + +CHECK_DIRS="/ /opt /etc /usr /usr/bin /usr/lib /usr/lib64 /usr/share /usr/libexec" + +rpm_format="[%{FILESTATES:fstate} %7{FILEMODES:octal} %{FILENAMES:shescape}\n]" + +rpm -q --qf "$rpm_format" filesystem | while read line +do + eval "set -- $line" + + case $1 in + normal) ;; + *) continue ;; + esac + + case " $CHECK_DIRS " in + *" $3 "*) + chmod "${2: -4}" "$3" + ;; + esac +done diff --git a/3.12-minimal/s2i/bin/assemble b/3.12-minimal/s2i/bin/assemble new file mode 100755 index 00000000..34a68dbb --- /dev/null +++ b/3.12-minimal/s2i/bin/assemble @@ -0,0 +1,134 @@ +#!/bin/bash + +function is_django_installed() { + python -c "import django" &>/dev/null +} + +function should_collectstatic() { + is_django_installed && [[ -z "$DISABLE_COLLECTSTATIC" ]] +} + +function virtualenv_bin() { + # New versions of Python (>3.6) should use venv module + # from stdlib instead of virtualenv package + python3.12 -m venv $1 +} + +# Install pipenv or micropipenv to the separate virtualenv to isolate it +# from system Python packages and packages in the main +# virtualenv. Executable is simlinked into ~/.local/bin +# to be accessible. This approach is inspired by pipsi +# (pip script installer). +function install_tool() { + echo "---> Installing $1 packaging tool ..." + VENV_DIR=$HOME/.local/venvs/$1 + virtualenv_bin "$VENV_DIR" + # First, try to install the tool without --isolated which means that if you + # have your own PyPI mirror, it will take it from there. If this try fails, try it + # again with --isolated which ignores external pip settings (env vars, config file) + # and installs the tool from PyPI (needs internet connetion). + # $1$2 combines package name with [extras] or version specifier if is defined as $2``` + if ! $VENV_DIR/bin/pip install -U $1$2; then + echo "WARNING: Installation of $1 failed, trying again from official PyPI with pip --isolated install" + $VENV_DIR/bin/pip install --isolated -U $1$2 # Combines package name with [extras] or version specifier if is defined as $2``` + fi + mkdir -p $HOME/.local/bin + ln -s $VENV_DIR/bin/$1 $HOME/.local/bin/$1 +} + +set -e + +# First of all, check that we don't have disallowed combination of ENVs +if [[ ! -z "$ENABLE_PIPENV" && ! -z "$ENABLE_MICROPIPENV" ]]; then + echo "ERROR: Pipenv and micropipenv cannot be enabled at the same time!" + # podman/buildah does not relay this exit code but it will be fixed hopefuly + # https://github.com/containers/buildah/issues/2305 + exit 3 +fi + +shopt -s dotglob +echo "---> Installing application source ..." +mv /tmp/src/* "$HOME" + +# set permissions for any installed artifacts +fix-permissions /opt/app-root -P + + +if [[ ! -z "$PIP_INDEX_URL" ]]; then + echo "---> Creating pip.ini config file" + echo "[global] +index-url = $PIP_INDEX_URL" >> /opt/app-root/src/pip.ini +fi + +if [[ ! -z "$UPGRADE_PIP_TO_LATEST" ]]; then + echo "---> Upgrading pip, setuptools and wheel to latest version ..." + if ! pip install -U pip setuptools wheel; then + echo "WARNING: Installation of the latest pip, setuptools and wheel failed, trying again from official PyPI with pip --isolated install" + pip install --isolated -U pip setuptools wheel + fi +fi + +if [[ ! -z "$ENABLE_PIPENV" ]]; then + if [[ ! -z "$PIN_PIPENV_VERSION" ]]; then + # Add == as a prefix to pipenv version, if defined + PIN_PIPENV_VERSION="==$PIN_PIPENV_VERSION" + fi + install_tool "pipenv" "$PIN_PIPENV_VERSION" + echo "---> Installing dependencies via pipenv ..." + if [[ -f Pipfile ]]; then + pipenv install --deploy + elif [[ -f requirements.txt ]]; then + pipenv install -r requirements.txt + fi + # pipenv check +elif [[ ! -z "$ENABLE_MICROPIPENV" ]]; then + install_tool "micropipenv" "[toml]" + echo "---> Installing dependencies via micropipenv ..." + # micropipenv detects Pipfile.lock and requirements.txt in this order + micropipenv install --deploy +elif [[ -f requirements.txt ]]; then + echo "---> Installing dependencies ..." + pip install -r requirements.txt +fi + +if [[ ( -f setup.py || -f setup.cfg ) && -z "$DISABLE_SETUP_PY_PROCESSING" ]]; then + echo "---> Installing application (via setup.{py,cfg})..." + pip install . +fi + +if [[ -f pyproject.toml && -z "$DISABLE_PYPROJECT_TOML_PROCESSING" ]]; then + echo "---> Installing application (via pyproject.toml)..." + pip install . +fi + +if should_collectstatic; then + ( + echo "---> Collecting Django static files ..." + + APP_HOME=$(readlink -f "${APP_HOME:-.}") + # Change the working directory to APP_HOME + PYTHONPATH="$(pwd)${PYTHONPATH:+:$PYTHONPATH}" + cd "$APP_HOME" + + # Look for 'manage.py' in the current directory + manage_file=./manage.py + + if [[ ! -f "$manage_file" ]]; then + echo "WARNING: seems that you're using Django, but we could not find a 'manage.py' file." + echo "'manage.py collectstatic' ignored." + exit + fi + + if ! python $manage_file collectstatic --dry-run --noinput &> /dev/null; then + echo "WARNING: could not run 'manage.py collectstatic'. To debug, run:" + echo " $ python $manage_file collectstatic --noinput" + echo "Ignore this warning if you're not serving static files with Django." + exit + fi + + python $manage_file collectstatic --noinput + ) +fi + +# set permissions for any installed artifacts +fix-permissions /opt/app-root -P diff --git a/3.12-minimal/s2i/bin/init-wrapper b/3.12-minimal/s2i/bin/init-wrapper new file mode 100755 index 00000000..26c8767d --- /dev/null +++ b/3.12-minimal/s2i/bin/init-wrapper @@ -0,0 +1,18 @@ +#!/bin/sh + +# Overview of how this script works: http://veithen.io/2014/11/16/sigterm-propagation.html +# Set a trap to kill the main app process when this +# init script receives SIGTERM or SIGINT +trap 'kill -s TERM $PID' TERM INT +# Execute the main application in the background +"$@" & +PID=$! +# wait command always terminates when trap is caught, even if the process hasn't finished yet +wait $PID +# Remove the trap and wait till the app process finishes completely +trap - TERM INT +# We wait again, since the first wait terminates when trap is caught +wait $PID +# Exit with the exit code of the app process +STATUS=$? +exit $STATUS diff --git a/3.12-minimal/s2i/bin/run b/3.12-minimal/s2i/bin/run new file mode 100755 index 00000000..2021cddb --- /dev/null +++ b/3.12-minimal/s2i/bin/run @@ -0,0 +1,157 @@ +#!/bin/bash +source /opt/app-root/etc/generate_container_user + +set -e + +function is_gunicorn_installed() { + hash gunicorn &>/dev/null +} + +function is_django_installed() { + python -c "import django" &>/dev/null +} + +function should_migrate() { + is_django_installed && [[ -z "$DISABLE_MIGRATE" ]] +} + +# Guess the number of workers according to the number of cores +function get_default_web_concurrency() { + # Using python and just python here because the script has + # platform-python in its shebang which we don't have in the minimal image + limit_vars=$(python /usr/bin/cgroup-limits) + local $limit_vars + if [ -z "${NUMBER_OF_CORES:-}" ]; then + echo 1 + return + fi + + local max=$((NUMBER_OF_CORES*2)) + # Require at least 43 MiB and additional 40 MiB for every worker + local default=$(((${MEMORY_LIMIT_IN_BYTES:-MAX_MEMORY_LIMIT_IN_BYTES}/1024/1024 - 43) / 40)) + default=$((default > max ? max : default)) + default=$((default < 1 ? 1 : default)) + # According to http://docs.gunicorn.org/en/stable/design.html#how-many-workers, + # 12 workers should be enough to handle hundreds or thousands requests per second + default=$((default > 12 ? 12 : default)) + echo $default +} + +function maybe_run_in_init_wrapper() { + if [[ -z "$ENABLE_INIT_WRAPPER" ]]; then + exec "$@" + else + exec $STI_SCRIPTS_PATH/init-wrapper "$@" + fi +} + +# Look for gunicorn>=20.1.0 to utilize gunicorn.conf.py +if is_gunicorn_installed && [[ -f "gunicorn.conf.py" ]]; then + ret=$(python -c 'import gunicorn +ver = gunicorn.version_info +print(0 if ver[0]>=21 or (ver[0] == 20 and ver[1] >= 1) else 1)') + grep -q "wsgi_app" gunicorn.conf.py && grep_result=0 || grep_result=1 + if [[ $ret -eq 0 ]] && [[ $grep_result -eq 0 ]]; then + echo "---> Using gunicorn.conf.py" + echo "---> Serving application with gunicorn ..." + exec gunicorn + fi +fi + +APP_HOME=$(readlink -f "${APP_HOME:-.}") +# Change the working directory to APP_HOME +PYTHONPATH="$(pwd)${PYTHONPATH:+:$PYTHONPATH}" +cd "$APP_HOME" + +if [ -z "$APP_SCRIPT" ] && [ -z "$APP_FILE" ] && [ -z "$APP_MODULE" ]; then + # Set default values for APP_SCRIPT and APP_FILE only when all three APP_ + # variables are not defined by user. This prevents a situation when + # APP_MODULE is defined to app:application but the app.py file is found as the + # APP_FILE and then executed by Python instead of gunicorn. + APP_SCRIPT="app.sh" + APP_SCRIPT_DEFAULT=1 + APP_FILE="app.py" + APP_FILE_DEFAULT=1 +fi + +if [ ! -z "$APP_SCRIPT" ]; then + if [[ -f "$APP_SCRIPT" ]]; then + echo "---> Running application from script ($APP_SCRIPT) ..." + if [[ "$APP_SCRIPT" != /* ]]; then + APP_SCRIPT="./$APP_SCRIPT" + fi + maybe_run_in_init_wrapper "$APP_SCRIPT" + elif [[ -z "$APP_SCRIPT_DEFAULT" ]]; then + echo "ERROR: file '$APP_SCRIPT' not found." && exit 1 + fi +fi + +if [ ! -z "$APP_FILE" ]; then + if [[ -f "$APP_FILE" ]]; then + echo "---> Running application from Python script ($APP_FILE) ..." + maybe_run_in_init_wrapper python "$APP_FILE" + elif [[ -z "$APP_FILE_DEFAULT" ]]; then + echo "ERROR: file '$APP_FILE' not found." && exit 1 + fi +fi + +# Look for 'manage.py' in the current directory +manage_file=./manage.py + +if should_migrate; then + if [[ -f "$manage_file" ]]; then + echo "---> Migrating database ..." + python "$manage_file" migrate --noinput + else + echo "WARNING: seems that you're using Django, but we could not find a 'manage.py' file." + echo "Skipped 'python manage.py migrate'." + fi +fi + +# If not set, use 8080 as the default port +if [ -z "$PORT" ]; then + PORT=8080 +fi + +if is_gunicorn_installed; then + setup_py=$(find "$HOME" -maxdepth 2 -type f -name 'setup.py' -print -quit) + # Look for wsgi module in the current directory + if [[ -z "$APP_MODULE" && -f "./wsgi.py" ]]; then + APP_MODULE=wsgi + elif [[ -z "$APP_MODULE" && -f "$setup_py" ]]; then + APP_MODULE="$(python "$setup_py" --name)" + fi + + if [[ "$APP_MODULE" ]]; then + export WEB_CONCURRENCY=${WEB_CONCURRENCY:-$(get_default_web_concurrency)} + + # Default settings for gunicorn if none of the custom are set + if [ -z "$APP_CONFIG" ] && [ -z "$GUNICORN_CMD_ARGS" ]; then + GUNICORN_CMD_ARGS="--bind=0.0.0.0:$PORT --access-logfile=-" + gunicorn_settings_source="default" + else + gunicorn_settings_source="custom" + fi + + # Gunicorn can read GUNICORN_CMD_ARGS as an env variable but because this is not + # supported in Gunicorn < 20 we still need for Python 2, we are using arguments directly. + echo "---> Serving application with gunicorn ($APP_MODULE) with $gunicorn_settings_source settings ..." + exec gunicorn "$APP_MODULE" $GUNICORN_CMD_ARGS --config "$APP_CONFIG" + fi +fi + +if is_django_installed; then + if [[ -f "$manage_file" ]]; then + echo "---> Serving application with 'manage.py runserver 0.0.0.0:$PORT' ..." + echo "WARNING: this is NOT a recommended way to run you application in production!" + echo "Consider using gunicorn or some other production web server." + maybe_run_in_init_wrapper python "$manage_file" runserver 0.0.0.0:$PORT + else + echo "WARNING: seems that you're using Django, but we could not find a 'manage.py' file." + echo "Skipped 'python manage.py runserver'." + fi +fi + +>&2 echo "ERROR: don't know how to run your application." +>&2 echo "Please set either APP_MODULE, APP_FILE or APP_SCRIPT environment variables, or create a file 'app.py' to launch your application." +exit 1 diff --git a/3.12-minimal/s2i/bin/usage b/3.12-minimal/s2i/bin/usage new file mode 100755 index 00000000..5261cd04 --- /dev/null +++ b/3.12-minimal/s2i/bin/usage @@ -0,0 +1,18 @@ +#!/bin/sh + +DISTRO=`cat /etc/*-release | grep ^ID= | grep -Po '".*?"' | tr -d '"'` +NAMESPACE=centos +[[ $DISTRO =~ rhel* ]] && NAMESPACE=rhscl + +cat < -- curl 127.0.0.1:8080 +EOF diff --git a/3.12-minimal/test/app-home-test-app b/3.12-minimal/test/app-home-test-app new file mode 120000 index 00000000..f6b42e77 --- /dev/null +++ b/3.12-minimal/test/app-home-test-app @@ -0,0 +1 @@ +../../examples/app-home-test-app \ No newline at end of file diff --git a/3.12-minimal/test/app-module-test-app b/3.12-minimal/test/app-module-test-app new file mode 120000 index 00000000..fa20034b --- /dev/null +++ b/3.12-minimal/test/app-module-test-app @@ -0,0 +1 @@ +../../examples/app-module-test-app \ No newline at end of file diff --git a/3.12-minimal/test/check_imagestreams.py b/3.12-minimal/test/check_imagestreams.py new file mode 120000 index 00000000..56bb2be7 --- /dev/null +++ b/3.12-minimal/test/check_imagestreams.py @@ -0,0 +1 @@ +../../common/check_imagestreams.py \ No newline at end of file diff --git a/3.12-minimal/test/django-different-port-test-app b/3.12-minimal/test/django-different-port-test-app new file mode 120000 index 00000000..cc115af1 --- /dev/null +++ b/3.12-minimal/test/django-different-port-test-app @@ -0,0 +1 @@ +../../examples/django-different-port-test-app \ No newline at end of file diff --git a/3.12-minimal/test/django-test-app b/3.12-minimal/test/django-test-app new file mode 120000 index 00000000..52cebf3b --- /dev/null +++ b/3.12-minimal/test/django-test-app @@ -0,0 +1 @@ +../../examples/django-test-app \ No newline at end of file diff --git a/3.12-minimal/test/from-dockerfile/mod_wsgi.Dockerfile.tpl b/3.12-minimal/test/from-dockerfile/mod_wsgi.Dockerfile.tpl new file mode 120000 index 00000000..f5c5b907 --- /dev/null +++ b/3.12-minimal/test/from-dockerfile/mod_wsgi.Dockerfile.tpl @@ -0,0 +1 @@ +../../../src/test/from-dockerfile/mod_wsgi.Dockerfile.tpl \ No newline at end of file diff --git a/3.12-minimal/test/from-dockerfile/uwsgi.Dockerfile.tpl b/3.12-minimal/test/from-dockerfile/uwsgi.Dockerfile.tpl new file mode 100644 index 00000000..53a88357 --- /dev/null +++ b/3.12-minimal/test/from-dockerfile/uwsgi.Dockerfile.tpl @@ -0,0 +1,20 @@ +FROM #IMAGE_NAME# # Replaced by sed in ct_test_app_dockerfile + +ENV UPGRADE_PIP_TO_LATEST=1 + +# Add application sources to a directory that the assemble script expects them +# and set permissions so that the container runs without root access +USER 0 +ADD app-src /tmp/src +RUN /usr/bin/fix-permissions /tmp/src +# Install packages necessary for compiling uwsgi from source +# pkgconfig(python-3.12) is provided by both python3-devel in c9s +# and python3.12-devel in UBI8. +RUN microdnf install -y gcc "pkgconfig(python-3.12)" which +USER 1001 + +# Install the dependencies +RUN /usr/libexec/s2i/assemble + +# Set the default command for the resulting image +CMD /usr/libexec/s2i/run diff --git a/3.12-minimal/test/gunicorn-config-different-port-test-app b/3.12-minimal/test/gunicorn-config-different-port-test-app new file mode 120000 index 00000000..c8eb6171 --- /dev/null +++ b/3.12-minimal/test/gunicorn-config-different-port-test-app @@ -0,0 +1 @@ +../../examples/gunicorn-config-different-port-test-app \ No newline at end of file diff --git a/3.12-minimal/test/gunicorn-different-port-test-app b/3.12-minimal/test/gunicorn-different-port-test-app new file mode 120000 index 00000000..72c8e433 --- /dev/null +++ b/3.12-minimal/test/gunicorn-different-port-test-app @@ -0,0 +1 @@ +../../examples/gunicorn-different-port-test-app \ No newline at end of file diff --git a/3.12-minimal/test/gunicorn-python-configfile-different-port-test-app b/3.12-minimal/test/gunicorn-python-configfile-different-port-test-app new file mode 120000 index 00000000..9139ee17 --- /dev/null +++ b/3.12-minimal/test/gunicorn-python-configfile-different-port-test-app @@ -0,0 +1 @@ +../../examples/gunicorn-python-configfile-different-port-test-app \ No newline at end of file diff --git a/3.12-minimal/test/imagestreams b/3.12-minimal/test/imagestreams new file mode 120000 index 00000000..7a0aee9c --- /dev/null +++ b/3.12-minimal/test/imagestreams @@ -0,0 +1 @@ +../../imagestreams \ No newline at end of file diff --git a/3.12-minimal/test/locale-test-app b/3.12-minimal/test/locale-test-app new file mode 120000 index 00000000..9105ec08 --- /dev/null +++ b/3.12-minimal/test/locale-test-app @@ -0,0 +1 @@ +../../examples/locale-test-app \ No newline at end of file diff --git a/3.12-minimal/test/micropipenv-requirements-test-app b/3.12-minimal/test/micropipenv-requirements-test-app new file mode 120000 index 00000000..cb2ddcf0 --- /dev/null +++ b/3.12-minimal/test/micropipenv-requirements-test-app @@ -0,0 +1 @@ +../../examples/micropipenv-requirements-test-app \ No newline at end of file diff --git a/3.12-minimal/test/micropipenv-test-app/.gitignore b/3.12-minimal/test/micropipenv-test-app/.gitignore new file mode 100644 index 00000000..ba746605 --- /dev/null +++ b/3.12-minimal/test/micropipenv-test-app/.gitignore @@ -0,0 +1,57 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/3.12-minimal/test/micropipenv-test-app/.s2i/environment b/3.12-minimal/test/micropipenv-test-app/.s2i/environment new file mode 100644 index 00000000..29efed4a --- /dev/null +++ b/3.12-minimal/test/micropipenv-test-app/.s2i/environment @@ -0,0 +1,5 @@ +ENABLE_MICROPIPENV=true +DISABLE_SETUP_PY_PROCESSING=true +# This tests second try to install micropipenv with --isolated +# because the first one won't work with following setting +PIP_INDEX_URL=https://example.com/ diff --git a/3.12-minimal/test/micropipenv-test-app/Pipfile b/3.12-minimal/test/micropipenv-test-app/Pipfile new file mode 100644 index 00000000..148759cb --- /dev/null +++ b/3.12-minimal/test/micropipenv-test-app/Pipfile @@ -0,0 +1,15 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +"e1839a8" = {path = ".", editable = true} +requests = "==2.28.2" +setuptools = {version = "==67.7.2", markers="python_version >= '3.12'"} + +[dev-packages] +pytest = ">=2.8.0" + +[requires] +python_version = "3.12" diff --git a/3.12-minimal/test/micropipenv-test-app/Pipfile.lock b/3.12-minimal/test/micropipenv-test-app/Pipfile.lock new file mode 100644 index 00000000..1d3f18e5 --- /dev/null +++ b/3.12-minimal/test/micropipenv-test-app/Pipfile.lock @@ -0,0 +1,211 @@ +{ + "_meta": { + "hash": { + "sha256": "08628d8adcf5d297b100b9dbb14807af951f225a705b902e39726cbbbaf91616" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.12" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.11.17" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, + "gunicorn": { + "hashes": [ + "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", + "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" + ], + "markers": "python_version >= '3.5'", + "version": "==21.2.0" + }, + "idna": { + "hashes": [ + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "markers": "python_version >= '3.5'", + "version": "==3.4" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "requests": { + "hashes": [ + "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", + "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + ], + "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.2" + }, + "setuptools": { + "hashes": [ + "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", + "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + ], + "markers": "python_version >= '3.12'", + "version": "==67.7.2" + }, + "testapp": { + "version": "==0.1" + }, + "urllib3": { + "hashes": [ + "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", + "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.26.18" + } + }, + "develop": { + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "pluggy": { + "hashes": [ + "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", + "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + ], + "markers": "python_version >= '3.8'", + "version": "==1.3.0" + }, + "pytest": { + "hashes": [ + "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", + "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==7.4.3" + } + } +} diff --git a/3.12-minimal/test/micropipenv-test-app/setup.py b/3.12-minimal/test/micropipenv-test-app/setup.py new file mode 100644 index 00000000..e3134094 --- /dev/null +++ b/3.12-minimal/test/micropipenv-test-app/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + + +setup ( + name = "testapp", + version = "0.1", + description = "Example application to be deployed.", + packages = find_packages(), + install_requires = ["gunicorn"], +) diff --git a/3.12-minimal/test/micropipenv-test-app/testapp.py b/3.12-minimal/test/micropipenv-test-app/testapp.py new file mode 100644 index 00000000..fe200352 --- /dev/null +++ b/3.12-minimal/test/micropipenv-test-app/testapp.py @@ -0,0 +1,12 @@ +import sys +import requests + + +def application(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/plain')]) + # Python 3.12 needed requests version bump + if sys.version_info.major == 3 and sys.version_info.minor >= 12: + assert requests.__version__ == '2.28.2' + else: + assert requests.__version__ == '2.20.0' + return [b"Hello from gunicorn WSGI application!"] diff --git a/3.12-minimal/test/numpy-test-app b/3.12-minimal/test/numpy-test-app new file mode 120000 index 00000000..b064f87f --- /dev/null +++ b/3.12-minimal/test/numpy-test-app @@ -0,0 +1 @@ +../../examples/numpy-test-app \ No newline at end of file diff --git a/3.12-minimal/test/pipenv-and-micropipenv-should-fail-test-app b/3.12-minimal/test/pipenv-and-micropipenv-should-fail-test-app new file mode 120000 index 00000000..cf830645 --- /dev/null +++ b/3.12-minimal/test/pipenv-and-micropipenv-should-fail-test-app @@ -0,0 +1 @@ +../../src/test/pipenv-and-micropipenv-should-fail-test-app \ No newline at end of file diff --git a/3.12-minimal/test/pipenv-test-app/.gitignore b/3.12-minimal/test/pipenv-test-app/.gitignore new file mode 100644 index 00000000..ba746605 --- /dev/null +++ b/3.12-minimal/test/pipenv-test-app/.gitignore @@ -0,0 +1,57 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/3.12-minimal/test/pipenv-test-app/.s2i/environment b/3.12-minimal/test/pipenv-test-app/.s2i/environment new file mode 100644 index 00000000..4a4f7605 --- /dev/null +++ b/3.12-minimal/test/pipenv-test-app/.s2i/environment @@ -0,0 +1,2 @@ +ENABLE_PIPENV=true +DISABLE_SETUP_PY_PROCESSING=true diff --git a/3.12-minimal/test/pipenv-test-app/Pipfile b/3.12-minimal/test/pipenv-test-app/Pipfile new file mode 100644 index 00000000..148759cb --- /dev/null +++ b/3.12-minimal/test/pipenv-test-app/Pipfile @@ -0,0 +1,15 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +"e1839a8" = {path = ".", editable = true} +requests = "==2.28.2" +setuptools = {version = "==67.7.2", markers="python_version >= '3.12'"} + +[dev-packages] +pytest = ">=2.8.0" + +[requires] +python_version = "3.12" diff --git a/3.12-minimal/test/pipenv-test-app/Pipfile.lock b/3.12-minimal/test/pipenv-test-app/Pipfile.lock new file mode 100644 index 00000000..1d3f18e5 --- /dev/null +++ b/3.12-minimal/test/pipenv-test-app/Pipfile.lock @@ -0,0 +1,211 @@ +{ + "_meta": { + "hash": { + "sha256": "08628d8adcf5d297b100b9dbb14807af951f225a705b902e39726cbbbaf91616" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.12" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.11.17" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, + "gunicorn": { + "hashes": [ + "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", + "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" + ], + "markers": "python_version >= '3.5'", + "version": "==21.2.0" + }, + "idna": { + "hashes": [ + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "markers": "python_version >= '3.5'", + "version": "==3.4" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "requests": { + "hashes": [ + "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", + "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + ], + "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.2" + }, + "setuptools": { + "hashes": [ + "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", + "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + ], + "markers": "python_version >= '3.12'", + "version": "==67.7.2" + }, + "testapp": { + "version": "==0.1" + }, + "urllib3": { + "hashes": [ + "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", + "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.26.18" + } + }, + "develop": { + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "pluggy": { + "hashes": [ + "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", + "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + ], + "markers": "python_version >= '3.8'", + "version": "==1.3.0" + }, + "pytest": { + "hashes": [ + "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", + "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==7.4.3" + } + } +} diff --git a/3.12-minimal/test/pipenv-test-app/setup.py b/3.12-minimal/test/pipenv-test-app/setup.py new file mode 100644 index 00000000..e3134094 --- /dev/null +++ b/3.12-minimal/test/pipenv-test-app/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + + +setup ( + name = "testapp", + version = "0.1", + description = "Example application to be deployed.", + packages = find_packages(), + install_requires = ["gunicorn"], +) diff --git a/3.12-minimal/test/pipenv-test-app/testapp.py b/3.12-minimal/test/pipenv-test-app/testapp.py new file mode 100644 index 00000000..fe200352 --- /dev/null +++ b/3.12-minimal/test/pipenv-test-app/testapp.py @@ -0,0 +1,12 @@ +import sys +import requests + + +def application(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/plain')]) + # Python 3.12 needed requests version bump + if sys.version_info.major == 3 and sys.version_info.minor >= 12: + assert requests.__version__ == '2.28.2' + else: + assert requests.__version__ == '2.20.0' + return [b"Hello from gunicorn WSGI application!"] diff --git a/3.12-minimal/test/poetry-src-layout-test-app b/3.12-minimal/test/poetry-src-layout-test-app new file mode 120000 index 00000000..92d2990d --- /dev/null +++ b/3.12-minimal/test/poetry-src-layout-test-app @@ -0,0 +1 @@ +../../examples/poetry-src-layout-test-app \ No newline at end of file diff --git a/3.12-minimal/test/pyuwsgi-pipenv-test-app/.s2i/environment b/3.12-minimal/test/pyuwsgi-pipenv-test-app/.s2i/environment new file mode 100644 index 00000000..160e51c4 --- /dev/null +++ b/3.12-minimal/test/pyuwsgi-pipenv-test-app/.s2i/environment @@ -0,0 +1,2 @@ +ENABLE_PIPENV=1 +PIN_PIPENV_VERSION=2023.11.14 diff --git a/3.12-minimal/test/pyuwsgi-pipenv-test-app/app.sh b/3.12-minimal/test/pyuwsgi-pipenv-test-app/app.sh new file mode 100755 index 00000000..3a2680f2 --- /dev/null +++ b/3.12-minimal/test/pyuwsgi-pipenv-test-app/app.sh @@ -0,0 +1,23 @@ +#!/bin/bash +source .s2i/environment + +echo "Testing PIN_PIPENV_VERSION (set in .s2i/environment) ..." +pipenv_version=`pipenv --version` +expected="pipenv, version $PIN_PIPENV_VERSION" +if [ "$pipenv_version" != "$expected" ]; then + echo "ERROR: pipenv version is different than expected." + echo "Expected: ${expected}" + echo "Actual: ${pipenv_version}" + exit 1 +fi + +# Test the uwsgi server +exec uwsgi \ + --http-socket :8080 \ + --die-on-term \ + --master \ + --single-interpreter \ + --enable-threads \ + --threads=5 \ + --thunder-lock \ + --module wsgi diff --git a/3.12-minimal/test/pyuwsgi-pipenv-test-app/requirements.txt b/3.12-minimal/test/pyuwsgi-pipenv-test-app/requirements.txt new file mode 100644 index 00000000..932ba1d6 --- /dev/null +++ b/3.12-minimal/test/pyuwsgi-pipenv-test-app/requirements.txt @@ -0,0 +1,2 @@ +pyuwsgi # this is just a copy of uwsgi project but it has wheels on PyPI +Flask diff --git a/3.12-minimal/test/pyuwsgi-pipenv-test-app/wsgi.py b/3.12-minimal/test/pyuwsgi-pipenv-test-app/wsgi.py new file mode 100644 index 00000000..96e1a614 --- /dev/null +++ b/3.12-minimal/test/pyuwsgi-pipenv-test-app/wsgi.py @@ -0,0 +1,9 @@ +from flask import Flask +application = Flask(__name__) + +@application.route('/') +def hello(): + return b'Hello World from uWSGI hosted WSGI application!' + +if __name__ == '__main__': + application.run() diff --git a/3.12-minimal/test/run b/3.12-minimal/test/run new file mode 100755 index 00000000..7b28885e --- /dev/null +++ b/3.12-minimal/test/run @@ -0,0 +1,323 @@ +#!/bin/bash +# +# The 'run' performs a simple test that verifies that S2I image. +# The main focus here is to excersise the S2I scripts. +# +# IMAGE_NAME specifies a name of the candidate image used for testing. +# The image has to be available before this script is executed. +# +declare -a COMMON_WEB_APPS=({gunicorn-config-different-port,gunicorn-different-port,django-different-port,standalone,setup,setup-requirements,django,numpy,app-home,locale,pipenv,pipenv-and-micropipenv-should-fail,app-module,pyuwsgi-pipenv,micropipenv,standalone-custom-pypi-index,gunicorn-python-configfile-different-port}-test-app) +declare -a FULL_WEB_APPS=({setup-cfg,npm-virtualenv-uwsgi,mod-wsgi,pin-pipenv-version,micropipenv-requirements,poetry-src-layout}-test-app) +declare -a MINIMAL_WEB_APPS=() +declare -a WEB_APPS=(${COMMON_WEB_APPS[@]} ${MINIMAL_WEB_APPS[@]}) + +# Some tests, like the one using the latest pipenv, might be unstable +# because new upstream releases tend to break our tests sometimes. +# If a test is in UNSTABLE_TESTS and IGNORE_UNSTABLE_TESTS env +# variable is defined, a result of the test has no impact on +# the overall result of the test suite. +# +# Reasons for specific tests to be marked as unstable: +# pipenv-test-app: +# - this testcase installs pipenv from the internet. +# Problem is that the upstream releases are from time-to-time broken +# which breaks this test. We generally want to know about it +# in upstream, but ignore this in downstream. +declare -a UNSTABLE_TESTS=(pipenv-test-app) + +# TODO: Make command compatible for Mac users +test_dir="$(readlink -f $(dirname "${BASH_SOURCE[0]}"))" +image_dir=$(readlink -f ${test_dir}/..) + +TEST_LIST="\ +test_s2i_usage +test_docker_run_usage +test_application +test_application_with_user +test_application_enable_init_wrapper +" + +TEST_VAR_DOCKER="\ +test_scl_variables_in_dockerfile +test_from_dockerfile_minimal +" + +if [[ -z $VERSION ]]; then + echo "ERROR: The VERSION variable must be set." + ct_check_testcase_result 1 + exit 1 +fi + +IMAGE_NAME=${IMAGE_NAME:-centos/python-${VERSION//./}-centos7} + +. test/test-lib.sh + +info() { + echo -e "\n\e[1m[INFO] $@\e[0m\n" +} + +image_exists() { + docker inspect $1 &>/dev/null +} + +container_exists() { + image_exists $(cat $cid_file) +} + + +container_ip() { + docker inspect --format="{{ .NetworkSettings.IPAddress }}" $(cat $cid_file) +} + +run_s2i_build() { + info "Building the ${1} application image ..." + ct_s2i_build_as_df file://${test_dir}/${1} ${IMAGE_NAME} ${IMAGE_NAME}-testapp ${s2i_args} +} + +prepare() { + if ! image_exists ${IMAGE_NAME}; then + echo "ERROR: The image ${IMAGE_NAME} must exist before this script is executed." + return 1 + fi + # TODO: S2I build require the application is a valid 'GIT' repository, we + # should remove this restriction in the future when a file:// is used. + info "Preparing to test ${1} ..." + pushd ${test_dir}/${1} >/dev/null + git init + git config user.email "build@localhost" && git config user.name "builder" + git add -A && git commit -m "Sample commit" + popd >/dev/null +} + +run_test_application() { + docker run --user=100001 ${CONTAINER_ARGS} --rm --cidfile=${cid_file} ${IMAGE_NAME}-testapp +} + +cleanup() { + info "Cleaning up the test application image" + if image_exists ${IMAGE_NAME}-testapp; then + docker rmi -f ${IMAGE_NAME}-testapp + fi + rm -rf ${test_dir}/${1}/.git +} +wait_for_cid() { + local max_attempts=10 + local sleep_time=1 + local attempt=1 + info "Waiting for application container to start $CONTAINER_ARGS ..." + while [ $attempt -le $max_attempts ]; do + [ -f $cid_file ] && [ -s $cid_file ] && return 0 + attempt=$(( $attempt + 1 )) + sleep $sleep_time + done + return 1 +} + +test_s2i_usage() { + info "Testing 's2i usage' ..." + ct_s2i_usage ${IMAGE_NAME} ${s2i_args} 1>/dev/null +} + +test_docker_run_usage() { + info "Testing 'docker run' usage ..." + docker run --rm ${IMAGE_NAME} &>/dev/null +} + +test_scl_usage() { + local run_cmd="$1" + local expected="$2" + local cid_file="$3" + + info "Testing the image SCL enable" + out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi + out=$(docker exec $(cat ${cid_file}) /bin/bash -c "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi + out=$(docker exec $(cat ${cid_file}) /bin/sh -ic "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi +} + +test_connection() { + info "Testing the HTTP connection (http://$(container_ip):${test_port}) ${CONTAINER_ARGS} ..." + local max_attempts=30 + local sleep_time=1 + local attempt=1 + local result=1 + while [ $attempt -le $max_attempts ]; do + response_code=$(curl -s -w %{http_code} -o /dev/null http://$(container_ip):${test_port}/) + status=$? + if [ $status -eq 0 ]; then + if [ $response_code -eq 200 ]; then + result=0 + fi + break + fi + attempt=$(( $attempt + 1 )) + sleep $sleep_time + done + return $result +} + +test_application() { + local cid_file="$CID_FILE_DIR"/"$(mktemp -u -p . --suffix .cid)" + # Verify that the HTTP connection can be established to test application container + run_test_application & + + # Wait for the container to write it's CID file + wait_for_cid + # Some test apps have tests in their startup code so we have to check + # that the container starts at all + ct_check_testcase_result $? + + # Instead of relying on VERSION variable coming from Makefile + # set the expected string based on the PYTHON_VERSION defined + # inside the running container. + python_version=$(docker run --rm $IMAGE_NAME /bin/bash -c "echo \$PYTHON_VERSION" 2>&1) + + test_scl_usage "python --version" "Python $python_version." "${cid_file}" + ct_check_testcase_result $? + test_connection + ct_check_testcase_result $? + container_exists && docker stop $(cat "$cid_file") +} + +test_from_dockerfile(){ + info "Test from Dockerfile" + django_example_repo_url="https://github.com/sclorg/django-ex.git@2.2.x" + + ct_test_app_dockerfile $test_dir/from-dockerfile/Dockerfile.tpl $django_example_repo_url 'Welcome to your Django application on OpenShift' app-src + ct_check_testcase_result $? + + info "Test from Dockerfile with no s2i scripts used" + ct_test_app_dockerfile $test_dir/from-dockerfile/Dockerfile_no_s2i.tpl $django_example_repo_url 'Welcome to your Django application on OpenShift' app-src + ct_check_testcase_result $? +} + +test_from_dockerfile_minimal(){ + info "Test from Dockerfile" + + # The following tests are for multi-stage builds. These technically also work on full images, but there is no reason to do multi-stage builds with full images. + + # uwsgi in uwsgi-test-app + ct_test_app_dockerfile $test_dir/from-dockerfile/uwsgi.Dockerfile.tpl $test_dir/uwsgi-test-app 'Hello World from uWSGI hosted WSGI application!' app-src + ct_check_testcase_result $? + + # So far, for all the minimal images, the name of the full container image counterpart + # is the same just without -minimal infix. + # sclorg/python-39-minimal-c9s / sclorg/python-39-c9s + # ubi8/python-39-minimal / ubi8/python-39 + FULL_IMAGE_NAME=${IMAGE_NAME/-minimal/} + + if ct_pull_image "$FULL_IMAGE_NAME"; then + # mod_wsgi in micropipenv-requirements-test-app + sed "s@#IMAGE_NAME#@${IMAGE_NAME}@;s@#FULL_IMAGE_NAME#@${FULL_IMAGE_NAME}@" $test_dir/from-dockerfile/mod_wsgi.Dockerfile.tpl > $test_dir/from-dockerfile/Dockerfile + ct_test_app_dockerfile $test_dir/from-dockerfile/Dockerfile $test_dir/micropipenv-requirements-test-app 'Hello World from mod_wsgi hosted WSGI application!' app-src + ct_check_testcase_result $? + else + echo "[SKIP] Multistage build from Dockerfile - $FULL_IMAGE_NAME does not exists." + fi + +} + +test_application_with_user() { + # test application with random user + CONTAINER_ARGS="--user 12345" test_application + +} + +test_application_enable_init_wrapper() { + # test application with init wrapper + CONTAINER_ARGS="-e ENABLE_INIT_WRAPPER=true" test_application +} + +test_scl_variables_in_dockerfile() { + if [ "$OS" == "rhel7" ] || [ "$OS" == "centos7" ]; then + TESTCASE_RESULT=0 + CID_FILE_DIR=$(mktemp -d) + + info "Testing variable presence during \`docker exec\`" + ct_check_exec_env_vars + ct_check_testcase_result $? + + info "Checking if all scl variables are defined in Dockerfile" + ct_check_scl_enable_vars + ct_check_testcase_result $? + fi +} + +# Positive test & non-zero exit status = ERROR. +# Negative test & zero exit status = ERROR. +# Tests with '-should-fail-' in their name should fail during a build, +# expecting non-zero exit status. +evaluate_build_result() { + local _result="$1" + local _app="$2" + local _type="positive" + local _test_msg="[PASSED]" + local _ret_code=0 + + if [[ "$_app" == *"-should-fail-"* ]]; then + _type="negative" + fi + + if [[ "$_type" == "positive" && "$_result" != "0" ]]; then + info "TEST FAILED (${_type}), EXPECTED:0 GOT:${_result}" + _ret_code=$_result + elif [[ "$_type" == "negative" && "$_result" == "0" ]]; then + info "TEST FAILED (${_type}), EXPECTED: non-zero GOT:${_result}" + _ret_code=1 + fi + if [ $_ret_code != 0 ]; then + cleanup + TESTSUITE_RESULT=1 + _test_msg="[FAILED]" + fi + ct_update_test_result "$_test_msg" "$_app" run_s2i_build + + if [[ "$_type" == "negative" && "$_result" != "0" ]]; then + _ret_code=127 # even though this is success, the app is still not built + fi + return $_ret_code +} + +ct_init + +# For debugging purposes, this script can be run with one or more arguments +# those arguments list is a sub-set of values in the WEB_APPS array defined above +# Example: ./run app-home-test-app pipenv-test-app +for app in ${@:-${WEB_APPS[@]}}; do + # Since we built the candidate image locally, we don't want S2I attempt to pull + # it from Docker hub + s2i_args="--pull-policy=never" + + # Example apps with "-different-port-" in their name don't use the default port 8080 + if [[ "$app" == *"-different-port-"* ]]; then + test_port=8085 + else + test_port=8080 + fi + + prepare ${app} + if [ $? -ne 0 ]; then + ct_update_test_result "[FAILED]" "${app}" "preparation" + TESTSUITE_RESULT=1 + continue + fi + run_s2i_build ${app} + evaluate_build_result $? "$app" || continue + + TEST_SET=${TESTS:-$TEST_LIST} ct_run_tests_from_testset "${app}" + + cleanup ${app} +done + +TEST_SET=${TESTS:-$TEST_VAR_DOCKER} ct_run_tests_from_testset "var-docker" diff --git a/3.12-minimal/test/run-openshift-remote-cluster b/3.12-minimal/test/run-openshift-remote-cluster new file mode 120000 index 00000000..1bffcba8 --- /dev/null +++ b/3.12-minimal/test/run-openshift-remote-cluster @@ -0,0 +1 @@ +../../test/run-openshift-remote-cluster \ No newline at end of file diff --git a/3.12-minimal/test/setup-cfg-test-app b/3.12-minimal/test/setup-cfg-test-app new file mode 120000 index 00000000..71a2b0cb --- /dev/null +++ b/3.12-minimal/test/setup-cfg-test-app @@ -0,0 +1 @@ +../../examples/setup-cfg-test-app \ No newline at end of file diff --git a/3.12-minimal/test/setup-requirements-test-app b/3.12-minimal/test/setup-requirements-test-app new file mode 120000 index 00000000..c909ccb9 --- /dev/null +++ b/3.12-minimal/test/setup-requirements-test-app @@ -0,0 +1 @@ +../../examples/setup-requirements-test-app \ No newline at end of file diff --git a/3.12-minimal/test/setup-test-app b/3.12-minimal/test/setup-test-app new file mode 120000 index 00000000..1b49f26f --- /dev/null +++ b/3.12-minimal/test/setup-test-app @@ -0,0 +1 @@ +../../examples/setup-test-app \ No newline at end of file diff --git a/3.12-minimal/test/standalone-custom-pypi-index-test-app b/3.12-minimal/test/standalone-custom-pypi-index-test-app new file mode 120000 index 00000000..12941a9a --- /dev/null +++ b/3.12-minimal/test/standalone-custom-pypi-index-test-app @@ -0,0 +1 @@ +../../examples/standalone-custom-pypi-index-test-app \ No newline at end of file diff --git a/3.12-minimal/test/standalone-test-app b/3.12-minimal/test/standalone-test-app new file mode 120000 index 00000000..3f158948 --- /dev/null +++ b/3.12-minimal/test/standalone-test-app @@ -0,0 +1 @@ +../../examples/standalone-test-app \ No newline at end of file diff --git a/3.12-minimal/test/test-lib-openshift.sh b/3.12-minimal/test/test-lib-openshift.sh new file mode 120000 index 00000000..4f9f2996 --- /dev/null +++ b/3.12-minimal/test/test-lib-openshift.sh @@ -0,0 +1 @@ +../../common/test-lib-openshift.sh \ No newline at end of file diff --git a/3.12-minimal/test/test-lib-python.sh b/3.12-minimal/test/test-lib-python.sh new file mode 120000 index 00000000..0fa72132 --- /dev/null +++ b/3.12-minimal/test/test-lib-python.sh @@ -0,0 +1 @@ +../../test/test-lib-python.sh \ No newline at end of file diff --git a/3.12-minimal/test/test-lib-remote-openshift.sh b/3.12-minimal/test/test-lib-remote-openshift.sh new file mode 120000 index 00000000..92ad2f4d --- /dev/null +++ b/3.12-minimal/test/test-lib-remote-openshift.sh @@ -0,0 +1 @@ +../../common/test-lib-remote-openshift.sh \ No newline at end of file diff --git a/3.12-minimal/test/test-lib.sh b/3.12-minimal/test/test-lib.sh new file mode 120000 index 00000000..1ac99b93 --- /dev/null +++ b/3.12-minimal/test/test-lib.sh @@ -0,0 +1 @@ +../../common/test-lib.sh \ No newline at end of file diff --git a/3.12-minimal/test/test-openshift.yaml b/3.12-minimal/test/test-openshift.yaml new file mode 120000 index 00000000..8613fbba --- /dev/null +++ b/3.12-minimal/test/test-openshift.yaml @@ -0,0 +1 @@ +../../common/test-openshift.yaml \ No newline at end of file diff --git a/3.12-minimal/test/uwsgi-test-app b/3.12-minimal/test/uwsgi-test-app new file mode 120000 index 00000000..c3ccf241 --- /dev/null +++ b/3.12-minimal/test/uwsgi-test-app @@ -0,0 +1 @@ +../../examples/uwsgi-test-app \ No newline at end of file