diff --git a/3.8 b/3.8 new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/3.8 @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/Dockerfile.fedora b/Dockerfile.fedora new file mode 120000 index 0000000..1d1fe94 --- /dev/null +++ b/Dockerfile.fedora @@ -0,0 +1 @@ +Dockerfile \ No newline at end of file diff --git a/README.md b/README.md index 16fffe3..91c2b36 100644 --- a/README.md +++ b/README.md @@ -106,17 +106,43 @@ To use the Source-to-Image scripts and build an image using a Dockerfile, create ``` FROM registry.access.redhat.com/ubi8/python-38 + # 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 chown -R 1001:0 /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 ``` + +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: + +``` +FROM registry.access.redhat.com/ubi8/python-38 + +# 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 +``` + #### 4. Build a new image from a Dockerfile prepared in the previous step ``` diff --git a/help.md b/help.md new file mode 120000 index 0000000..42061c0 --- /dev/null +++ b/help.md @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/s2i/bin/run b/s2i/bin/run index ee1e1d3..b5c2441 100755 --- a/s2i/bin/run +++ b/s2i/bin/run @@ -48,25 +48,36 @@ APP_HOME=$(readlink -f "${APP_HOME:-.}") PYTHONPATH="$(pwd)${PYTHONPATH:+:$PYTHONPATH}" cd "$APP_HOME" -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" - fi - maybe_run_in_init_wrapper "$APP_SCRIPT" -else - test -n "$app_script_check" && (>&2 echo "ERROR: file '$app_script_check' not found.") && exit 1 +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) ..." - maybe_run_in_init_wrapper 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 + +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 diff --git a/s2i/bin/usage b/s2i/bin/usage index 9caff40..73353aa 100755 --- a/s2i/bin/usage +++ b/s2i/bin/usage @@ -6,13 +6,13 @@ NAMESPACE=centos cat < -- curl 127.0.0.1:8080 EOF diff --git a/test/app-module-test-app/.gitignore b/test/app-module-test-app/.gitignore new file mode 100644 index 0000000..ba74660 --- /dev/null +++ b/test/app-module-test-app/.gitignore @@ -0,0 +1,57 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/test/app-module-test-app/.s2i/environment b/test/app-module-test-app/.s2i/environment new file mode 100644 index 0000000..ac798d7 --- /dev/null +++ b/test/app-module-test-app/.s2i/environment @@ -0,0 +1 @@ +APP_MODULE=app:application diff --git a/test/app-module-test-app/app.py b/test/app-module-test-app/app.py new file mode 100644 index 0000000..09fee1b --- /dev/null +++ b/test/app-module-test-app/app.py @@ -0,0 +1,4 @@ + +def application(environ, start_response): + start_response('200 OK', [('Content-Type','text/plain')]) + return [b"Hello from gunicorn WSGI application!"] diff --git a/test/app-module-test-app/requirements.txt b/test/app-module-test-app/requirements.txt new file mode 100644 index 0000000..268bd60 --- /dev/null +++ b/test/app-module-test-app/requirements.txt @@ -0,0 +1,2 @@ +gunicorn<20.0.0; python_version < '3.5' +gunicorn>=20.0.0; python_version >= '3.5' \ No newline at end of file diff --git a/test/check_imagestreams.py b/test/check_imagestreams.py index 6da0f63..b0eb11b 100755 --- a/test/check_imagestreams.py +++ b/test/check_imagestreams.py @@ -1,5 +1,27 @@ #!/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 diff --git a/test/from-dockerfile/Dockerfile.tpl b/test/from-dockerfile/Dockerfile.tpl index 2a12b8c..a44d41d 100644 --- a/test/from-dockerfile/Dockerfile.tpl +++ b/test/from-dockerfile/Dockerfile.tpl @@ -4,7 +4,7 @@ FROM #IMAGE_NAME# # Replaced by sed in tests, see test_from_dockerfile in test/r # and set permissions so that the container runs without root access USER 0 ADD app-src /tmp/src -RUN chown -R 1001:0 /tmp/src +RUN /usr/bin/fix-permissions /tmp/src USER 1001 # Install the dependencies diff --git a/test/from-dockerfile/Dockerfile_no_s2i.tpl b/test/from-dockerfile/Dockerfile_no_s2i.tpl new file mode 100644 index 0000000..7f073a6 --- /dev/null +++ b/test/from-dockerfile/Dockerfile_no_s2i.tpl @@ -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 diff --git a/test/imagestreams/python-centos.json b/test/imagestreams/python-centos.json index 0f6aeae..1be8538 100644 --- a/test/imagestreams/python-centos.json +++ b/test/imagestreams/python-centos.json @@ -14,7 +14,7 @@ "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.8/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.", + "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", @@ -22,7 +22,27 @@ }, "from": { "kind": "ImageStreamTag", - "name": "3.8-ubi8" + "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" @@ -142,7 +162,7 @@ }, "from": { "kind": "DockerImage", - "name": "docker.io/centos/python-27-centos7:latest" + "name": "quay.io/centos7/python-27-centos7:latest" }, "referencePolicy": { "type": "Local" diff --git a/test/imagestreams/python-rhel-aarch64.json b/test/imagestreams/python-rhel-aarch64.json index 1c2ed27..a390935 100644 --- a/test/imagestreams/python-rhel-aarch64.json +++ b/test/imagestreams/python-rhel-aarch64.json @@ -14,7 +14,7 @@ "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.8/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.", + "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", @@ -22,7 +22,27 @@ }, "from": { "kind": "ImageStreamTag", - "name": "3.8-ubi8" + "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" diff --git a/test/imagestreams/python-rhel.json b/test/imagestreams/python-rhel.json index ce2e080..7522c80 100644 --- a/test/imagestreams/python-rhel.json +++ b/test/imagestreams/python-rhel.json @@ -14,7 +14,7 @@ "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.8/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.", + "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", @@ -22,7 +22,27 @@ }, "from": { "kind": "ImageStreamTag", - "name": "3.8-ubi8" + "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" diff --git a/test/npm-virtualenv-uwsgi-test-app/app.sh b/test/npm-virtualenv-uwsgi-test-app/app.sh index f34357a..5b7a273 100755 --- a/test/npm-virtualenv-uwsgi-test-app/app.sh +++ b/test/npm-virtualenv-uwsgi-test-app/app.sh @@ -13,7 +13,7 @@ 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 -v "^Requirement already up-to-date: " + 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 diff --git a/test/run b/test/run index 42a52d5..d14e9a7 100755 --- a/test/run +++ b/test/run @@ -6,12 +6,28 @@ # IMAGE_NAME specifies a name of the candidate image used for testing. # The image has to be available before this script is executed. # -declare -a WEB_APPS=({standalone,setup,setup-requirements,django,numpy,app-home,npm-virtualenv-uwsgi,locale,mod-wsgi,pipenv,pipenv-and-micropipenv-should-fail,pin-pipenv-version,micropipenv,micropipenv-requirements}-test-app) +declare -a WEB_APPS=({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}/..) +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 @@ -82,7 +98,7 @@ cleanup() { rm -rf ${test_dir}/${1}/.git } -check_result() { +check_type() { # positive test & non-zero exit status = ERROR # negative test & zero exit status = ERROR local result="$1" @@ -90,12 +106,22 @@ check_result() { if [[ "$type" == "positive" && "$result" != "0" ]]; then info "TEST FAILED (${type}), EXPECTED:0 GOT:${result}" cleanup - exit $result + return $result elif [[ "$type" == "negative" && "$result" == "0" ]]; then info "TEST FAILED (${type}), EXPECTED: non-zero GOT:${result}" cleanup - exit 1 + 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 + TESTCASE_RESULT=1 + fi + return $result } wait_for_cid() { @@ -189,65 +215,114 @@ test_application() { 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. + + 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 + 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 'https://github.com/sclorg/django-ex.git' 'Welcome to your Django application on OpenShift' app-src + ct_test_app_dockerfile $test_dir/from-dockerfile/Dockerfile $django_example_repo_url 'Welcome to your Django application on OpenShift' app-src + check_result $? + + 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 $? } -# Since we built the candidate image locally, we don't want S2I attempt to pull -# it from Docker hub -s2i_args="--pull-policy=never" +test_application_with_user() { + # test application with random user + CONTAINER_ARGS="--user 12345" test_application -# 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 $? +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" + 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_result $RESULT "negative" + 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_result $RESULT + 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}" - # test application with default user - test_application - - # test application with random user - CONTAINER_ARGS="--user 12345" test_application - - # test application with init wrapper - CONTAINER_ARGS="-e ENABLE_INIT_WRAPPER=true" test_application - - info "All tests for the ${app} finished successfully." cleanup ${app} done -if [ "$OS" == "rhel7" ] || [ "$OS" == "centos7" ]; then - # autocleanup only enabled here as only the following tests so far use it - CID_FILE_DIR=$(mktemp -d) - ct_enable_cleanup +TEST_SET=${TESTS:-$TEST_VAR_DOCKER} run_all_tests - info "Testing variable presence during \`docker exec\`" - ct_check_exec_env_vars - check_result $? +echo "$test_short_summary" - info "Checking if all scl variables are defined in Dockerfile" - ct_check_scl_enable_vars - check_result $? +if [ $TESTSUITE_RESULT -eq 0 ] ; then + echo "Tests for ${IMAGE_NAME} succeeded." +else + echo "Tests for ${IMAGE_NAME} failed." fi -test_from_dockerfile - -info "All tests finished successfully." +exit $TESTSUITE_RESULT diff --git a/test/run-openshift b/test/run-openshift index 35d3b7d..76d36e8 100755 --- a/test/run-openshift +++ b/test/run-openshift @@ -46,8 +46,8 @@ for template in $EPHEMERAL_TEMPLATES; do "$template" \ python \ 'Welcome to your Django application on OpenShift' \ - 8080 http 200 "-p SOURCE_REPOSITORY_REF=master -p PYTHON_VERSION=${VERSION} -p POSTGRESQL_VERSION=9.6 -p NAME=python-testing" \ - "centos/postgresql-96-centos7|postgresql:9.6" + 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 diff --git a/test/run-openshift-remote-cluster b/test/run-openshift-remote-cluster new file mode 100755 index 0000000..b810752 --- /dev/null +++ b/test/run-openshift-remote-cluster @@ -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: + diff --git a/test/test-lib-openshift.sh b/test/test-lib-openshift.sh index c11a025..1135ad7 100644 --- a/test/test-lib-openshift.sh +++ b/test/test-lib-openshift.sh @@ -617,20 +617,21 @@ function ct_os_test_s2i_app_func() { ct_os_new_project namespace=${CT_NAMESPACE:-"$(oc project -q)"} + local image_tagged="${image_name_no_namespace%:*}:${VERSION}" if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then - ct_os_import_image_ocp4 "${image_name}" "${image_name_no_namespace}" + ct_os_import_image_ocp4 "${image_name}" "${image_tagged}" else # Create a specific imagestream tag for the image so that oc cannot use anything else if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then - echo "Importing image ${image_name} as ${namespace}/${image_name_no_namespace}" + echo "Importing image ${image_name} as ${namespace}/${image_tagged}" # Use --reference-policy=local to pull remote image content to the cluster # Works around the issue of builder pods not having access to registry.redhat.io - oc tag --source=docker "${image_name}" "${namespace}/${image_name_no_namespace}" --insecure=true --reference-policy=local - ct_os_wait_stream_ready "${image_name_no_namespace}" "${namespace}" + oc tag --source=docker "${image_name}" "${namespace}/${image_tagged}" --insecure=true --reference-policy=local + ct_os_wait_stream_ready "${image_tagged}" "${namespace}" else - echo "Uploading image ${image_name} as ${image_name_no_namespace}" - ct_os_upload_image "${image_name}" "${image_name_no_namespace}" + echo "Uploading image ${image_name} as ${image_tagged}" + ct_os_upload_image "${image_name}" "${image_tagged}" fi fi @@ -642,7 +643,7 @@ function ct_os_test_s2i_app_func() { fi # shellcheck disable=SC2086 - ct_os_deploy_s2i_image "${image_name_no_namespace}" "${app_param}" \ + ct_os_deploy_s2i_image "${image_tagged}" "${app_param}" \ --context-dir="${context_dir}" \ --name "${service_name}" \ ${oc_args} @@ -793,6 +794,8 @@ function ct_os_test_template_app_func() { local local_template local_template=$(ct_obtain_input "${template}" 2>/dev/null || echo "--template=${template}") + + echo "Creating a new-app with name ${name_in_template} in namespace ${namespace} with args ${oc_args}." # shellcheck disable=SC2086 oc new-app "${local_template}" \ --name "${name_in_template}" \ @@ -928,7 +931,7 @@ ct_os_test_image_update() { function ct_os_deploy_cmd_image() { local image_name=${1} oc get pod command-app &>/dev/null && echo "command POD already running" && return 0 - echo "command POD not running yet, will start one called command-app" + echo "command POD not running yet, will start one called command-app ${image_name}" oc create -f - </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 +} diff --git a/test/test-lib.sh b/test/test-lib.sh index b2e092e..cc046a9 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -84,11 +84,12 @@ function ct_check_envs_set { 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=")" - IFS=$old_IFS } # ct_get_cid [name] @@ -817,7 +818,7 @@ ct_show_resources() echo "Memory:" free -h echo "Storage:" - df -h + df -h || : echo "CPU" lscpu } @@ -826,7 +827,7 @@ ct_show_resources() # ----------------------------- # 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 +# 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) @@ -864,7 +865,17 @@ ct_test_app_dockerfile() { echo "Using this Dockerfile:" cat Dockerfile - if ! git clone "${app_url}" "${app_dir}" ; then + # 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