Compare commits

...
Sign in to create a new pull request.

15 commits

Author SHA1 Message Date
phracek
5513184556 Merge pull request #465 from phracek/more_outputs_correct_return_states
Add support for OpenShift 3
2021-06-30 11:16:59 +00:00
phracek
d246020731 Distgen generated content 2021-05-25 17:42:58 +00:00
Lumir Balhar
fdf8f9b035 Update from the upstream Github repository 2021-04-07 08:44:42 +02:00
Lumir Balhar
896a5dcee2 Update from the upstream Github repository 2020-11-30 08:55:39 +01:00
Lumir Balhar
61373b3322 Switch to Fedora 33 and Python 3.9 2020-10-12 11:54:07 +02:00
Lumir Balhar
7d867c91ad Update from upstream Github repository 2020-07-16 07:26:18 +02:00
Lumir Balhar
d86a7a9c0e Remove temporary added release label and let osbs handle it 2020-06-02 10:31:07 +02:00
Lumir Balhar
8fb112e5e4 Increase release to resolve conflict with an existing build 2020-06-02 10:10:00 +02:00
Lumir Balhar
72f4f216bb Temporary change to fix for release/disttag combination 2020-06-02 09:25:39 +02:00
Lumir Balhar
b0910418b6 Update from upstream github repo
- pipenv is locked to older stable version
- ENV for micropipenv
- no python-virtualenv package
- updated documentation
2020-06-02 09:21:09 +02:00
Petr "Stone" Hracek
584be66e16 This commit adds support for syncing upstream repository to Fedora land
Signed-off-by: Petr "Stone" Hracek <phracek@redhat.com>
2020-04-27 14:49:19 +02:00
Lumir Balhar
7bea193d56 Use python -m venv instead of virtualenv command
https://github.com/sclorg/s2i-python-container/pull/374
2020-04-03 13:18:18 +02:00
Lumir Balhar
4fb1f66d7a New image Fedora 32 + Python 3.8 (synced from upstream) 2020-03-30 14:31:59 +02:00
Lumir Balhar
1425f4c95f The latest fixes and enhancenments from upstream 2020-02-05 13:31:25 +01:00
Lumir Balhar
c1cc83f38d Blind update from upstream Github - Fedora 31, Python 3.7 2020-01-07 10:11:58 +01:00
82 changed files with 4843 additions and 215 deletions

1
3.9 Symbolic link
View file

@ -0,0 +1 @@
.

View file

@ -1,10 +1,10 @@
# This image provides a Python 3.5 environment you can use to run your Python
# This image provides a Python 3.9 environment you can use to run your Python
# applications.
FROM registry.fedoraproject.org/f30/s2i-base:latest
FROM registry.fedoraproject.org/f33/s2i-base:latest
EXPOSE 8080
ENV PYTHON_VERSION=3.6 \
ENV PYTHON_VERSION=3.9 \
PATH=$HOME/.local/bin/:$PATH \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=UTF-8 \
@ -12,12 +12,13 @@ ENV PYTHON_VERSION=3.6 \
LANG=en_US.UTF-8 \
PIP_NO_CACHE_DIR=off
ENV NAME=python3 \
VERSION=0 \
ARCH=x86_64
ENV SUMMARY="Platform for building and running Python $PYTHON_VERSION applications" \
DESCRIPTION="Python $PYTHON_VERSION available as docker container is a base platform for \
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. \
@ -28,37 +29,60 @@ on most platforms."
LABEL summary="$SUMMARY" \
description="$DESCRIPTION" \
io.k8s.description="$DESCRIPTION" \
io.k8s.display-name="Python 3.6" \
io.k8s.display-name="Python 3.9" \
io.openshift.expose-services="8080:http" \
io.openshift.tags="builder,python,python36,rh-python36" \
io.openshift.tags="builder,python,python39,python-39,rh-python39" \
com.redhat.component="$NAME" \
name="$FGC/$NAME" \
version="$VERSION" \
architecture="$ARCH" \
usage="s2i build file:///your/app 26/python3 your-app" \
usage="s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.9/test/setup-test-app/ $FGC/$NAME python-sample-app" \
maintainer="SoftwareCollections.org <sclorg@redhat.com>"
RUN INSTALL_PKGS="python3 python3-devel python3-setuptools python3-pip python3-virtualenv \
nss_wrapper httpd httpd-devel atlas-devel gcc-gfortran libffi-devel libtool-ltdl" && \
dnf install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \
RUN INSTALL_PKGS="python3 python3-devel python3-setuptools python3-pip nss_wrapper \
httpd httpd-devel atlas-devel gcc-gfortran libffi-devel \
libtool-ltdl enchant redhat-rpm-config" && \
dnf -y --setopt=tsflags=nodocs install $INSTALL_PKGS && \
rpm -V $INSTALL_PKGS && \
dnf clean all -y
dnf -y clean all --enablerepo='*'
# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH.
COPY ./s2i/bin/ $STI_SCRIPTS_PATH
COPY 3.9/s2i/bin/ $STI_SCRIPTS_PATH
# Copy extra files to the image.
COPY ./root/ /
COPY 3.9/root/ /
# Create a Python virtual environment for use by any application to avoid
# potential conflicts with Python packages preinstalled in the main Python
# installation.
RUN virtualenv-$PYTHON_VERSION /opt/app-root
# Python 3 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.9/root/opt/wheels /opt/wheels
# - 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.9 -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 (in the macro) 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
# 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 chown -R 1001:0 /opt/app-root && chmod -R og+rwx /opt/app-root
# For Fedora scl_enable isn't sourced automatically in s2i-core
# so virtualenv needs to be activated this way
ENV BASH_ENV="${APP_ROOT}/bin/activate" \
ENV="${APP_ROOT}/bin/activate" \
PROMPT_COMMAND=". ${APP_ROOT}/bin/activate"
USER 1001

1
Dockerfile.fedora Symbolic link
View file

@ -0,0 +1 @@
Dockerfile

276
README.md
View file

@ -1,90 +1,159 @@
Python Docker image
===================
Python 3.9 container image
=========================
This repository contains the source for building various versions of
the Python application as a reproducible Docker image using
[source-to-image](https://github.com/openshift/source-to-image).
This container image includes Python 3.9 as a [S2I](https://github.com/openshift/source-to-image) base image for your Python 3.9 applications.
Users can choose between RHEL and CentOS based builder images.
The resulting image can be run using [Docker](http://docker.io).
The RHEL images are available in the [Red Hat Container Catalog](https://access.redhat.com/containers/),
the CentOS images are available on [Quay.io](https://quay.io/organization/centos7),
and the Fedora images are available in [Fedora Registry](https://registry.fedoraproject.org/).
The resulting image can be run using [podman](https://github.com/containers/libpod) or
[docker](http://docker.io).
Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments
Usage
---------------------
To build a simple [python-sample-app](https://github.com/openshift/s2i-python/tree/master/3.6/test/setup-test-app) application
using standalone [S2I](https://github.com/openshift/source-to-image) and then run the
resulting image with [Docker](http://docker.io) execute:
Description
-----------
Python 3.9 available as container is a base platform for
building and running various Python 3.9 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.
This container image includes an npm utility
(see [base image repository](https://github.com/sclorg/s2i-base-container/tree/master/base)),
so users can use it to install JavaScript
modules for their web applications. There is no guarantee for any specific npm or nodejs
version, that is included in the image; those versions can be changed anytime and
the nodejs itself is included just to make the npm work.
Usage in Openshift
------------------
For this, we will assume that you are using the supported image, available via `python:3.9` imagestream tag in Openshift.
Building a simple [python-sample-app](https://github.com/sclorg/django-ex.git) application
in Openshift can be achieved with the following step:
* **For RHEL based image**
```
$ s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.6/test/setup-test-app/ rhscl/python-36-rhel7 python-sample-app
$ docker run -p 8080:8080 python-sample-app
```
* **For CentOS based image**
```
$ s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.6/test/setup-test-app/ centos/python-36-centos7 python-sample-app
$ docker run -p 8080:8080 python-sample-app
oc new-app python:3.9~https://github.com/sclorg/django-ex.git
```
**Accessing the application:**
```
$ curl 127.0.0.1:8080
$ oc get pods
$ oc exec <pod> -- curl 127.0.0.1:8080
```
Source-to-Image framework and scripts
-------------------------------------
This image supports the [Source-to-Image](https://docs.openshift.com/container-platform/3.11/creating_images/s2i.html)
(S2I) strategy in OpenShift. The Source-to-Image is an OpenShift framework
which makes it easy to write images that take application source code as
an input, use a builder image like this Python container image, and produce
a new image that runs the assembled application as an output.
Repository organization
------------------------
* **`<python-version>`**
To support the Source-to-Image framework, important scripts are included in the builder image:
* **Dockerfile**
* The `/usr/libexec/s2i/assemble` script inside the image is run to produce a new image with the application artifacts.
The script takes sources of a given application and places them into appropriate directories inside the image.
It utilizes some common patterns in Perl application development (see the **Environment variables** section below).
* The `/usr/libexec/s2i/run` script is set as the default command in the resulting container image (the new image with the application artifacts).
It runs your application according to settings in `APP_MODULE`, `APP_FILE` or `APP_SCRIPT` environment variables or it tries to detect the best
way automatically.
CentOS based Dockerfile.
Building an application using a Dockerfile
------------------------------------------
Compared to the Source-to-Image strategy, using a Dockerfile is a more
flexible way to build a Python container image with an application.
Use a Dockerfile when Source-to-Image is not sufficiently flexible for you or
when you build the image outside of the OpenShift environment.
* **Dockerfile.rhel7**
To use the Python image in a Dockerfile, follow these steps:
#### 1. Pull a base builder image to build on
RHEL based Dockerfile. In order to perform build or test actions on this
Dockerfile you need to run the action on a properly subscribed RHEL machine.
```
podman pull registry.access.redhat.com/ubi8/python-39
```
* **`s2i/bin/`**
#### 2. Pull and application code
This folder contains scripts that are run by [S2I](https://github.com/openshift/source-to-image):
An example application available at https://github.com/sclorg/django-ex.git is used here. Feel free to clone the repository for further experiments.
You can also take a look at code examples in s2i-python-container repository: https://github.com/sclorg/s2i-python-container/tree/master/examples
* **assemble**
```
git clone https://github.com/sclorg/django-ex.git app-src
```
Used to install the sources into the location where the application
will be run and prepare the application for deployment (eg. installing
dependencies, etc.)
#### 3. Prepare an application inside a container
* **run**
This step usually consists of at least these parts:
This script is responsible for running the application by using the
application web server.
* putting the application source into the container
* installing the dependencies
* setting the default command in the resulting image
* **usage***
For all these three parts, users can either setup all manually and use commands `python` and `pip` explicitly in the Dockerfile,
or users can use the Source-to-Image scripts inside the image.
This script prints the usage of this image.
The manual way comes with the highest level of flexibility but requires you to know how to work
with modules or software collections manually, how to setup virtual environment with the right version
of Python and many more. On the other hand, using Source-to-Image scripts makes your Dockerfile
prepared for a future flawless switch to a newer or different platform.
* **`contrib/`**
To use the Source-to-Image scripts and build an image using a Dockerfile, create a Dockerfile with this content:
This folder contains a file with commonly used modules.
```
FROM registry.access.redhat.com/ubi8/python-39
* **`test/`**
# 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
This folder contains a [S2I](https://github.com/openshift/source-to-image)
test framework with a simple server.
# Install the dependencies
RUN /usr/libexec/s2i/assemble
* **`setup-test-app/`**
# Set the default command for the resulting image
CMD /usr/libexec/s2i/run
```
Simple Gunicorn application used for testing purposes by the [S2I](https://github.com/openshift/source-to-image) test framework.
If you decide not to use the Source-to-Image scripts, you will need to manually tailor the Dockerfile to your application and its needs.
Example Dockerfile for a simple Django application:
* **`standalone-test-app/`**
```
FROM registry.access.redhat.com/ubi8/python-39
Simple standalone application used for testing purposes by the [S2I](https://github.com/openshift/source-to-image) test framework.
# Add application sources with correct permissions for OpenShift
USER 0
ADD app-src .
RUN chown -R 1001:0 ./
USER 1001
* **run**
# Install the dependencies
RUN pip install -U "pip>=19.3.1" && \
pip install -r requirements.txt && \
python manage.py collectstatic --noinput && \
python manage.py migrate
Script that runs the [S2I](https://github.com/openshift/source-to-image) test framework.
# Run the application
CMD python manage.py runserver 0.0.0.0:8080
```
#### 4. Build a new image from a Dockerfile prepared in the previous step
```
podman build -t python-app .
```
#### 5. Run the resulting image with final application
```
podman run -d python-app
```
Environment variables
---------------------
@ -119,7 +188,7 @@ file inside your source code repository.
If using `setup.py` for installing the application, the `MODULE_NAME` part
can be read from there. For an example, see
[setup-test-app](https://github.com/openshift/s2i-python/tree/master/3.6/test/setup-test-app).
[setup-test-app](https://github.com/sclorg/s2i-python-container/tree/master/3.9/test/setup-test-app).
* **APP_HOME**
@ -134,33 +203,83 @@ file inside your source code repository.
Path to a valid Python file with a
[Gunicorn configuration](http://docs.gunicorn.org/en/latest/configure.html#configuration-file) file.
* **DISABLE_MIGRATE**
Set this variable to a non-empty value to inhibit the execution of 'manage.py migrate'
when the produced image is run. This only affects Django projects. See
"Handling Database Migrations" section of [Django blogpost on OpenShift blog](
https://blog.openshift.com/migrating-django-applications-openshift-3/) on suggestions
how/when to run DB migrations in OpenShift environment. Most importantly,
note that running DB migrations from two or more pods might corrupt your database.
* **DISABLE_COLLECTSTATIC**
Set this variable to a non-empty value to inhibit the execution of
'manage.py collectstatic' during the build. This only affects Django projects.
* **DISABLE_MIGRATE**
* **DISABLE_SETUP_PY_PROCESSING**
Set this variable to a non-empty value to inhibit the execution of 'manage.py migrate'
when the produced image is run. This only affects Django projects.
Set this to a non-empty value to skip processing of setup.py script if you
use `-e .` in requirements.txt to trigger its processing or you don't want
your application to be installed into site-packages directory.
* **ENABLE_PIPENV**
Set this variable to use [Pipenv](https://github.com/pypa/pipenv),
the higher-level Python packaging tool, to manage dependencies of the application.
This should be used only if your project contains properly formated Pipfile
and Pipfile.lock.
* **PIN_PIPENV_VERSION**
Set this variable together with `ENABLE_PIPENV` to use a specific version of Pipenv.
If not set, the latest stable version from PyPI is installed.
For example `PIN_PIPENV_VERSION=2018.11.26` installs `pipenv==2018.11.26`.
* **ENABLE_MICROPIPENV**
Set this variable to use [micropipenv](https://github.com/thoth-station/micropipenv),
a lightweight wrapper for pip to support requirements.txt, Pipenv and Poetry lock
files or converting them to pip-tools compatible output. Designed for containerized Python applications.
Available only for Python 3 images.
* **ENABLE_INIT_WRAPPER**
Set this variable to a non-empty value to make use of an init wrapper.
This is useful for servers that are not capable of reaping zombie
processes, such as Django development server or Tornado. This option can
be used together with **APP_SCRIPT** or **APP_FILE**. It never applies
to Gunicorn used through **APP_MODULE** as Gunicorn reaps zombie
processes correctly.
* **PIP_INDEX_URL**
Set this variable to use a custom index URL or mirror to download required packages
during build process. This only affects packages listed in requirements.txt.
Set this variable to use a custom index URL or mirror to download required
packages during build process. This affects packages listed in
requirements.txt. It also affects the installation of pipenv and
micropipenv and the update of pip in the container, though if not found in
the custom index, the container will try to install/update them from
upstream PyPI afterwards.
* **PORT**
HTTP(S) port your application should listen on. The default is 8080.
`PORT` is used only for Django development server and for Gunicorn
with the default configutation (no `APP_CONFIG` or `GUNICORN_CMD_ARGS` specified).
* **UPGRADE_PIP_TO_LATEST**
Set this variable to a non-empty value to have the 'pip' program be upgraded
to the most recent version before any Python packages are installed. If not
set it will use whatever the default version is included by the platform
for the Python version being used.
Set this variable to a non-empty value to have the 'pip' program and related
python packages (setuptools and wheel) be upgraded to the most recent version
before any Python packages are installed. If not set, the container will use
the stable pip version this container was built with, taken from a recent Fedora release.
* **WEB_CONCURRENCY**
Set this to change the default setting for the number of
[workers](http://docs.gunicorn.org/en/stable/settings.html#workers). By
default, this is set to the number of available cores times 2.
default, this is set to the number of available cores times 2, capped
at 12.
Source repository layout
------------------------
@ -174,18 +293,26 @@ However, if these files exist they will affect the behavior of the build process
[here](https://pip.pypa.io/en/latest/user_guide.html#requirements-files).
* **Pipfile**
The replacement for requirements.txt, project is currently under active
design and development, as documented [here](https://github.com/pypa/pipfile).
Set `ENABLE_PIPENV` environment variable to true in order to process this file.
* **setup.py**
Configures various aspects of the project, including installation of
dependencies, as documented
[here](https://packaging.python.org/en/latest/distributing.html#setup-py).
For most projects, it is sufficient to simply use `requirements.txt`.
For most projects, it is sufficient to simply use `requirements.txt` or
`Pipfile`. Set `DISABLE_SETUP_PY_PROCESSING` environment variable to true
in order to skip processing of this file.
Run strategies
--------------
The Docker image produced by s2i-python executes your project in one of the
The container image produced by s2i-python executes your project in one of the
following ways, in precedence order:
* **Gunicorn**
@ -202,6 +329,9 @@ following ways, in precedence order:
If you have both Django and Gunicorn in your requirements, your Django project
will automatically be served using Gunicorn.
The default setting for Gunicorn (`--bind=0.0.0.0:$PORT --access-logfile=-`) is applied
only if both `$APP_CONFIG` and `$GUNICORN_CMD_ARGS` are not defined.
* **Django development server**
If you have Django in your requirements but don't have Gunicorn, then your
@ -225,7 +355,7 @@ following ways, in precedence order:
application.
Hot deploy
---------------------
----------
If you are using Django, hot deploy will work out of the box.
@ -235,12 +365,20 @@ configuration file inside your repository with the
option set to `true`. Make sure to specify your config via the `APP_CONFIG`
environment variable.
To change your source code in running container, use Docker's
[exec](https://docs.docker.com/reference/commandline/exec/) command:
To change your source code in running container, use podman's (or docker's)
[exec](https://github.com/containers/libpod/blob/master/docs/podman-exec.1.md) command:
```
docker exec -it <CONTAINER_ID> /bin/bash
podman exec -it <CONTAINER_ID> /bin/bash
```
After you enter into the running container, your current directory is set
to `/opt/app-root/src`, where the source code is located.
See also
--------
Dockerfile and other sources are available on https://github.com/sclorg/s2i-python-container.
In that repository you also can find another versions of Python environment Dockerfiles.
Dockerfile for CentOS is called `Dockerfile`, Dockerfile for RHEL7 is called `Dockerfile.rhel7`,
for RHEL8 it's `Dockerfile.rhel8` and the Fedora Dockerfile is called `Dockerfile.fedora`.

23
bot-cfg.yml Normal file
View file

@ -0,0 +1,23 @@
version: "1"
betka:
# is betka enabled for this repository
# optional - defaults to false
enabled: true
# optional
notifications:
email_addresses: [phracek@redhat.com, lbalhar@redhat.com]
# Specify if master branch in upstream repository is synced
master_checker: true
# Should pull requests be synced?
# optional
pr_checker: false
# Either 'upstream_branch_name' or 'upstream_git_path' has to be specified
# Branch name which is used for sync
upstream_branch_name: master
# Path to directory with dockerfile withing upstream repository
upstream_git_path: "3.8"
# Github comment message to enforce sync of a pull request
# required if pr_checker is true otherwise optional
pr_comment_message: "[test]"
# optional
image_url: quay.io/rhscl/cwt-generator

1
help.md Symbolic link
View file

@ -0,0 +1 @@
README.md

View file

@ -2,5 +2,8 @@
# 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
source scl_source enable httpd24 rh-python36
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 rh-python39
fi
source /opt/app-root/bin/activate

Binary file not shown.

View file

@ -8,35 +8,99 @@ 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.9 -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/* ./
mv /tmp/src/* "$HOME"
# set permissions for any installed artifacts
fix-permissions /opt/app-root -P
if [[ ! -z "$UPGRADE_PIP_TO_LATEST" ]]; then
echo "---> Upgrading pip to latest version ..."
pip install -U pip
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 [[ -f requirements.txt ]]; then
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 ]]; then
if [[ -f setup.py && -z "$DISABLE_SETUP_PY_PROCESSING" ]]; then
echo "---> Installing application ..."
python setup.py develop
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"
APP_HOME=${APP_HOME:-.}
# Look for 'manage.py' in the directory specified by APP_HOME, or the current directory
manage_file=$APP_HOME/manage.py
# 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."
@ -56,4 +120,4 @@ if should_collectstatic; then
fi
# set permissions for any installed artifacts
fix-permissions /opt/app-root
fix-permissions /opt/app-root -P

18
s2i/bin/init-wrapper Executable file
View file

@ -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

View file

@ -29,33 +29,59 @@ function get_default_web_concurrency() {
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
}
app_script_check="${APP_SCRIPT-}"
APP_SCRIPT="${APP_SCRIPT-app.sh}"
if [[ -f "$APP_SCRIPT" ]]; then
echo "---> Running application from script ($APP_SCRIPT) ..."
if [[ "$APP_SCRIPT" != /* ]]; then
APP_SCRIPT="./$APP_SCRIPT"
function maybe_run_in_init_wrapper() {
if [[ -z "$ENABLE_INIT_WRAPPER" ]]; then
exec "$@"
else
exec $STI_SCRIPTS_PATH/init-wrapper "$@"
fi
exec "$APP_SCRIPT"
else
test -n "$app_script_check" && (>&2 echo "ERROR: file '$app_script_check' not found.") && exit 1
}
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
app_file_check="${APP_FILE-}"
APP_FILE="${APP_FILE-app.py}"
if [[ -f "$APP_FILE" ]]; then
echo "---> Running application from Python script ($APP_FILE) ..."
exec python "$APP_FILE"
else
test -n "$app_file_check" && (>&2 echo "ERROR: file '$app_file_check' not found.") && exit 1
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
APP_HOME=${APP_HOME:-.}
# Look for 'manage.py' in the directory specified by APP_HOME, or the current direcotry
manage_file=$APP_HOME/manage.py
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
@ -67,31 +93,44 @@ if should_migrate; then
fi
fi
if is_gunicorn_installed; then
if [[ -z "$APP_MODULE" ]]; then
# Look only in the directory specified by APP_HOME, or the current directory
# replace all "/" with ".", remove leading "." and ".py" suffix
APP_MODULE=$(find $APP_HOME -maxdepth 1 -type f -name 'wsgi.py' | sed 's:/:.:g;s:^\.\+::;s:\.py$::')
fi
# If not set, use 8080 as the default port
if [ -z "$PORT" ]; then
PORT=8080
fi
if [[ -z "$APP_MODULE" && -f setup.py ]]; then
APP_MODULE="$(python setup.py --name)"
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)}
echo "---> Serving application with gunicorn ($APP_MODULE) ..."
exec gunicorn "$APP_MODULE" --bind=0.0.0.0:8080 --access-logfile=- --config "$APP_CONFIG"
# 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' ..."
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."
exec python "$manage_file" runserver 0.0.0.0:8080
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'."

View file

@ -5,14 +5,14 @@ NAMESPACE=centos
[[ $DISTRO =~ rhel* ]] && NAMESPACE=rhscl
cat <<EOF
This is a S2I python-3.6 ${DISTRO} base image:
To use it, install S2I: https://github.com/openshift/source-to-image
Sample invocation:
s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.6/test/setup-test-app/ ${NAMESPACE}/python-36-${DISTRO}7 python-sample-app
This is a S2I python-3.9 ${DISTRO} base image:
There are multiple ways how to run the image, see documentation at:
https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md
To use it in Openshift, run:
oc new-app python:3.9~https://github.com/sclorg/s2i-python-container.git --context-dir=3.9/test/setup-test-app/
You can then run the resulting image via:
docker run -p 8080:8080 python-sample-app
oc get pods
oc exec <pod> -- curl 127.0.0.1:8080
EOF

View file

@ -1 +1,2 @@
gunicorn
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

57
test/app-module-test-app/.gitignore vendored Normal file
View file

@ -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/

View file

@ -0,0 +1 @@
APP_MODULE=app:application

View file

@ -0,0 +1,4 @@
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
return [b"Hello from gunicorn WSGI application!"]

View file

@ -0,0 +1,2 @@
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

95
test/check_imagestreams.py Executable file
View file

@ -0,0 +1,95 @@
#!/bin/env python3
# MIT License
#
# Copyright (c) 2018-2019 Red Hat, Inc.
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys
import json
import logging
import os
from pathlib import Path
from typing import Dict
IMAGESTREAMS_DIR: str = "imagestreams"
class ImageStreamChecker(object):
version: str = ""
results: Dict = {}
def __init__(self, version: str):
self.version = version
def load_json_file(self, filename: Path):
with open(str(filename)) as f:
return json.load(f)
def check_version(self, json_dict: Dict):
res = []
for tags in json_dict["spec"]["tags"]:
# The name can be"<stream>" or "<stream>-elX" or "<stream>-ubiX"
if (tags["name"] == self.version or
tags["name"].startswith(self.version + '-')):
res.append(tags)
return res
def check_latest_tag(self, json_dict: Dict):
latest_tag_correct: bool = False
for tags in json_dict["spec"]["tags"]:
if tags["name"] != "latest":
continue
# The latest can link to either "<stream>" or "<stream>-elX" or "<stream>-ubiX"
if tags["from"]["name"] == self.version or tags["from"]["name"].startswith(self.version + '-'):
latest_tag_correct = True
return latest_tag_correct
def check_imagestreams(self):
p = Path(".")
json_files = p.glob(f"{IMAGESTREAMS_DIR}/*.json")
if not json_files:
print(f"No json files present in {IMAGESTREAMS_DIR}.")
return 0
for f in json_files:
if os.environ.get("TARGET") in ("rhel7", "centos7") and "aarch64" in str(f):
print("Imagestream aarch64 is not supported on rhel7")
continue
print(f"Checking file {str(f)}.")
json_dict = self.load_json_file(f)
if not (self.check_version(json_dict) and self.check_latest_tag(json_dict)):
print(f"The latest version is not present in {str(f)} or in latest tag.")
self.results[f] = False
if self.results:
return 1
print("Imagestreams contains the latest version.")
return 0
if __name__ == "__main__":
if len(sys.argv) != 2:
logging.fatal("%s: %s", sys.argv[0], "VERSION as an argument was not provided")
sys.exit(1)
print(f"Version to check is {sys.argv[1]}.")
isc = ImageStreamChecker(version=sys.argv[1])
sys.exit(isc.check_imagestreams())

View file

@ -0,0 +1,61 @@
# Django
db.sqlite3
staticfiles/
# 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/

View file

@ -0,0 +1 @@
PORT=8085

View file

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View file

@ -0,0 +1,104 @@
"""
Django settings for project project.
Generated by 'django-admin startproject' using Django 1.8.1.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'y*b^6p#z&cm2)8rzgbp2i4k*+rg2h%60l*bmf6hg&ro!z0-ael'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# SECURITY WARNING: do not use '*' on production or use some HTTP(S) proxy
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
ROOT_URLCONF = 'project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

View file

@ -0,0 +1,21 @@
"""project URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.8/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Add an import: from blog import urls as blog_urls
2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
"""
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
]

View file

@ -0,0 +1,16 @@
"""
WSGI config for project project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
application = get_wsgi_application()

View file

@ -0,0 +1 @@
Django==1.11.29

View file

@ -25,7 +25,8 @@ SECRET_KEY = 'y*b^6p#z&cm2)8rzgbp2i4k*+rg2h%60l*bmf6hg&ro!z0-ael'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# SECURITY WARNING: do not use '*' on production or use some HTTP(S) proxy
ALLOWED_HOSTS = ['*']
# Application definition

View file

@ -1 +1 @@
Django==1.8.1
Django==1.11.29

View file

@ -0,0 +1,14 @@
FROM #IMAGE_NAME# # Replaced by sed in tests, see test_from_dockerfile in test/run
# 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 dependencies
RUN /usr/libexec/s2i/assemble
# Set the default command for the resulting image
CMD /usr/libexec/s2i/run

View file

@ -0,0 +1,16 @@
FROM #IMAGE_NAME# # Replaced by sed in tests, see test_from_dockerfile in test/run
# Add application sources with correct permissions for OpenShift
USER 0
ADD app-src .
RUN chown -R 1001:0 ./
USER 1001
# Install the dependencies
RUN pip install -U "pip>=19.3.1" && \
pip install -r requirements.txt && \
python manage.py collectstatic --noinput && \
python manage.py migrate
# Run the application
CMD python manage.py runserver 0.0.0.0:8080

View file

@ -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/

View file

@ -0,0 +1,2 @@
APP_MODULE=app:application
APP_CONFIG=./gunicorn.conf.py

View file

@ -0,0 +1,4 @@
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
return [b"Hello from gunicorn WSGI application!"]

View file

@ -0,0 +1,4 @@
import multiprocessing
bind = "0.0.0.0:8085"
workers = multiprocessing.cpu_count() * 2 + 1

View file

@ -0,0 +1,2 @@
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

View file

@ -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/

View file

@ -0,0 +1,2 @@
APP_MODULE=app:application
PORT=8085

View file

@ -0,0 +1,4 @@
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
return [b"Hello from gunicorn WSGI application!"]

View file

@ -0,0 +1,2 @@
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

View file

@ -0,0 +1,173 @@
{
"kind": "ImageStream",
"apiVersion": "v1",
"metadata": {
"name": "python",
"annotations": {
"openshift.io/display-name": "Python"
}
},
"spec": {
"tags": [
{
"name": "latest",
"annotations": {
"openshift.io/display-name": "Python (Latest)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python applications on UBI. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Python available on OpenShift, including major version updates.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "ImageStreamTag",
"name": "3.9-ubi8"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.9-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.9 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.9 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.9,python",
"version": "3.9",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.access.redhat.com/ubi8/python-39:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.8-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.8 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.8 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.8/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.8,python",
"version": "3.8",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.access.redhat.com/ubi8/python-38:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.8-ubi7",
"annotations": {
"openshift.io/display-name": "Python 3.8 (UBI 7)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.8 applications on UBI 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.8/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.8,python",
"version": "3.8",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.access.redhat.com/ubi7/python-38:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.6-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.6 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.6 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.6/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.6,python",
"version": "3.6",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.access.redhat.com/ubi8/python-36:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7-ubi8",
"annotations": {
"openshift.io/display-name": "Python 2.7 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.access.redhat.com/ubi8/python-27:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7-ubi7",
"annotations": {
"openshift.io/display-name": "Python 2.7 (UBI 7)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on UBI 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.access.redhat.com/ubi7/python-27:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7",
"annotations": {
"openshift.io/display-name": "Python 2.7",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on CentOS 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python,hidden",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "quay.io/centos7/python-27-centos7:latest"
},
"referencePolicy": {
"type": "Local"
}
}
]
}
}

View file

@ -0,0 +1,73 @@
{
"kind": "ImageStream",
"apiVersion": "v1",
"metadata": {
"name": "python",
"annotations": {
"openshift.io/display-name": "Python"
}
},
"spec": {
"tags": [
{
"name": "latest",
"annotations": {
"openshift.io/display-name": "Python (Latest)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python applications on CentOS 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.6/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Python available on OpenShift, including major version updates.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "ImageStreamTag",
"name": "3.6"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.6",
"annotations": {
"openshift.io/display-name": "Python 3.6",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.6 applications on CentOS 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.6/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.6,python",
"version": "3.6",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "docker.io/centos/python-36-centos7:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7",
"annotations": {
"openshift.io/display-name": "Python 2.7",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on CentOS 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "docker.io/centos/python-27-centos7:latest"
},
"referencePolicy": {
"type": "Local"
}
}
]
}
}

View file

@ -0,0 +1,113 @@
{
"kind": "ImageStream",
"apiVersion": "v1",
"metadata": {
"name": "python",
"annotations": {
"openshift.io/display-name": "Python"
}
},
"spec": {
"tags": [
{
"name": "latest",
"annotations": {
"openshift.io/display-name": "Python (Latest)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python applications on UBI. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Python available on OpenShift, including major version updates.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "ImageStreamTag",
"name": "3.9-ubi8"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.9-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.9 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.9 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.9,python",
"version": "3.9",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-39:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.8-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.8 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.8 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.8/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.8,python",
"version": "3.8",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-38:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.6-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.6 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.6 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.6/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.6,python",
"version": "3.6",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-36:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7-ubi8",
"annotations": {
"openshift.io/display-name": "Python 2.7 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-27:latest"
},
"referencePolicy": {
"type": "Local"
}
}
]
}
}

View file

@ -0,0 +1,193 @@
{
"kind": "ImageStream",
"apiVersion": "v1",
"metadata": {
"name": "python",
"annotations": {
"openshift.io/display-name": "Python"
}
},
"spec": {
"tags": [
{
"name": "latest",
"annotations": {
"openshift.io/display-name": "Python (Latest)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python applications on UBI. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Python available on OpenShift, including major version updates.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "ImageStreamTag",
"name": "3.9-ubi8"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.9-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.9 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.9 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.9/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.9,python",
"version": "3.9",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-39:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.8-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.8 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.8 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.8/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.8,python",
"version": "3.8",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-38:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.8-ubi7",
"annotations": {
"openshift.io/display-name": "Python 3.8 (UBI 7)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.8 applications on UBI 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.8/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.8,python",
"version": "3.8",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi7/python-38:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.8",
"annotations": {
"openshift.io/display-name": "Python 3.8",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.8 applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.8/README.md.",
"iconClass": "icon-python",
"tags": "builder,python,hidden",
"supports":"python:3.8,python",
"version": "3.8",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/rhscl/python-38-rhel7:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "3.6-ubi8",
"annotations": {
"openshift.io/display-name": "Python 3.6 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 3.6 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/3.6/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:3.6,python",
"version": "3.6",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-36:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7-ubi8",
"annotations": {
"openshift.io/display-name": "Python 2.7 (UBI 8)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on UBI 8. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi8/python-27:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7-ubi7",
"annotations": {
"openshift.io/display-name": "Python 2.7 (UBI 7)",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on UBI 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/ubi7/python-27:latest"
},
"referencePolicy": {
"type": "Local"
}
},
{
"name": "2.7",
"annotations": {
"openshift.io/display-name": "Python 2.7",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"description": "Build and run Python 2.7 applications on RHEL 7. For more information about using this builder image, including OpenShift considerations, see https://github.com/sclorg/s2i-python-container/blob/master/2.7/README.md.",
"iconClass": "icon-python",
"tags": "builder,python,hidden",
"supports":"python:2.7,python",
"version": "2.7",
"sampleRepo": "https://github.com/sclorg/django-ex.git"
},
"from": {
"kind": "DockerImage",
"name": "registry.redhat.io/rhscl/python-27-rhel7:latest"
},
"referencePolicy": {
"type": "Local"
}
}
]
}
}

View file

@ -1,2 +1,3 @@
Flask
gunicorn
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

View file

@ -0,0 +1,2 @@
ENABLE_MICROPIPENV=true
DISABLE_SETUP_PY_PROCESSING=true

View file

@ -0,0 +1,14 @@
import os
import mod_wsgi.server
mod_wsgi.server.start(
'--log-to-terminal',
'--port', '8080',
'--trust-proxy-header', 'X-Forwarded-For',
'--trust-proxy-header', 'X-Forwarded-Port',
'--trust-proxy-header', 'X-Forwarded-Proto',
'--processes', os.environ.get('MOD_WSGI_PROCESSES', '1'),
'--threads', os.environ.get('MOD_WSGI_THREADS', '5'),
'--application-type', 'module',
'--entry-point', 'wsgi'
)

View file

@ -0,0 +1,2 @@
mod_wsgi
Flask

View file

@ -0,0 +1,9 @@
from flask import Flask
application = Flask(__name__)
@application.route('/')
def hello():
return b'Hello World from mod_wsgi hosted WSGI application!'
if __name__ == '__main__':
application.run()

57
test/micropipenv-test-app/.gitignore vendored Normal file
View file

@ -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/

View file

@ -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/

View file

@ -0,0 +1,14 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
"e1839a8" = {path = ".", editable = true}
requests = "==2.20.0"
[dev-packages]
pytest = ">=2.8.0"
[requires]
python_version = "3.9"

145
test/micropipenv-test-app/Pipfile.lock generated Normal file
View file

@ -0,0 +1,145 @@
{
"_meta": {
"hash": {
"sha256": "9d47e9d2d35583e081cf783e9421e75fafb102d07969ad6f74546f48db3a8101"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
"sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
],
"version": "==2020.6.20"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"e1839a8": {
"editable": true,
"path": "."
},
"gunicorn": {
"hashes": [
"sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
"sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
],
"markers": "python_version >= '3.4'",
"version": "==20.0.4"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
],
"version": "==2.7"
},
"requests": {
"hashes": [
"sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c",
"sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279"
],
"index": "pypi",
"version": "==2.20.0"
},
"testapp": {
"editable": true,
"path": "."
},
"urllib3": {
"hashes": [
"sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4",
"sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'",
"version": "==1.24.3"
}
},
"develop": {
"attrs": {
"hashes": [
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.0"
},
"iniconfig": {
"hashes": [
"sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437",
"sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"
],
"version": "==1.0.1"
},
"packaging": {
"hashes": [
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.4"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"py": {
"hashes": [
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.9.0"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytest": {
"hashes": [
"sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9",
"sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92"
],
"index": "pypi",
"version": "==6.1.1"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"toml": {
"hashes": [
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"version": "==0.10.1"
}
}
}

View file

@ -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"],
)

View file

@ -0,0 +1,7 @@
import requests
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
assert requests.__version__ == '2.20.0'
return [b"Hello from gunicorn WSGI application!"]

View file

@ -0,0 +1,61 @@
#!/bin/bash
# Test virtualenv environment and pip upgrade
echo "Testing that the virtual environment's Python is being used ..."
if [ "$(which python)" != "/opt/app-root/bin/python" ]; then
echo "ERROR: Initialization of the virtual environment failed."
exit 1
fi
echo "Testing UPGRADE_PIP_TO_LATEST=1 (set in .s2i/environment) ..."
packages=("pip" "setuptools" "wheel")
for pkg in ${packages[@]}; do
# grep returns exit code 1 if the output contains only one line starting
# with "Requirement already …" which means that the package is updated
python -m pip install -U --no-deps --no-python-version-warning $pkg 2>&1 | grep -Ev "^Requirement already (up-to-date|satisfied): "
if [ $? -eq 0 ]; then
echo "ERROR: Failed to upgrade '$pkg' to the latest version."
exit 1
fi
done
# Test that npm works correctly
# Create a package.json file so npm doesn't throw off warnings
echo "Initializing npm and testing the installation and use of a module ..."
npm init -y
# mkdirp chosen because it's on the list of 'most depended-upon packages':
# https://www.npmjs.com/browse/depended
npm install mkdirp
if [ $? -ne 0 ]; then
echo "ERROR: Installation of npm module 'mkdirp' failed"
exit 1
fi
# Use the mkdirp module to create a directory and it's subdirectory
node -e "var mkdirp = require('mkdirp'); mkdirp('dir1/dir2')"
if [ $? -ne 0 ]; then
echo "ERROR: Nodejs cannot use the freshly installed 'mkdirp' module."
exit 1
fi
if [ "$(ls dir1)" != "dir2" ]; then
echo "ERROR: The 'mkdirp' module could not create subdirectories at the current path."
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

View file

@ -1,2 +1,4 @@
gunicorn
numpy
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'
numpy<1.17.0; python_version < '3.5'
numpy>=1.17.0; python_version >= '3.5'

@ -0,0 +1 @@
Subproject commit d805fc9986bc087d2dd6dd6655f96097699c56fb

View file

@ -0,0 +1,2 @@
ENABLE_PIPENV=true
ENABLE_MICROPIPENV=true

57
test/pipenv-test-app/.gitignore vendored Normal file
View file

@ -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/

View file

@ -0,0 +1,2 @@
ENABLE_PIPENV=true
DISABLE_SETUP_PY_PROCESSING=true

View file

@ -0,0 +1,14 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
"e1839a8" = {path = ".", editable = true}
requests = "==2.20.0"
[dev-packages]
pytest = ">=2.8.0"
[requires]
python_version = "3.9"

145
test/pipenv-test-app/Pipfile.lock generated Normal file
View file

@ -0,0 +1,145 @@
{
"_meta": {
"hash": {
"sha256": "9d47e9d2d35583e081cf783e9421e75fafb102d07969ad6f74546f48db3a8101"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
"sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
],
"version": "==2020.6.20"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"e1839a8": {
"editable": true,
"path": "."
},
"gunicorn": {
"hashes": [
"sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
"sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
],
"markers": "python_version >= '3.4'",
"version": "==20.0.4"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
],
"version": "==2.7"
},
"requests": {
"hashes": [
"sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c",
"sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279"
],
"index": "pypi",
"version": "==2.20.0"
},
"testapp": {
"editable": true,
"path": "."
},
"urllib3": {
"hashes": [
"sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4",
"sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'",
"version": "==1.24.3"
}
},
"develop": {
"attrs": {
"hashes": [
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.0"
},
"iniconfig": {
"hashes": [
"sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437",
"sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"
],
"version": "==1.0.1"
},
"packaging": {
"hashes": [
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.4"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"py": {
"hashes": [
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.9.0"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytest": {
"hashes": [
"sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9",
"sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92"
],
"index": "pypi",
"version": "==6.1.1"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"toml": {
"hashes": [
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"version": "==0.10.1"
}
}
}

View file

@ -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"],
)

View file

@ -0,0 +1,7 @@
import requests
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
assert requests.__version__ == '2.20.0'
return [b"Hello from gunicorn WSGI application!"]

200
test/run
View file

@ -6,16 +6,36 @@
# IMAGE_NAME specifies a name of the candidate image used for testing.
# The image has to be available before this script is executed.
#
IMAGE_NAME=${IMAGE_NAME:-openshift/python-36-centos7-candidate}
declare -a WEB_APPS=({standalone,setup,django,numpy,app-home,virtualenv-uwsgi,locale,mod-wsgi}-test-app)
declare -a WEB_APPS=({gunicorn-config-different-port,gunicorn-different-port,django-different-port,standalone,setup,setup-requirements,django,numpy,app-home,npm-virtualenv-uwsgi,locale,mod-wsgi,pipenv,pipenv-and-micropipenv-should-fail,pin-pipenv-version,app-module,micropipenv,micropipenv-requirements}-test-app)
# TODO: Make command compatible for Mac users
test_dir="$(readlink -zf $(dirname "${BASH_SOURCE[0]}"))"
image_dir=$(readlink -zf ${test_dir}/..)
# TODO: This should be part of the image metadata
test_port=8080
test_short_summary=''
TESTSUITE_RESULT=0
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
"
if [[ -z $VERSION ]]; then
echo "ERROR: The VERSION variable must be set."
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"
@ -29,13 +49,14 @@ 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 ..."
s2i build ${s2i_args} file://${test_dir}/${1} ${IMAGE_NAME} ${IMAGE_NAME}-testapp
ct_s2i_build_as_df file://${test_dir}/${1} ${IMAGE_NAME} ${IMAGE_NAME}-testapp ${s2i_args}
}
prepare() {
@ -74,31 +95,48 @@ cleanup() {
rm -rf ${test_dir}/${1}/.git
}
check_type() {
# positive test & non-zero exit status = ERROR
# negative test & zero exit status = ERROR
local result="$1"
local type=${2:-positive} # if not defined, we expect possitive test type (zero exit code)
if [[ "$type" == "positive" && "$result" != "0" ]]; then
info "TEST FAILED (${type}), EXPECTED:0 GOT:${result}"
cleanup
return $result
elif [[ "$type" == "negative" && "$result" == "0" ]]; then
info "TEST FAILED (${type}), EXPECTED: non-zero GOT:${result}"
cleanup
return 1
fi
}
check_result() {
# Function sets if test suite failed or not
# If return value is not 0 then mark test case as TESTCASE_RESULT=1
local result="$1"
if [[ "$result" != "0" ]]; then
info "TEST FAILED (${result})"
cleanup
exit $result
TESTCASE_RESULT=1
fi
return $result
}
wait_for_cid() {
local max_attempts=10
local sleep_time=1
local attempt=1
local result=1
info "Waiting for application container to start $CONTAINER_ARGS ..."
while [ $attempt -le $max_attempts ]; do
[ -f $cid_file ] && [ -s $cid_file ] && break
[ -f $cid_file ] && [ -s $cid_file ] && return 0
attempt=$(( $attempt + 1 ))
sleep $sleep_time
done
return 1
}
test_s2i_usage() {
info "Testing 's2i usage' ..."
s2i usage ${s2i_args} ${IMAGE_NAME} &>/dev/null
ct_s2i_usage ${IMAGE_NAME} ${s2i_args} 1>/dev/null
}
test_docker_run_usage() {
@ -157,40 +195,138 @@ 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
check_result $?
test_scl_usage "python --version" "Python 3.6." "${cid_file}"
test_scl_usage "python --version" "Python $VERSION." "${cid_file}"
check_result $?
test_scl_usage "node --version" "^v[0-9]*\.[0-9]*\.[0-9]*" "${cid_file}"
check_result $?
test_scl_usage "npm --version" "^[0-9]*\.[0-9]*\.[0-9]*" "${cid_file}"
check_result $?
test_connection
check_result $?
cleanup_app
}
test_from_dockerfile(){
info "Test from Dockerfile"
# The latest LTS release of Django does not work on RHEL 7 (due to an old version of SQLite),
# and no longer supports Python 2. So on all CentOS/RHEL 7 images and all Python 2 images,
# we're using the old Django LTS version 1.11.x from the main git branch of the django-ex project.
# In all other cases we're using the newer Django LTS version 2.2.x from the corresponding git branch.
# Since we built the candidate image locally, we don't want S2I attempt to pull
# it from Docker hub
s2i_args="--force-pull=false"
if [[ ${VERSION} == "2.7" ]] || docker inspect ${IMAGE_NAME} --format "{{.Config.Env}}" | tr " " "\n" | grep -q "^PLATFORM=el7"; then
django_example_repo_url="https://github.com/sclorg/django-ex.git"
else
django_example_repo_url="https://github.com/sclorg/django-ex.git@2.2.x"
fi
# Verify the 'usage' script is working properly when running the base image with 's2i usage ...'
test_s2i_usage
check_result $?
# Verify the 'usage' script is working properly when running the base image with 'docker run ...'
test_docker_run_usage
check_result $?
for app in ${WEB_APPS[@]}; do
prepare ${app}
run_s2i_build ${app}
sed "s@#IMAGE_NAME#@${IMAGE_NAME}@" $test_dir/from-dockerfile/Dockerfile.tpl > $test_dir/from-dockerfile/Dockerfile
ct_test_app_dockerfile $test_dir/from-dockerfile/Dockerfile $django_example_repo_url 'Welcome to your Django application on OpenShift' app-src
check_result $?
# test application with default user
test_application
info "Test from Dockerfile with no s2i scripts used"
sed "s@#IMAGE_NAME#@${IMAGE_NAME}@" $test_dir/from-dockerfile/Dockerfile_no_s2i.tpl > $test_dir/from-dockerfile/Dockerfile
ct_test_app_dockerfile $test_dir/from-dockerfile/Dockerfile $django_example_repo_url 'Welcome to your Django application on OpenShift' app-src
check_result $?
}
test_application_with_user() {
# test application with random user
CONTAINER_ARGS="-u 12345" test_application
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
# autocleanup only enabled here as only the following tests so far use it
CID_FILE_DIR=$(mktemp -d)
ct_enable_cleanup
info "Testing variable presence during \`docker exec\`"
ct_check_exec_env_vars
check_result $?
info "Checking if all scl variables are defined in Dockerfile"
ct_check_scl_enable_vars
check_result $?
fi
}
function run_all_tests() {
local APP_NAME=${1:-undefined}
for test_case in $TEST_SET; do
info "Running test $test_case ... "
TESTCASE_RESULT=0
$test_case
local test_msg
if [ $TESTCASE_RESULT -eq 0 ]; then
test_msg="[PASSED]"
else
test_msg="[FAILED]"
TESTSUITE_RESULT=1
fi
test "$APP_NAME" == "undefined" && msg_app="" || msg_app="'$APP_NAME'"
printf -v test_short_summary "%s %s for %s %s\n" "${test_short_summary}" "${test_msg}" "${msg_app}" "$test_case"
[ -n "${FAIL_QUICKLY:-}" ] && {
cleanup "${APP_NAME}"
return 1
}
done;
}
# 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}
run_s2i_build ${app}
RESULT=$?
msg_run_s2i_build="'${app}' run_s2i_build"
if [[ "$app" == *"-should-fail-"* ]]; then
# Tests with '-should-fail-' in their name should fail during a build, expecting non-zero exit status
check_type $RESULT "negative"
test "$?" == "0" && test_msg="[PASSED]" || test_msg="[FAILED]"
printf -v test_short_summary "%s %s for %s\n" "${test_short_summary}" "$test_msg" "$msg_run_s2i_build"
continue
else
check_type $RESULT
test "$?" != "0" && test_msg="[FAILED]" || test_msg="[PASSED]"
printf -v test_short_summary "%s %s for %s\n" "${test_short_summary}" "$test_msg" "$msg_run_s2i_build"
fi
echo ""
TEST_SET=${TESTS:-$TEST_LIST} run_all_tests "${app}"
info "All tests for the ${app} finished successfully."
cleanup ${app}
done
info "All tests finished successfully."
TEST_SET=${TESTS:-$TEST_VAR_DOCKER} run_all_tests
echo "$test_short_summary"
if [ $TESTSUITE_RESULT -eq 0 ] ; then
echo "Tests for ${IMAGE_NAME} succeeded."
else
echo "Tests for ${IMAGE_NAME} failed."
fi
exit $TESTSUITE_RESULT

64
test/run-openshift Executable file
View file

@ -0,0 +1,64 @@
#!/bin/bash
#
# Test the Python image in the OpenShift.
#
# IMAGE_NAME specifies a name of the candidate image used for testing.
# VERSION specifies a version of the python in the candidate image.
# The image has to be available before this script is executed.
THISDIR=$(dirname ${BASH_SOURCE[0]})
test_dir="$(readlink -f $(dirname ${BASH_SOURCE[0]}))"
source "${THISDIR}/test-lib.sh"
source "${THISDIR}/test-lib-openshift.sh"
source "${THISDIR}/test-lib-python.sh"
set -eo nounset
trap ct_os_cleanup EXIT SIGINT
test_latest_imagestreams() {
info "Testing the latest version in imagestreams"
# Switch to root directory of a container
pushd "${test_dir}/../.." >/dev/null
ct_check_latest_imagestreams
popd >/dev/null
}
ct_os_check_compulsory_vars
ct_os_enable_print_logs
if [ -z "${EPHEMERAL_TEMPLATES:-}" ]; then
EPHEMERAL_TEMPLATES="
https://raw.githubusercontent.com/sclorg/django-ex/master/openshift/templates/django-postgresql.json \
https://raw.githubusercontent.com/openshift/origin/master/examples/quickstarts/django-postgresql.json"
fi
ct_os_cluster_up
ct_os_test_s2i_app "${IMAGE_NAME}" "${THISDIR}/standalone-test-app" . "Hello World from standalone WSGI application!"
ct_os_test_s2i_app "${IMAGE_NAME}" "https://github.com/sclorg/django-ex.git" . 'Welcome to your Django application on OpenShift'
for template in $EPHEMERAL_TEMPLATES; do
ct_os_test_template_app "$IMAGE_NAME" \
"$template" \
python \
'Welcome to your Django application on OpenShift' \
8080 http 200 "-p SOURCE_REPOSITORY_REF=master -p PYTHON_VERSION=${VERSION} -p POSTGRESQL_VERSION=10 -p NAME=python-testing" \
"quay.io/centos7/postgresql-10-centos7|postgresql:10"
done
# Check the imagestream
test_python_imagestream
# check if latest imagestream version is correct
test_latest_imagestreams
OS_TESTSUITE_RESULT=0
ct_os_cluster_down
# vim: set tabstop=2:shiftwidth=2:expandtab:

View file

@ -0,0 +1,56 @@
#!/bin/bash
#
# Test the Python image in the OpenShift.
#
# IMAGE_NAME specifies a name of the candidate image used for testing.
# VERSION specifies a version of the python in the candidate image.
# The image has to be available before this script is executed.
THISDIR=$(dirname ${BASH_SOURCE[0]})
source "${THISDIR}/test-lib.sh"
source "${THISDIR}/test-lib-openshift.sh"
source "${THISDIR}/test-lib-python.sh"
source "${THISDIR}/test-lib-remote-openshift.sh"
set -eo nounset
ct_os_set_ocp4
trap ct_os_cleanup EXIT SIGINT
ct_os_check_compulsory_vars
oc status || false "It looks like oc is not properly logged in."
# For testing on OpenShift 4 we use internal registry
export CT_EXTERNAL_REGISTRY=true
test -n "${IMAGE_NAME-}" || false 'make sure $IMAGE_NAME is defined'
test -n "${VERSION-}" || false 'make sure $VERSION is defined'
if [ -z "${EPHEMERAL_TEMPLATES:-}" ]; then
EPHEMERAL_TEMPLATES="
https://raw.githubusercontent.com/sclorg/django-ex/master/openshift/templates/django-postgresql.json \
https://raw.githubusercontent.com/openshift/origin/master/examples/quickstarts/django-postgresql.json"
fi
ct_os_test_s2i_app "${IMAGE_NAME}" "https://github.com/sclorg/s2i-python-container.git" "examples/standalone-test-app" "Hello World from standalone WSGI application!"
ct_os_test_s2i_app "${IMAGE_NAME}" "https://github.com/sclorg/django-ex.git" . 'Welcome to your Django application on OpenShift'
for template in $EPHEMERAL_TEMPLATES; do
ct_os_test_template_app "$IMAGE_NAME" \
"$template" \
python \
'Welcome to your Django application on OpenShift' \
8080 http 200 "-p SOURCE_REPOSITORY_REF=master -p PYTHON_VERSION=${VERSION} -p POSTGRESQL_VERSION=10 -p NAME=python-testing" \
"rhscl/postgresql-10-rhel7|postgresql:10"
done
# Check the imagestream
test_python_imagestream
OS_TESTSUITE_RESULT=0
# vim: set tabstop=2:shiftwidth=2:expandtab:

View file

@ -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/

View file

@ -0,0 +1,3 @@
-e .
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

View file

@ -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"],
)

View file

@ -0,0 +1,4 @@
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
return [b"Hello from gunicorn WSGI application!"]

View file

@ -0,0 +1,2 @@
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'

View file

@ -2,7 +2,7 @@ import os
import time
from gunicorn.app.base import BaseApplication
from gunicorn.six import iteritems
from six import iteritems
print('LOADING MODULE %s' % __file__)

View file

@ -1 +1,3 @@
gunicorn
gunicorn<20.0.0; python_version < '3.5'
gunicorn>=20.0.0; python_version >= '3.5'
six

1297
test/test-lib-openshift.sh Normal file

File diff suppressed because it is too large Load diff

31
test/test-lib-python.sh Normal file
View file

@ -0,0 +1,31 @@
#!/bin/bash
#
# Functions for tests for the Python image in OpenShift.
#
# IMAGE_NAME specifies a name of the candidate image used for testing.
# The image has to be available before this script is executed.
#
THISDIR=$(dirname ${BASH_SOURCE[0]})
source "${THISDIR}/test-lib.sh"
source "${THISDIR}/test-lib-openshift.sh"
# Check the imagestream
function test_python_imagestream() {
case ${OS} in
rhel7|centos7) ;;
*) echo "Imagestream testing not supported for $OS environment." ; return 0 ;;
esac
ct_os_test_image_stream_quickstart "${THISDIR}/imagestreams/python-${OS%[0-9]*}.json" \
'https://raw.githubusercontent.com/sclorg/django-ex/master/openshift/templates/django-postgresql.json' \
"${IMAGE_NAME}" \
'python' \
'Welcome to your Django application on OpenShift' \
8080 http 200 "-p SOURCE_REPOSITORY_REF=master -p PYTHON_VERSION=${VERSION} -p POSTGRESQL_VERSION=10 -p NAME=python-testing" \
"rhscl/postgresql-10-rhel7|postgresql:10"
}
# vim: set tabstop=2:shiftwidth=2:expandtab:

View file

@ -0,0 +1,112 @@
# shellcheck shell=bash
# some functions are used from test-lib.sh, that is usually in the same dir
# shellcheck source=/dev/null
source "$(dirname "${BASH_SOURCE[0]}")"/test-lib.sh
# Set of functions for testing docker images in OpenShift using 'oc' command
# A variable containing the overall test result; must be changed to 0 in the end
# of the testing script:
# OS_TESTSUITE_RESULT=0
# And the following trap must be set, in the beginning of the test script:
# trap ct_os_cleanup EXIT SIGINT
# ct_os_set_path_oc_4 OC_VERSION
# --------------------
# This is a trick that helps using correct version 4 of the `oc`:
# The input is version of the openshift in format 4.4 etc.
# If the currently available version of oc is not of this version,
# it first takes a look into /usr/local/oc-<ver>/bin directory,
# Arguments: oc_version - X.Y part of the version of OSE (e.g. 3.9)
function ct_os_set_path_oc_4() {
echo "Setting OCP4 client"
local oc_version=$1
local installed_oc_path="/usr/local/oc-v${oc_version}/bin"
echo "PATH ${installed_oc_path}"
if [ -x "${installed_oc_path}/oc" ] ; then
oc_path="${installed_oc_path}"
echo "Binary oc found in ${installed_oc_path}" >&2
else
echo "OCP4 not found"
return 1
fi
export PATH="${oc_path}:${PATH}"
oc version
if ! oc version | grep -q "Client Version: ${oc_version}." ; then
echo "ERROR: something went wrong, oc located at ${oc_path}, but oc of version ${oc_version} not found in PATH ($PATH)" >&1
return 1
else
echo "PATH set correctly, binary oc found in version ${oc_version}: $(command -v oc)"
fi
}
# ct_os_prepare_ocp4
# ------------------
# Prepares environment for testing images in OpenShift 4 environment
#
#
function ct_os_set_ocp4() {
local login
OS_OC_CLIENT_VERSION=${OS_OC_CLIENT_VERSION:-4.4}
ct_os_set_path_oc_4 "${OS_OC_CLIENT_VERSION}"
oc version
login=$(cat "$KUBEPASSWORD")
oc login -u kubeadmin -p "$login"
echo "Login to OpenShift ${OS_OC_CLIENT_VERSION} is DONE"
# let openshift cluster to sync to avoid some race condition errors
sleep 3
}
function ct_os_upload_image_external_registry() {
local input_name="${1}" ; shift
local image_name=${input_name##*/}
local imagestream=${1:-$image_name:latest}
local output_name
ct_os_login_external_registry
output_name="${INTERNAL_DOCKER_REGISTRY}/rhscl-ci-testing/$imagestream"
docker images
docker tag "${input_name}" "${output_name}"
docker push "${output_name}"
}
function ct_os_login_external_registry() {
local docker_token
# docker login fails with "404 page not found" error sometimes, just try it more times
# shellcheck disable=SC2034
echo "loging"
[ -z "${INTERNAL_DOCKER_REGISTRY:-}" ] && "INTERNAL_DOCKER_REGISTRY has to be set for working with Internal registry" && return 1
# shellcheck disable=SC2034
for i in $(seq 12) ; do
# shellcheck disable=SC2015
docker_token=$(cat "$DOCKER_UPSHIFT_TOKEN")
# shellcheck disable=SC2015
docker login -u rhscl-ci-testing -p "$docker_token" "${INTERNAL_DOCKER_REGISTRY}" && return 0 || :
sleep 5
done
return 1
}
function ct_os_import_image_ocp4() {
local image_name="${1}"; shift
local imagestream=${1:-$image_name:latest}
local namespace
namespace=${CT_NAMESPACE:-"$(oc project -q)"}
deploy_image_name="${INTERNAL_DOCKER_REGISTRY}/rhscl-ci-testing/${imagestream}"
echo "Uploading image ${image_name} as ${deploy_image_name} , ${imagestream} into external registry."
ct_os_upload_image_external_registry "${image_name}" "${imagestream}"
if [ "${CT_TAG_IMAGE:-false}" == 'true' ]; then
echo "Tag ${deploy_image_name} to ${namespace}/${imagestream}"
oc tag --source=docker "${deploy_image_name}" "${namespace}/${imagestream}" --insecure=true --reference-policy=local
else
echo "Import image into OpenShift 4 environment ${namespace}/${imagestream} from ${deploy_image_name}"
oc import-image "${namespace}/${imagestream}" --from="${deploy_image_name}" --confirm --reference-policy=local
fi
}

913
test/test-lib.sh Normal file
View file

@ -0,0 +1,913 @@
# shellcheck shell=bash
#
# Test a container image.
#
# Always use sourced from a specific container testfile
#
# reguires definition of CID_FILE_DIR
# CID_FILE_DIR=$(mktemp --suffix=<container>_test_cidfiles -d)
# reguires definition of TEST_LIST
# TEST_LIST="\
# ctest_container_creation
# ctest_doc_content"
# Container CI tests
# abbreviated as "ct"
# may be redefined in the specific container testfile
EXPECTED_EXIT_CODE=0
# ct_cleanup
# --------------------
# Cleans up containers used during tests. Stops and removes all containers
# referenced by cid_files in CID_FILE_DIR. Dumps logs if a container exited
# unexpectedly. Removes the cid_files and CID_FILE_DIR as well.
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $EXPECTED_EXIT_CODE - expected container exit code
function ct_cleanup() {
ct_show_resources
for cid_file in "$CID_FILE_DIR"/* ; do
[ -f "$cid_file" ] || continue
local container
container=$(cat "$cid_file")
: "Stopping and removing container $container..."
docker stop "$container"
exit_status=$(docker inspect -f '{{.State.ExitCode}}' "$container")
if [ "$exit_status" != "$EXPECTED_EXIT_CODE" ]; then
: "Dumping logs for $container"
docker logs "$container"
fi
docker rm -v "$container"
rm "$cid_file"
done
rmdir "$CID_FILE_DIR"
: "Done."
}
# ct_enable_cleanup
# --------------------
# Enables automatic container cleanup after tests.
function ct_enable_cleanup() {
trap ct_cleanup EXIT SIGINT
}
# ct_check_envs_set env_filter check_envs loop_envs [env_format]
# --------------------
# Compares values from one list of environment variable definitions against such list,
# checking if the values are present and have a specific format.
# Argument: env_filter - optional string passed to grep used for
# choosing which variables to filter out in env var lists.
# Argument: check_envs - list of env var definitions to check values against
# Argument: loop_envs - list of env var definitions to check values for
# Argument: env_format (optional) - format string for bash substring deletion used
# for checking whether the value is contained in check_envs.
# Defaults to: "*VALUE*", VALUE string gets replaced by actual value from loop_envs
function ct_check_envs_set {
local env_filter check_envs env_format
env_filter=$1; shift
check_envs=$1; shift
loop_envs=$1; shift
env_format=${1:-"*VALUE*"}
while read -r variable; do
[ -z "$variable" ] && continue
var_name=$(echo "$variable" | awk -F= '{ print $1 }')
stripped=$(echo "$variable" | awk -F= '{ print $2 }')
filtered_envs=$(echo "$check_envs" | grep "^$var_name=")
[ -z "$filtered_envs" ] && { echo "$var_name not found during \` docker exec\`"; return 1; }
old_IFS=$IFS
# For each such variable compare its content with the `docker exec` result, use `:` as delimiter
IFS=:
for value in $stripped; do
# If the falue checked does not go through env_filter we do not care about it
echo "$value" | grep -q "$env_filter" || continue
if [ -n "${filtered_envs##${env_format//VALUE/$value}}" ]; then
echo " Value $value is missing from variable $var_name"
echo "$filtered_envs"
IFS=$old_IFS
return 1
fi
done
IFS=$old_IFS
done <<< "$(echo "$loop_envs" | grep "$env_filter" | grep -v "^PWD=")"
}
# ct_get_cid [name]
# --------------------
# Prints container id from cid_file based on the name of the file.
# Argument: name - name of cid_file where the container id will be stored
# Uses: $CID_FILE_DIR - path to directory containing cid_files
function ct_get_cid() {
local name="$1" ; shift || return 1
cat "$CID_FILE_DIR/$name"
}
# ct_get_cip [id]
# --------------------
# Prints container ip address based on the container id.
# Argument: id - container id
function ct_get_cip() {
local id="$1" ; shift
docker inspect --format='{{.NetworkSettings.IPAddress}}' "$(ct_get_cid "$id")"
}
# ct_wait_for_cid [cid_file]
# --------------------
# Holds the execution until the cid_file is created. Usually run after container
# creation.
# Argument: cid_file - name of the cid_file that should be created
function ct_wait_for_cid() {
local cid_file=$1
local max_attempts=10
local sleep_time=1
local attempt=1
local result=1
while [ $attempt -le $max_attempts ]; do
[ -f "$cid_file" ] && [ -s "$cid_file" ] && return 0
: "Waiting for container start..."
attempt=$(( attempt + 1 ))
sleep $sleep_time
done
return 1
}
# ct_assert_container_creation_fails [container_args]
# --------------------
# The invocation of docker run should fail based on invalid container_args
# passed to the function. Returns 0 when container fails to start properly.
# Argument: container_args - all arguments are passed directly to dokcer run
# Uses: $CID_FILE_DIR - path to directory containing cid_files
function ct_assert_container_creation_fails() {
local ret=0
local max_attempts=10
local attempt=1
local cid_file=assert
set +e
local old_container_args="${CONTAINER_ARGS-}"
# we really work with CONTAINER_ARGS as with a string
# shellcheck disable=SC2124
CONTAINER_ARGS="$@"
if ct_create_container "$cid_file" ; then
local cid
cid=$(ct_get_cid "$cid_file")
while [ "$(docker inspect -f '{{.State.Running}}' "$cid")" == "true" ] ; do
sleep 2
attempt=$(( attempt + 1 ))
if [ "$attempt" -gt "$max_attempts" ]; then
docker stop "$cid"
ret=1
break
fi
done
exit_status=$(docker inspect -f '{{.State.ExitCode}}' "$cid")
if [ "$exit_status" == "0" ]; then
ret=1
fi
docker rm -v "$cid"
rm "$CID_FILE_DIR/$cid_file"
fi
[ -n "$old_container_args" ] && CONTAINER_ARGS="$old_container_args"
set -e
return "$ret"
}
# ct_create_container [name, command]
# --------------------
# Creates a container using the IMAGE_NAME and CONTAINER_ARGS variables. Also
# stores the container id to a cid_file located in the CID_FILE_DIR, and waits
# for the creation of the file.
# Argument: name - name of cid_file where the container id will be stored
# Argument: command - optional command to be executed in the container
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $CONTAINER_ARGS - optional arguments passed directly to docker run
# Uses: $IMAGE_NAME - name of the image being tested
function ct_create_container() {
local cid_file="$CID_FILE_DIR/$1" ; shift
# create container with a cidfile in a directory for cleanup
# shellcheck disable=SC2086
docker run --cidfile="$cid_file" -d ${CONTAINER_ARGS:-} "$IMAGE_NAME" "$@"
ct_wait_for_cid "$cid_file" || return 1
: "Created container $(cat "$cid_file")"
}
# ct_scl_usage_old [name, command, expected]
# --------------------
# Tests three ways of running the SCL, by looking for an expected string
# in the output of the command
# Argument: name - name of cid_file where the container id will be stored
# Argument: command - executed inside the container
# Argument: expected - string that is expected to be in the command output
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $IMAGE_NAME - name of the image being tested
function ct_scl_usage_old() {
local name="$1"
local command="$2"
local expected="$3"
local out=""
: " Testing the image SCL enable"
out=$(docker run --rm "${IMAGE_NAME}" /bin/bash -c "${command}")
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[/bin/bash -c \"${command}\"] Expected '${expected}', got '${out}'" >&2
return 1
fi
out=$(docker exec "$(ct_get_cid "$name")" /bin/bash -c "${command}" 2>&1)
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[exec /bin/bash -c \"${command}\"] Expected '${expected}', got '${out}'" >&2
return 1
fi
out=$(docker exec "$(ct_get_cid "$name")" /bin/sh -ic "${command}" 2>&1)
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[exec /bin/sh -ic \"${command}\"] Expected '${expected}', got '${out}'" >&2
return 1
fi
}
# ct_doc_content_old [strings]
# --------------------
# Looks for occurence of stirngs in the documentation files and checks
# the format of the files. Files examined: help.1
# Argument: strings - strings expected to appear in the documentation
# Uses: $IMAGE_NAME - name of the image being tested
function ct_doc_content_old() {
local tmpdir
tmpdir=$(mktemp -d)
local f
: " Testing documentation in the container image"
# Extract the help files from the container
# shellcheck disable=SC2043
for f in help.1 ; do
docker run --rm "${IMAGE_NAME}" /bin/bash -c "cat /${f}" >"${tmpdir}/$(basename "${f}")"
# Check whether the files contain some important information
for term in "$@" ; do
if ! grep -F -q -e "${term}" "${tmpdir}/$(basename "${f}")" ; then
echo "ERROR: File /${f} does not include '${term}'." >&2
return 1
fi
done
# Check whether the files use the correct format
for term in TH PP SH ; do
if ! grep -q "^\.${term}" "${tmpdir}/help.1" ; then
echo "ERROR: /help.1 is probably not in troff or groff format, since '${term}' is missing." >&2
return 1
fi
done
done
: " Success!"
}
# full_ca_file_path
# Return string for full path to CA file
function full_ca_file_path()
{
echo "/etc/pki/ca-trust/source/anchors/RH-IT-Root-CA.crt"
}
# ct_mount_ca_file
# ------------------
# Check if /etc/pki/certs/RH-IT-Root-CA.crt file exists
# return mount string for containers or empty string
function ct_mount_ca_file()
{
# mount CA file only if NPM_REGISTRY variable is present.
local mount_parameter=""
if [ -n "$NPM_REGISTRY" ] && [ -f "$(full_ca_file_path)" ]; then
mount_parameter="-v $(full_ca_file_path):$(full_ca_file_path):Z"
fi
echo "$mount_parameter"
}
# ct_build_s2i_npm_variables URL_TO_NPM_JS_SERVER
# ------------------------------------------
# Function returns -e NPM_MIRROR and -v MOUNT_POINT_FOR_CAFILE
# or empty string
function ct_build_s2i_npm_variables()
{
npm_variables=""
if [ -n "$NPM_REGISTRY" ] && [ -f "$(full_ca_file_path)" ]; then
npm_variables="-e NPM_MIRROR=$NPM_REGISTRY $(ct_mount_ca_file)"
fi
echo "$npm_variables"
}
# ct_npm_works
# --------------------
# Checks existance of the npm tool and runs it.
function ct_npm_works() {
local tmpdir
tmpdir=$(mktemp -d)
: " Testing npm in the container image"
local cid_file="${tmpdir}/cid"
if ! docker run --rm "${IMAGE_NAME}" /bin/bash -c "npm --version" >"${tmpdir}/version" ; then
echo "ERROR: 'npm --version' does not work inside the image ${IMAGE_NAME}." >&2
return 1
fi
# shellcheck disable=SC2046
docker run -d $(ct_mount_ca_file) --rm --cidfile="$cid_file" "${IMAGE_NAME}-testapp"
# Wait for the container to write it's CID file
ct_wait_for_cid "$cid_file" || return 1
if ! docker exec "$(cat "$cid_file")" /bin/bash -c "npm --verbose install jquery && test -f node_modules/jquery/src/jquery.js" >"${tmpdir}/jquery" 2>&1 ; then
echo "ERROR: npm could not install jquery inside the image ${IMAGE_NAME}." >&2
return 1
fi
if [ -n "$NPM_REGISTRY" ] && [ -f "$(full_ca_file_path)" ]; then
if ! grep -qo "$NPM_REGISTRY" "${tmpdir}/jquery"; then
echo "ERROR: Internal repository is NOT set. Even it is requested."
return 1
fi
fi
if [ -f "$cid_file" ]; then
docker stop "$(cat "$cid_file")"
rm "$cid_file"
fi
: " Success!"
}
# ct_binary_found_from_df binary [path]
# --------------------
# Checks if a binary can be found in PATH during Dockerfile build
# Argument: binary - name of the binary to test accessibility for
# Argument: path - optional path in which the binary should reside in
# /opt/rh by default
function ct_binary_found_from_df() {
local tmpdir
local binary=$1; shift
local binary_path=${1:-"^/opt/rh"}
tmpdir=$(mktemp -d)
: " Testing $binary in build from Dockerfile"
# Create Dockerfile that looks for the binary
cat <<EOF >"$tmpdir/Dockerfile"
FROM $IMAGE_NAME
RUN command -v $binary | grep "$binary_path"
EOF
# Build an image, looking for expected path in the output
if ! docker build -f "$tmpdir/Dockerfile" --no-cache "$tmpdir"; then
echo " ERROR: Failed to find $binary in Dockerfile!" >&2
return 1
fi
: " Success!"
}
# ct_check_exec_env_vars [env_filter]
# --------------------
# Checks if all relevant environment variables from `docker run`
# can be found in `docker exec` as well.
# Argument: env_filter - optional string passed to grep used for
# choosing which variables to check in the test case.
# Defaults to X_SCLS and variables containing /opt/app-root, /opt/rh
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $IMAGE_NAME - name of the image being tested
function ct_check_exec_env_vars() {
local tmpdir exec_envs cid old_IFS env_filter
local var_name stripped filtered_envs run_envs
env_filter=${1:-"^X_SCLS=\|/opt/rh\|/opt/app-root"}
tmpdir=$(mktemp -d)
CID_FILE_DIR=${CID_FILE_DIR:-$(mktemp -d)}
# Get environment variables from `docker run`
run_envs=$(docker run --rm "$IMAGE_NAME" /bin/bash -c "env")
# Get environment variables from `docker exec`
ct_create_container "test_exec_envs" bash -c "sleep 1000" >/dev/null
cid=$(ct_get_cid "test_exec_envs")
exec_envs=$(docker exec "$cid" env)
# Filter out variables we are not interested in
# Always check X_SCLS, ignore PWD
# Check variables from `docker run` that have alternative paths inside (/opt/rh, /opt/app-root)
ct_check_envs_set "$env_filter" "$exec_envs" "$run_envs" "*VALUE*" || return 1
echo " All values present in \`docker exec\`"
return 0
}
# ct_check_scl_enable_vars [env_filter]
# --------------------
# Checks if all relevant environment variables from `docker run`
# are set twice after a second call of `scl enable $SCLS`.
# Argument: env_filter - optional string passed to grep used for
# choosing which variables to check in the test case.
# Defaults to paths containing enabled SCLS in the image
# Uses: $IMAGE_NAME - name of the image being tested
function ct_check_scl_enable_vars() {
local tmpdir exec_envs cid old_IFS env_filter enabled_scls
local var_name stripped filtered_envs loop_envs
env_filter=$1
tmpdir=$(mktemp -d)
enabled_scls=$(docker run --rm "$IMAGE_NAME" /bin/bash -c "echo \$X_SCLS")
if [ -z "$env_filter" ]; then
for scl in $enabled_scls; do
[ -z "$env_filter" ] && env_filter="/$scl" && continue
# env_filter not empty, append to the existing list
env_filter="$env_filter|/$scl"
done
fi
# Get environment variables from `docker run`
loop_envs=$(docker run --rm "$IMAGE_NAME" /bin/bash -c "env")
run_envs=$(docker run --rm "$IMAGE_NAME" /bin/bash -c "X_SCLS= scl enable $enabled_scls env")
# Check if the values are set twice in the second set of envs
ct_check_envs_set "$env_filter" "$run_envs" "$loop_envs" "*VALUE*VALUE*" || return 1
echo " All scl_enable values present"
return 0
}
# ct_path_append PATH_VARNAME DIRECTORY
# -------------------------------------
# Append DIRECTORY to VARIABLE of name PATH_VARNAME, the VARIABLE must consist
# of colon-separated list of directories.
ct_path_append ()
{
if eval "test -n \"\${$1-}\""; then
eval "$1=\$2:\$$1"
else
eval "$1=\$2"
fi
}
# ct_path_foreach PATH ACTION [ARGS ...]
# --------------------------------------
# For each DIR in PATH execute ACTION (path is colon separated list of
# directories). The particular calls to ACTION will look like
# '$ ACTION directory [ARGS ...]'
ct_path_foreach ()
{
local dir dirlist action save_IFS
save_IFS=$IFS
IFS=:
dirlist=$1
action=$2
shift 2
for dir in $dirlist; do "$action" "$dir" "$@" ; done
IFS=$save_IFS
}
# ct_run_test_list
# --------------------
# Execute the tests specified by TEST_LIST
# Uses: $TEST_LIST - list of test names
function ct_run_test_list() {
for test_case in $TEST_LIST; do
: "Running test $test_case"
# shellcheck source=/dev/null
[ -f "test/$test_case" ] && source "test/$test_case"
# shellcheck source=/dev/null
[ -f "../test/$test_case" ] && source "../test/$test_case"
$test_case
done;
}
# ct_gen_self_signed_cert_pem
# ---------------------------
# Generates a self-signed PEM certificate pair into specified directory.
# Argument: output_dir - output directory path
# Argument: base_name - base name of the certificate files
# Resulted files will be those:
# <output_dir>/<base_name>-cert-selfsigned.pem -- public PEM cert
# <output_dir>/<base_name>-key.pem -- PEM private key
ct_gen_self_signed_cert_pem() {
local output_dir=$1 ; shift
local base_name=$1 ; shift
mkdir -p "${output_dir}"
openssl req -newkey rsa:2048 -nodes -keyout "${output_dir}"/"${base_name}"-key.pem -subj '/C=GB/ST=Berkshire/L=Newbury/O=My Server Company' > "${base_name}"-req.pem
openssl req -new -x509 -nodes -key "${output_dir}"/"${base_name}"-key.pem -batch > "${output_dir}"/"${base_name}"-cert-selfsigned.pem
}
# ct_obtain_input FILE|DIR|URL
# --------------------
# Either copies a file or a directory to a tmp location for local copies, or
# downloads the file from remote location.
# Resulted file path is printed, so it can be later used by calling function.
# Arguments: input - local file, directory or remote URL
function ct_obtain_input() {
local input=$1
local extension="${input##*.}"
# Try to use same extension for the temporary file if possible
[[ "${extension}" =~ ^[a-z0-9]*$ ]] && extension=".${extension}" || extension=""
local output
output=$(mktemp "/var/tmp/test-input-XXXXXX$extension")
if [ -f "${input}" ] ; then
cp -f "${input}" "${output}"
elif [ -d "${input}" ] ; then
rm -f "${output}"
cp -r -LH "${input}" "${output}"
elif echo "${input}" | grep -qe '^http\(s\)\?://' ; then
curl "${input}" > "${output}"
else
echo "ERROR: file type not known: ${input}" >&2
return 1
fi
echo "${output}"
}
# ct_test_response
# ----------------
# Perform GET request to the application container, checks output with
# a reg-exp and HTTP response code.
# Argument: url - request URL path
# Argument: expected_code - expected HTTP response code
# Argument: body_regexp - PCRE regular expression that must match the response body
# Argument: max_attempts - Optional number of attempts (default: 20), three seconds sleep between
# Argument: ignore_error_attempts - Optional number of attempts when we ignore error output (default: 10)
ct_test_response() {
local url="$1"
local expected_code="$2"
local body_regexp="$3"
local max_attempts=${4:-20}
local ignore_error_attempts=${5:-10}
: " Testing the HTTP(S) response for <${url}>"
local sleep_time=3
local attempt=1
local result=1
local status
local response_code
local response_file
response_file=$(mktemp /tmp/ct_test_response_XXXXXX)
while [ "${attempt}" -le "${max_attempts}" ]; do
curl --connect-timeout 10 -s -w '%{http_code}' "${url}" >"${response_file}" && status=0 || status=1
if [ "${status}" -eq 0 ]; then
response_code=$(tail -c 3 "${response_file}")
if [ "${response_code}" -eq "${expected_code}" ]; then
result=0
fi
grep -qP -e "${body_regexp}" "${response_file}" || result=1;
# Some services return 40x code until they are ready, so let's give them
# some chance and not end with failure right away
# Do not wait if we already have expected outcome though
if [ "${result}" -eq 0 ] || [ "${attempt}" -gt "${ignore_error_attempts}" ] || [ "${attempt}" -eq "${max_attempts}" ] ; then
break
fi
fi
attempt=$(( attempt + 1 ))
sleep "${sleep_time}"
done
rm -f "${response_file}"
return "${result}"
}
# ct_registry_from_os OS
# ----------------
# Transform operating system string [os] into registry url
# Argument: OS - string containing the os version
ct_registry_from_os() {
local registry=""
case $1 in
rhel*)
registry=registry.redhat.io
;;
*)
registry=quay.io
;;
esac
echo "$registry"
}
# ct_get_public_image_name OS BASE_IMAGE_NAME VERSION
# ----------------
# Transform the arguments into public image name
# Argument: OS - string containing the os version
# Argument: BASE_IMAGE_NAME - string containing the base name of the image as defined in the Makefile
# Argument: VERSION - string containing the version of the image as defined in the Makefile
ct_get_public_image_name() {
local os=$1; shift
local base_image_name=$1; shift
local version=$1; shift
local public_image_name
local registry
registry=$(ct_registry_from_os "$os")
if [ "x$os" == "xrhel7" ]; then
public_image_name=$registry/rhscl/$base_image_name-${version//./}-rhel7
elif [ "x$os" == "xrhel8" ]; then
public_image_name=$registry/rhel8/$base_image_name-${version//./}
elif [ "x$os" == "xcentos7" ]; then
public_image_name=$registry/centos7/$base_image_name-${version//./}-centos7
elif [ "x$os" == "xcentos8" ]; then
public_image_name=$registry/centos8/$base_image_name-${version//./}-centos8
fi
echo "$public_image_name"
}
# ct_assert_cmd_success CMD
# ----------------
# Evaluates [cmd] and fails if it does not succeed.
# Argument: CMD - Command to be run
function ct_assert_cmd_success() {
echo "Checking '$*' for success ..."
if ! eval "$@" &>/dev/null; then
echo " FAIL"
return 1
fi
echo " PASS"
return 0
}
# ct_assert_cmd_failure CMD
# ----------------
# Evaluates [cmd] and fails if it succeeds.
# Argument: CMD - Command to be run
function ct_assert_cmd_failure() {
echo "Checking '$*' for failure ..."
if eval "$@" &>/dev/null; then
echo " FAIL"
return 1
fi
echo " PASS"
return 0
}
# ct_random_string [LENGTH=10]
# ----------------------------
# Generate pseudorandom alphanumeric string of LENGTH bytes, the
# default length is 10. The string is printed on stdout.
ct_random_string()
(
export LC_ALL=C
dd if=/dev/urandom count=1 bs=10k 2>/dev/null \
| tr -dc 'a-z0-9' \
| fold -w "${1-10}" \
| head -n 1
)
# ct_s2i_usage IMG_NAME [S2I_ARGS]
# ----------------------------
# Create a container and run the usage script inside
# Argument: IMG_NAME - name of the image to be used for the container run
# Argument: S2I_ARGS - Additional list of source-to-image arguments, currently unused.
ct_s2i_usage()
{
local img_name=$1; shift
local s2i_args="$*";
local usage_command="/usr/libexec/s2i/usage"
docker run --rm "$img_name" bash -c "$usage_command"
}
# ct_s2i_build_as_df APP_PATH SRC_IMAGE DST_IMAGE [S2I_ARGS]
# ----------------------------
# Create a new s2i app image from local sources in a similar way as source-to-image would have used.
# Argument: APP_PATH - local path to the app sources to be used in the test
# Argument: SRC_IMAGE - image to be used as a base for the s2i build
# Argument: DST_IMAGE - image name to be used during the tagging of the s2i build result
# Argument: S2I_ARGS - Additional list of source-to-image arguments.
# Only used to check for pull-policy=never and environment variable definitions.
ct_s2i_build_as_df()
{
local app_path=$1; shift
local src_image=$1; shift
local dst_image=$1; shift
local s2i_args="$*";
local local_app=upload/src/
local local_scripts=upload/scripts/
local user_id=
local df_name=
local tmpdir=
local incremental=false
local mount_options=""
# Run the entire thing inside a subshell so that we do not leak shell options outside of the function
(
# Error out if any part of the build fails
set -e
# Use /tmp to not pollute cwd
tmpdir=$(mktemp -d)
df_name=$(mktemp -p "$tmpdir" Dockerfile.XXXX)
cd "$tmpdir"
# Check if the image is available locally and try to pull it if it is not
docker images "$src_image" &>/dev/null || echo "$s2i_args" | grep -q "pull-policy=never" || docker pull "$src_image"
user=$(docker inspect -f "{{.Config.User}}" "$src_image")
# Default to root if no user is set by the image
user=${user:-0}
# run the user through the image in case it is non-numeric or does not exist
# NOTE: The '-eq' test is used to check if $user is numeric as it will fail if $user is not an integer
if ! [ "$user" -eq "$user" ] 2>/dev/null && ! user_id=$(docker run --rm "$src_image" bash -c "id -u $user 2>/dev/null"); then
echo "ERROR: id of user $user not found inside image $src_image."
echo "Terminating s2i build."
return 1
else
user_id=${user_id:-$user}
fi
echo "$s2i_args" | grep -q "\-\-incremental" && incremental=true
if $incremental; then
inc_tmp=$(mktemp -d --tmpdir incremental.XXXX)
setfacl -m "u:$user_id:rwx" "$inc_tmp"
# Check if the image exists, build should fail (for testing use case) if it does not
docker images "$dst_image" &>/dev/null || (echo "Image $dst_image not found."; false)
# Run the original image with a mounted in volume and get the artifacts out of it
cmd="if [ -s /usr/libexec/s2i/save-artifacts ]; then /usr/libexec/s2i/save-artifacts > \"$inc_tmp/artifacts.tar\"; else touch \"$inc_tmp/artifacts.tar\"; fi"
docker run --rm -v "$inc_tmp:$inc_tmp:Z" "$dst_image" bash -c "$cmd"
# Move the created content into the $tmpdir for the build to pick it up
mv "$inc_tmp/artifacts.tar" "$tmpdir/"
fi
# Strip file:// from APP_PATH and copy its contents into current context
mkdir -p "$local_app"
cp -r "${app_path/file:\/\//}/." "$local_app"
[ -d "$local_app/.s2i/bin/" ] && mv "$local_app/.s2i/bin" "$local_scripts"
# Create a Dockerfile named df_name and fill it with proper content
#FIXME: Some commands could be combined into a single layer but not sure if worth the trouble for testing purposes
cat <<EOF >"$df_name"
FROM $src_image
LABEL "io.openshift.s2i.build.image"="$src_image" \\
"io.openshift.s2i.build.source-location"="$app_path"
USER root
COPY $local_app /tmp/src
EOF
[ -d "$local_scripts" ] && echo "COPY $local_scripts /tmp/scripts" >> "$df_name" &&
echo "RUN chown -R $user_id:0 /tmp/scripts" >>"$df_name"
echo "RUN chown -R $user_id:0 /tmp/src" >>"$df_name"
# Check for custom environment variables inside .s2i/ folder
if [ -e "$local_app/.s2i/environment" ]; then
# Remove any comments and add the contents as ENV commands to the Dockerfile
sed '/^\s*#.*$/d' "$local_app/.s2i/environment" | while read -r line; do
echo "ENV $line" >>"$df_name"
done
fi
# Filter out env var definitions from $s2i_args and create Dockerfile ENV commands out of them
echo "$s2i_args" | grep -o -e '\(-e\|--env\)[[:space:]=]\S*=\S*' | sed -e 's/-e /ENV /' -e 's/--env[ =]/ENV /' >>"$df_name"
# Check if CA autority is present on host and add it into Dockerfile
[ -f "$(full_ca_file_path)" ] && echo "RUN cd /etc/pki/ca-trust/source/anchors && update-ca-trust extract" >>"$df_name"
# Add in artifacts if doing an incremental build
if $incremental; then
{ echo "RUN mkdir /tmp/artifacts"
echo "ADD artifacts.tar /tmp/artifacts"
echo "RUN chown -R $user_id:0 /tmp/artifacts" ; } >>"$df_name"
fi
echo "USER $user_id" >>"$df_name"
# If exists, run the custom assemble script, else default to /usr/libexec/s2i/assemble
if [ -x "$local_scripts/assemble" ]; then
echo "RUN /tmp/scripts/assemble" >>"$df_name"
else
echo "RUN /usr/libexec/s2i/assemble" >>"$df_name"
fi
# If exists, set the custom run script as CMD, else default to /usr/libexec/s2i/run
if [ -x "$local_scripts/run" ]; then
echo "CMD /tmp/scripts/run" >>"$df_name"
else
echo "CMD /usr/libexec/s2i/run" >>"$df_name"
fi
# Check if -v parameter is present in s2i_args and add it into docker build command
mount_options=$(echo "$s2i_args" | grep -o -e '\(-v\)[[:space:]]\.*\S*' || true)
# Run the build and tag the result
# shellcheck disable=SC2086
docker build $mount_options -f "$df_name" --no-cache=true -t "$dst_image" .
)
}
# ct_check_image_availability PUBLIC_IMAGE_NAME
# ----------------------------
# Pull an image from the public repositories to see if the image is already available.
# Argument: PUBLIC_IMAGE_NAME - string containing the public name of the image to pull
ct_check_image_availability() {
local public_image_name=$1;
# Try pulling the image to see if it is accessible
if ! docker pull "$public_image_name" &>/dev/null; then
echo "$public_image_name could not be downloaded via 'docker'"
return 1
fi
}
# ct_check_latest_imagestreams
# -----------------------------
# Check if the latest version present in Makefile in the variable VERSIONS
# is present in all imagestreams.
# Also the latest tag in the imagestreams has to contain the latest version
ct_check_latest_imagestreams() {
local latest_version=
local test_lib_dir=
# We only maintain imagestreams for RHEL and CentOS (Community)
if [[ "$OS" =~ ^fedora.* ]] ; then
echo "Imagestreams for Fedora are not maintained, skipping ct_check_latest_imagestreams"
return 0
fi
# Check only lines which starts with VERSIONS
latest_version=$(grep '^VERSIONS' Makefile | rev | cut -d ' ' -f 1 | rev )
# Fall back to previous version if the latest is excluded for this OS
[ -f "$latest_version/.exclude-$OS" ] && latest_version=$(grep '^VERSIONS' Makefile | rev | cut -d ' ' -f 2 | rev )
# Only test the imagestream once, when the version matches
# ignore the SC warning, $VERSION is always available
# shellcheck disable=SC2153
if [ "$latest_version" == "$VERSION" ]; then
test_lib_dir=$(dirname "$(readlink -f "$0")")
python3 "${test_lib_dir}/check_imagestreams.py" "$latest_version"
else
echo "Image version $VERSION is not latest, skipping ct_check_latest_imagestreams"
fi
}
# ct_show_resources
# ----------------
# Prints the available resources
ct_show_resources()
{
echo "Resources info:"
echo "Memory:"
free -h
echo "Storage:"
df -h || :
echo "CPU"
lscpu
}
# ct_test_app_dockerfile
# -----------------------------
# Argument: dockerfile - path to a Dockerfile that will be used for building an image
# (must work with an application directory called 'app-src')
# Argument: app_url - git URI with a testing application, supports "@" to indicate a different branch
# Argument: body_regexp - PCRE regular expression that must match the response body
# Argument: app_dir - name of the application directory that is used in the Dockerfile
# Argument: port - Optional port number (default: 8080)
ct_test_app_dockerfile() {
local dockerfile=$1
local app_url=$2
local expected_text=$3
local app_dir=$4 # this is a directory that must match with the name in the Dockerfile
local port=${5:-8080}
local app_image_name=myapp
local ret
local cname=app_dockerfile
if [ -z "$app_dir" ] ; then
echo "ERROR: Option app_dir not set. Terminating the Dockerfile build."
return 1
fi
if ! [ -r "${dockerfile}" ] || ! [ -s "${dockerfile}" ] ; then
echo "ERROR: Dockerfile ${dockerfile} does not exist or is empty."
echo "Terminating the Dockerfile build."
return 1
fi
CID_FILE_DIR=${CID_FILE_DIR:-$(mktemp -d)}
local dockerfile_abs
dockerfile_abs=$(readlink -f "${dockerfile}")
tmpdir=$(mktemp -d)
pushd "$tmpdir" >/dev/null
cp "${dockerfile_abs}" Dockerfile
# Rewrite the source image to what we test
sed -i -e "s|^FROM.*$|FROM $IMAGE_NAME|" Dockerfile
# a bit more verbose, but should help debugging failures
echo "Using this Dockerfile:"
cat Dockerfile
# If app_url contains @, the string after @ is considered
# as a name of a branch to clone instead of the main/master branch
IFS='@' read -ra git_url_parts <<< "${app_url}"
if [ -n "${git_url_parts[1]}" ]; then
git_clone_cmd="git clone --branch ${git_url_parts[1]} ${git_url_parts[0]} ${app_dir}"
else
git_clone_cmd="git clone ${app_url} ${app_dir}"
fi
if ! $git_clone_cmd ; then
echo "ERROR: Git repository ${app_url} cannot be cloned into ${app_dir}."
echo "Terminating the Dockerfile build."
return 1
fi
echo "Building '${app_image_name}' image using docker build"
if ! docker build --no-cache=true -t "${app_image_name}" . ; then
echo "ERROR: The image cannot be built from ${dockerfile} and application ${app_url}."
echo "Terminating the Dockerfile build."
return 1
fi
if ! docker run -d --cidfile="${CID_FILE_DIR}/app_dockerfile" --rm "${app_image_name}" ; then
echo "ERROR: The image ${app_image_name} cannot be run for ${dockerfile} and application ${app_url}."
echo "Terminating the Dockerfile build."
return 1
fi
echo "Waiting for ${app_image_name} to start"
ct_wait_for_cid "${CID_FILE_DIR}/app_dockerfile"
ip="$(ct_get_cip "${cname}")"
ct_test_response "http://$ip:${port}" 200 "${expected_text}"
ret=$?
# cleanup
docker kill "$(ct_get_cid "${cname}")"
sleep 2
docker rmi "${app_image_name}"
popd >/dev/null
rm -rf "${tmpdir}"
rm -f "${CID_FILE_DIR}/${cname}"
return $ret
}
# vim: set tabstop=2:shiftwidth=2:expandtab:

View file

@ -1,29 +0,0 @@
#!/bin/bash
# First test virtualenv environment and pip upgrade
echo "Testing that the virtual environment's Python is being used ..."
if [ "$(which python)" != "/opt/app-root/bin/python" ]; then
echo "ERROR: Initialization of the virtual environment failed."
exit 1
fi
echo "Testing UPGRADE_PIP_TO_LATEST=1 (set in .s2i/environment) ..."
pip_major_version=$(pip --version | cut -d" " -f2 | cut -d"." -f1)
if [ -z "$pip_major_version" ] || [ "$pip_major_version" -lt "9" ]; then
echo "ERROR: Failed to upgrade pip to version 9 or later."
exit 1
fi
# Now test the uwsgi server
exec uwsgi \
--http-socket :8080 \
--die-on-term \
--master \
--single-interpreter \
--enable-threads \
--threads=5 \
--thunder-lock \
--module wsgi