diff --git a/Dockerfile b/Dockerfile index 9b7dcd9..f89d361 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # This image provides a Python 3.9 environment you can use to run your Python # applications. -FROM registry.fedoraproject.org/f34/s2i-base:latest +FROM quay.io/fedora/s2i-base:latest EXPOSE 8080 @@ -33,9 +33,9 @@ LABEL summary="$SUMMARY" \ io.openshift.expose-services="8080:http" \ io.openshift.tags="builder,python,python39,python-39,rh-python39" \ com.redhat.component="$NAME" \ - name="$FGC/$NAME" \ + name="fedora/$NAME-39" \ version="$VERSION" \ - usage="s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.9/test/setup-test-app/ $FGC/$NAME python-sample-app" \ + usage="s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.9/test/setup-test-app/ quay.io/fedora/$NAME-39 python-sample-app" \ maintainer="SoftwareCollections.org " RUN INSTALL_PKGS="python3 python3-devel python3-setuptools python3-pip nss_wrapper \ diff --git a/Dockerfile.c9s b/Dockerfile.c9s new file mode 100644 index 0000000..db87461 --- /dev/null +++ b/Dockerfile.c9s @@ -0,0 +1,90 @@ +# This image provides a Python 3.9 environment you can use to run your Python +# applications. +FROM quay.io/sclorg/s2i-base-c9s:c9s + +EXPOSE 8080 + +ENV PYTHON_VERSION=3.9 \ + PATH=$HOME/.local/bin/:$PATH \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + LC_ALL=en_US.UTF-8 \ + 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 container is a base platform for \ +building and running various Python $PYTHON_VERSION applications and frameworks. \ +Python is an easy to learn, powerful programming language. It has efficient high-level \ +data structures and a simple but effective approach to object-oriented programming. \ +Python's elegant syntax and dynamic typing, together with its interpreted nature, \ +make it an ideal language for scripting and rapid application development in many areas \ +on most platforms." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$DESCRIPTION" \ + io.k8s.display-name="Python 3.9" \ + io.openshift.expose-services="8080:http" \ + io.openshift.tags="builder,python,python39,python-39,rh-python39" \ + com.redhat.component="$NAME" \ + name="sclorg/python-39-c9s" \ + version="$VERSION" \ + 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 " + +RUN INSTALL_PKGS="python3 python3-devel python3-setuptools python3-pip nss_wrapper \ + httpd httpd-devel mod_ssl mod_auth_gssapi mod_ldap \ + mod_session atlas-devel gcc-gfortran libffi-devel libtool-ltdl enchant" && \ + yum -y --setopt=tsflags=nodocs install $INSTALL_PKGS && \ + rpm -V $INSTALL_PKGS && \ + yum -y clean all --enablerepo='*' + +# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH. +COPY 3.9/s2i/bin/ $STI_SCRIPTS_PATH + +# Copy extra files to the image. +COPY 3.9/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 + +# 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 + +# Set the default CMD to print the usage of the language image. +CMD $STI_SCRIPTS_PATH/usage diff --git a/test/check_imagestreams.py b/test/check_imagestreams.py index d759277..a10476b 100755 --- a/test/check_imagestreams.py +++ b/test/check_imagestreams.py @@ -49,6 +49,9 @@ class ImageStreamChecker(object): def check_version(self, json_dict: Dict[Any, Any]) -> List[str]: res = [] for tags in json_dict["spec"]["tags"]: + print( + f"check_version: Compare tags['name']:'{tags['name']}' against version:'{self.version}'" + ) # The name can be"" or "-elX" or "-ubiX" if tags["name"] == self.version or tags["name"].startswith( self.version + "-" @@ -61,11 +64,15 @@ class ImageStreamChecker(object): for tags in json_dict["spec"]["tags"]: if tags["name"] != "latest": continue + print( + f"check_latest_tag: Compare tags['name']:'{tags['name']}' against version:'{self.version}'" + ) # The latest can link to either "" or "-elX" or "-ubiX" if tags["from"]["name"] == self.version or tags["from"]["name"].startswith( self.version + "-" ): latest_tag_correct = True + print(f"Latest tag found.") return latest_tag_correct def check_imagestreams(self) -> int: diff --git a/test/imagestreams/python-centos.json b/test/imagestreams/python-centos.json index 405c06e..12a0a1f 100644 --- a/test/imagestreams/python-centos.json +++ b/test/imagestreams/python-centos.json @@ -28,6 +28,26 @@ "type": "Local" } }, + { + "name": "3.9-ubi9", + "annotations": { + "openshift.io/display-name": "Python 3.9 (UBI 9)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "description": "Build and run Python 3.9 applications on UBI 9. 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/ubi9/python-39:latest" + }, + "referencePolicy": { + "type": "Local" + } + }, { "name": "3.9-ubi8", "annotations": { @@ -127,46 +147,6 @@ "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" - } } ] } diff --git a/test/imagestreams/python-rhel-aarch64.json b/test/imagestreams/python-rhel-aarch64.json index f1400c5..87eed64 100644 --- a/test/imagestreams/python-rhel-aarch64.json +++ b/test/imagestreams/python-rhel-aarch64.json @@ -28,6 +28,26 @@ "type": "Local" } }, + { + "name": "3.9-ubi9", + "annotations": { + "openshift.io/display-name": "Python 3.9 (UBI 9)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "description": "Build and run Python 3.9 applications on UBI 9. 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/ubi9/python-39:latest" + }, + "referencePolicy": { + "type": "Local" + } + }, { "name": "3.9-ubi8", "annotations": { diff --git a/test/imagestreams/python-rhel.json b/test/imagestreams/python-rhel.json index 92f545f..6954ba8 100644 --- a/test/imagestreams/python-rhel.json +++ b/test/imagestreams/python-rhel.json @@ -28,6 +28,26 @@ "type": "Local" } }, + { + "name": "3.9-ubi9", + "annotations": { + "openshift.io/display-name": "Python 3.9 (UBI 9)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "description": "Build and run Python 3.9 applications on UBI 9. 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/ubi9/python-39:latest" + }, + "referencePolicy": { + "type": "Local" + } + }, { "name": "3.9-ubi8", "annotations": { @@ -147,46 +167,6 @@ "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" - } } ] } diff --git a/test/run b/test/run index 3a51db2..af99aad 100755 --- a/test/run +++ b/test/run @@ -8,6 +8,13 @@ # declare -a WEB_APPS=({gunicorn-config-different-port,gunicorn-different-port,django-different-port,standalone,setup,setup-cfg,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) +# Some tests, like the one using the latest pipenv, might be unstable +# because new upstream releases tend to break our tests sometimes. +# If a test is in UNSTABLE_TESTS and IGNORE_UNSTABLE_TESTS env +# variable is defined, a result of the test has no impact on +# the overall result of the test suite. +declare -a UNSTABLE_TESTS=(pipenv-test-app) + # TODO: Make command compatible for Mac users test_dir="$(readlink -f $(dirname "${BASH_SOURCE[0]}"))" image_dir=$(readlink -f ${test_dir}/..) @@ -37,6 +44,8 @@ IMAGE_NAME=${IMAGE_NAME:-centos/python-${VERSION//./}-centos7} . test/test-lib.sh +ct_enable_cleanup + info() { echo -e "\n\e[1m[INFO] $@\e[0m\n" } @@ -245,9 +254,7 @@ test_application_enable_init_wrapper() { 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 @@ -259,28 +266,6 @@ test_scl_variables_in_dockerfile() { 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 @@ -312,19 +297,10 @@ for app in ${@:-${WEB_APPS[@]}}; do 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_SET=${TESTS:-$TEST_LIST} ct_run_tests_from_testset "${app}" cleanup ${app} done -TEST_SET=${TESTS:-$TEST_VAR_DOCKER} run_all_tests +TEST_SET=${TESTS:-$TEST_VAR_DOCKER} ct_run_tests_from_testset "var-docker" -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 diff --git a/test/run-openshift-remote-cluster b/test/run-openshift-remote-cluster index 80e9a9c..4f2cc1c 100755 --- a/test/run-openshift-remote-cluster +++ b/test/run-openshift-remote-cluster @@ -13,8 +13,14 @@ source "${THISDIR}/test-lib-openshift.sh" source "${THISDIR}/test-lib-python.sh" source "${THISDIR}/test-lib-remote-openshift.sh" -set -eo nounset +TEST_LIST="\ +test_python_s2i_app_ex_standalone +test_python_s2i_app_ex +test_python_imagestream +test_python_s2i_templates +" +set -u # Pull image before going throw tests # Exit in case of failure ct_pull_image "quay.io/centos7/postgresql-10-centos7" "true" @@ -28,45 +34,14 @@ 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 +export CT_OCP4_TEST=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!" - -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 - -ct_os_test_s2i_app "${IMAGE_NAME}" "${django_example_repo_url}" . 'Welcome to your Django application on OpenShift' - -for template in $EPHEMERAL_TEMPLATES; do - if [[ ${VERSION} == "2.7" ]] || docker inspect ${IMAGE_NAME} --format "{{.Config.Env}}" | tr " " "\n" | grep -q "^PLATFORM=el7"; then - branch="master" - else - branch="2.2.x" - fi - ct_os_test_template_app "$IMAGE_NAME" \ - "$template" \ - python \ - 'Welcome to your Django application on OpenShift' \ - 8080 http 200 "-p SOURCE_REPOSITORY_REF=$branch -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 - -OS_TESTSUITE_RESULT=0 +TEST_SUMMARY='' +TEST_SET=${TESTS:-$TEST_LIST} ct_run_tests_from_testset "openshift-remote-cluster" # vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/test/test-lib-openshift.sh b/test/test-lib-openshift.sh index 96cae8d..71c137c 100644 --- a/test/test-lib-openshift.sh +++ b/test/test-lib-openshift.sh @@ -5,22 +5,23 @@ 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 +# A variable containing the overall test result +# TESTSUITE_RESULT=0 # And the following trap must be set, in the beginning of the test script: # trap ct_os_cleanup EXIT SIGINT -OS_TESTSUITE_RESULT=1 +TESTSUITE_RESULT=0 OS_CLUSTER_STARTED_BY_TEST=0 function ct_os_cleanup() { - echo "${test_short_summary:-}" - if [ $OS_TESTSUITE_RESULT -eq 0 ] ; then + echo "${TEST_SUMMARY:-}" + if [ $TESTSUITE_RESULT -eq 0 ] ; then # shellcheck disable=SC2153 echo "OpenShift tests for ${IMAGE_NAME} succeeded." + exit 0 else # shellcheck disable=SC2153 echo "OpenShift tests for ${IMAGE_NAME} failed." + exit 1 fi } @@ -45,6 +46,7 @@ function ct_os_check_compulsory_vars() { function ct_os_get_status() { oc get all oc status + oc status --suggest } # ct_os_print_logs @@ -398,13 +400,13 @@ function ct_delete_all_objects() { sleep 10 } -# ct_os_docker_login +# ct_os_docker_login_v3 # -------------------- # Logs in into docker daemon # Uses global REGISRTY_ADDRESS environment variable for arbitrary registry address. # Does not do anything if REGISTRY_ADDRESS is set. -function ct_os_docker_login() { - [ -n "${REGISTRY_ADDRESS:-}" ] && "REGISTRY_ADDRESS set, not trying to docker login." && return 0 +function ct_os_docker_login_v3() { + [ -n "${REGISTRY_ADDRESS:-}" ] && echo "REGISTRY_ADDRESS set, not trying to docker login." && return 0 # docker login fails with "404 page not found" error sometimes, just try it more times # shellcheck disable=SC2034 for i in $(seq 12) ; do @@ -415,6 +417,26 @@ function ct_os_docker_login() { return 1 } +# ct_os_docker_login_v4 +# -------------------- +# Logs in into docker daemon +# Uses global REGISRTY_ADDRESS environment variable for arbitrary registry address. +# Does not do anything if REGISTRY_ADDRESS is set. +function ct_os_docker_login_v4() { + OCP4_REGISTER=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}') + echo "OCP4 loging address is $OCP4_REGISTER." + if [ -z "${OCP4_REGISTER}" ]; then + echo "!!!OpenShift 4 registry address not found. This is an error. Check OpenShift 4 cluster!!!" + return 1 + fi + + if docker login -u kubeadmin -p "$(oc whoami -t)" "${OCP4_REGISTER}"; then + echo "Login to $OCP4_REGISTER was successfully." + return 0 + fi + return 1 +} + # ct_os_upload_image IMAGE [IMAGESTREAM] # -------------------- # Uploads image from local registry to the OpenShift internal registry. @@ -423,15 +445,35 @@ function ct_os_docker_login() { # In the format of name:tag ($image_name:latest by default) # Uses global REGISRTY_ADDRESS environment variable for arbitrary registry address. function ct_os_upload_image() { + local os_version="${1}" ; shift local input_name="${1}" ; shift - local image_name=${input_name##*/} - local imagestream=${1:-$image_name:latest} + local image_name=${1} local output_name + local source_name - output_name="${REGISRTY_ADDRESS:-172.30.1.1:5000}/$(oc project -q)/$imagestream" + if [ "${os_version}" != "v3" ] && [ "${os_version}" != "v4" ]; then + echo "You have to specify OpenShift version to upload an image." + echo "Either 'v3' or 'v4' is allowed" + return 1 + fi - ct_os_docker_login - docker tag "${input_name}" "${output_name}" + source_name="${input_name}" + if [ "${os_version}" == "v3" ]; then + output_name="${REGISRTY_ADDRESS:-172.30.1.1:5000}/$(oc project -q)/$image_name" + + if ! ct_os_docker_login_v3; then + return 1 + fi + + fi + if [ "${os_version}" == "v4" ]; then + # Variable OCP4_REGISTER is set in function ct_os_docker_login_v4 + if ! ct_os_docker_login_v4; then + return 1 + fi + output_name="$OCP4_REGISTER/$namespace/$image_name" + fi + docker tag "${source_name}" "${output_name}" docker push "${output_name}" } @@ -673,8 +715,9 @@ function ct_os_test_s2i_app_func() { local image_tagged="${image_name_no_namespace%:*}:${VERSION}" if [ "${CVP:-0}" -eq "0" ]; then - if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then - ct_os_import_image_ocp4 "${image_name}" "${image_tagged}" + if [ "${CT_OCP4_TEST:-false}" == 'true' ] ; then + echo "Uploading image ${image_name} as ${image_tagged} into OpenShift internal registry." + ct_os_upload_image "v4" "${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 @@ -685,7 +728,7 @@ function ct_os_test_s2i_app_func() { ct_os_wait_stream_ready "${image_tagged}" "${namespace}" else echo "Uploading image ${image_name} as ${image_tagged}" - ct_os_upload_image "${image_name}" "${image_tagged}" + ct_os_upload_image "v3" "${image_name}" "${image_tagged}" fi fi else @@ -817,8 +860,9 @@ function ct_os_test_template_app_func() { # Upload main image is already done by CVP pipeline. No need to do it twice. if [ "${CVP:-0}" -eq "0" ]; then # Create a specific imagestream tag for the image so that oc cannot use anything else - if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then - ct_os_import_image_ocp4 "${image_name}" "${image_tagged}" + if [ "${CT_OCP4_TEST:-false}" == 'true' ] ; then + echo "Uploading image ${image_name} as ${image_tagged} into OpenShift internal registry." + ct_os_upload_image "v4" "${image_name}" "${image_tagged}" else if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then echo "Importing image ${image_name} as ${image_tagged}" @@ -828,7 +872,7 @@ function ct_os_test_template_app_func() { ct_os_wait_stream_ready "${image_tagged}" "${namespace}" else echo "Uploading image ${image_name} as ${image_tagged}" - ct_os_upload_image "${image_name}" "${image_tagged}" + ct_os_upload_image "v3" "${image_name}" "${image_tagged}" fi fi else @@ -850,10 +894,11 @@ function ct_os_test_template_app_func() { exit 1 fi - if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then - ct_os_import_image_ocp4 "${image_tag_a[0]}" "${image_tag_a[1]}" + if [ "${CT_OCP4_TEST:-false}" == 'true' ] ; then + echo "Uploading image ${image_tag_a[0]} as ${image_tag_a[1]} into OpenShift internal registry." + ct_os_upload_image "v4" "${image_tag_a[0]}" "${image_tag_a[1]}" else - ct_os_upload_image "${image_tag_a[0]}" "${image_tag_a[1]}" + ct_os_upload_image "v3" "${image_tag_a[0]}" "${image_tag_a[1]}" fi done fi @@ -968,7 +1013,7 @@ ct_os_test_image_update() { # Get current image from repository and create an imagestream docker pull "$old_image:latest" 2>/dev/null - ct_os_upload_image "$old_image" "$istag" + ct_os_upload_image "v3" "$old_image" "$istag" # Setup example application with curent image oc new-app "$@" --name "$service_name" @@ -980,7 +1025,7 @@ ct_os_test_image_update() { ct_assert_cmd_success "$check_command_exp" # Tag built image into the imagestream and wait for rebuild - ct_os_upload_image "$image_name" "$istag" + ct_os_upload_image "v3" "$image_name" "$istag" ct_os_wait_pod_ready "${service_name}-2" 60 # Check application output diff --git a/test/test-lib-python.sh b/test/test-lib-python.sh index 09f0baf..b1f7f2e 100644 --- a/test/test-lib-python.sh +++ b/test/test-lib-python.sh @@ -26,6 +26,45 @@ function test_python_imagestream() { 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" } +function test_python_s2i_app_ex_standalone() { + 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!" +} +function test_python_s2i_app_ex() { + 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 + ct_os_test_s2i_app "${IMAGE_NAME}" \ + "${django_example_repo_url}" \ + . \ + 'Welcome to your Django application on OpenShift' +} + + +function test_python_s2i_templates() { + 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 + for template in $EPHEMERAL_TEMPLATES; do + if [[ ${VERSION} == "2.7" ]] || docker inspect ${IMAGE_NAME} --format "{{.Config.Env}}" | tr " " "\n" | grep -q "^PLATFORM=el7"; then + branch="master" + else + branch="2.2.x" + fi + ct_os_test_template_app "$IMAGE_NAME" \ + "$template" \ + python \ + 'Welcome to your Django application on OpenShift' \ + 8080 http 200 "-p SOURCE_REPOSITORY_REF=$branch -p PYTHON_VERSION=${VERSION} -p POSTGRESQL_VERSION=10 -p NAME=python-testing" \ + "quay.io/centos7/postgresql-10-centos7|postgresql:10" + done +} # vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/test/test-lib-remote-openshift.sh b/test/test-lib-remote-openshift.sh index bda03f6..e7b9e06 100644 --- a/test/test-lib-remote-openshift.sh +++ b/test/test-lib-remote-openshift.sh @@ -5,9 +5,8 @@ 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 +# A variable containing the overall test result +# TESTSUITE_RESULT=0 # And the following trap must be set, in the beginning of the test script: # trap ct_os_cleanup EXIT SIGINT @@ -32,13 +31,6 @@ function ct_os_set_path_oc_4() { 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 @@ -55,10 +47,15 @@ function ct_os_set_ocp4() { 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" + oc version + if ! oc version | grep -q "Client Version: ${OS_OC_CLIENT_VERSION}." ; then + echo "ERROR: something went wrong, oc located at ${oc_path}, but oc of version ${OS_OC_CLIENT_VERSION} not found in PATH ($PATH)" >&1 + return 1 + else + echo "PATH set correctly, binary oc found in version ${OS_OC_CLIENT_VERSION}: $(command -v oc)" + fi echo "Login to OpenShift ${OS_OC_CLIENT_VERSION} is DONE" # let openshift cluster to sync to avoid some race condition errors sleep 3 @@ -80,37 +77,28 @@ function ct_os_upload_image_external_registry() { } -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 + echo "Uploading image ${image_name} as ${imagestream} into OpenShift internal registry." + ct_os_upload_image_v4 "${image_name}" "${imagestream}" + +} + +# ct_os_check_login +# --------------- +# function checks if the login to openshift was successful +# if successful returns 0 +# if not, write error message, sets test result to 1 +# and exits with non-zero +# Uses: $TESTSUITE_RESULT - overall result of all tests +function ct_os_check_login() { + oc status || { + echo "-------------------------------------------" + echo "It looks like oc is not properly logged in." + # shellcheck disable=SC2034 + TESTSUITE_RESULT=1 + return 1 + } } diff --git a/test/test-lib.sh b/test/test-lib.sh index e954053..e98bf08 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -16,6 +16,11 @@ # may be redefined in the specific container testfile EXPECTED_EXIT_CODE=0 +export TESTSUITE_RESULT=0 + +# define UNSTABLE_TESTS if not already defined, as this variable +# is not mandatory for containers +UNSTABLE_TESTS="${UNSTABLE_TESTS:-""}" # ct_cleanup # -------------------- @@ -24,6 +29,7 @@ EXPECTED_EXIT_CODE=0 # 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 +# Uses: $TESTSUITE_RESULT - overall result of all tests function ct_cleanup() { ct_show_resources for cid_file in "$CID_FILE_DIR"/* ; do @@ -33,16 +39,47 @@ function ct_cleanup() { : "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" + + # Container has not been removed by `docker stop` and still exists + if [ "$( docker ps -a -f "id=$container" | wc -l )" -eq 2 ]; then + 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" fi - docker rm -v "$container" + rm "$cid_file" done rmdir "$CID_FILE_DIR" : "Done." + + ct_show_results + exit "${TESTSUITE_RESULT:-0}" +} + +# ct_show_results +# --------------- +# Prints results of all test cases that are stored into TEST_SUMMARY variable. +# Uses: $TEST_SUMMARY - text info about test-cases +# Uses: $TESTSUITE_RESULT - overall result of all tests +function ct_show_results() { + echo + echo "===============================================" + echo "Test cases results:" + echo + echo "${TEST_SUMMARY:-}" + + if [ -n "${TESTSUITE_RESULT:-}" ] ; then + if [ "$TESTSUITE_RESULT" -eq 0 ] ; then + # shellcheck disable=SC2153 + echo "Tests for ${IMAGE_NAME} succeeded." + else + # shellcheck disable=SC2153 + echo "Tests for ${IMAGE_NAME} failed." + fi + fi } # ct_enable_cleanup @@ -125,6 +162,7 @@ function ct_check_envs_set { 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 + # shellcheck disable=SC2295 if [ -n "${filtered_envs##${env_format//VALUE/$value}}" ]; then echo " Value $value is missing from variable $var_name" echo "$filtered_envs" @@ -284,7 +322,7 @@ function ct_doc_content_old() { 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 + if ! grep -E -q -e "${term}" "${tmpdir}/$(basename "${f}")" ; then echo "ERROR: File /${f} does not include '${term}'." >&2 return 1 fi @@ -489,21 +527,6 @@ ct_path_foreach () } -# 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. @@ -631,10 +654,14 @@ ct_get_public_image_name() { public_image_name=$registry/rhscl/$base_image_name-${version//./}-rhel7 elif [ "$os" == "rhel8" ]; then public_image_name=$registry/rhel8/$base_image_name-${version//./} + elif [ "$os" == "rhel9" ]; then + public_image_name=$registry/rhel9/$base_image_name-${version//./} elif [ "$os" == "centos7" ]; then public_image_name=$registry/centos7/$base_image_name-${version//./}-centos7 - elif [ "$os" == "centos8" ]; then - public_image_name=$registry/centos8/$base_image_name-${version//./}-centos8 + elif [ "$os" == "c8s" ]; then + public_image_name=$registry/sclorg/$base_image_name-${version//./}-c8s + elif [ "$os" == "c9s" ]; then + public_image_name=$registry/sclorg/$base_image_name-${version//./}-c9s fi echo "$public_image_name" @@ -646,6 +673,7 @@ ct_get_public_image_name() { # Argument: CMD - Command to be run function ct_assert_cmd_success() { echo "Checking '$*' for success ..." + # shellcheck disable=SC2294 if ! eval "$@" &>/dev/null; then echo " FAIL" return 1 @@ -660,6 +688,7 @@ function ct_assert_cmd_success() { # Argument: CMD - Command to be run function ct_assert_cmd_failure() { echo "Checking '$*' for failure ..." + # shellcheck disable=SC2294 if eval "$@" &>/dev/null; then echo " FAIL" return 1 @@ -935,6 +964,7 @@ ct_check_latest_imagestreams() { # Prints the available resources ct_show_resources() { + echo echo "Resources info:" echo "Memory:" free -h @@ -1059,6 +1089,8 @@ ct_test_app_dockerfile() { ct_test_response "http://$ip:${port}" 200 "${expected_text}" ret=$? + [[ $ret -eq 0 ]] || docker logs "$(ct_get_cid "${cname}")" + # cleanup docker kill "$(ct_get_cid "${cname}")" sleep 2 @@ -1069,4 +1101,101 @@ ct_test_app_dockerfile() { return $ret } +# ct_check_testcase_result +# ----------------------------- +# Check if testcase ended in success or error +# Argument: result - testcase result value +# Uses: $TESTCASE_RESULT - result of the testcase +# Uses: $IMAGE_NAME - name of the image being tested +ct_check_testcase_result() { + local result="$1" + if [[ "$result" != "0" ]]; then + echo "Test for image '${IMAGE_NAME}' FAILED (exit code: ${result})" + TESTCASE_RESULT=1 + fi + return "$result" +} + +# ct_run_tests_from_testset +# ----------------------------- +# Runs all tests in $TEST_SET, prints result to +# the $TEST_SUMMARY variable +# Argument: app_name - application name to log +# Uses: $TEST_SET - set of test cases to run +# Uses: $TEST_SUMMARY - variable for storing test results +# Uses: $IMAGE_NAME - name of the image being tested +# Uses: $UNSTABLE_TESTS - set of tests, whose result can be ignored +# Uses: $IGNORE_UNSTABLE_TESTS - flag to ignore unstable tests +ct_run_tests_from_testset() { + local app_name="${1:-appnamenotset}" + local time_beg_pretty + local time_beg + local time_end + local time_diff + local test_msg + local is_unstable + + # Let's store in the log what change do we test + echo + git show -s + echo + + for test_case in $TEST_SET; do + TESTCASE_RESULT=0 + # shellcheck disable=SC2076 + if [[ " ${UNSTABLE_TESTS[*]} " =~ " ${app_name} " ]]; then + is_unstable=1 + else + is_unstable=0 + fi + time_beg_pretty=$(ct_timestamp_pretty) + time_beg=$(ct_timestamp_s) + echo "-----------------------------------------------" + echo "Running test $test_case (starting at $time_beg_pretty) ... " + echo "-----------------------------------------------" + $test_case + ct_check_testcase_result $? + time_end=$(ct_timestamp_s) + if [ $TESTCASE_RESULT -eq 0 ]; then + test_msg="[PASSED]" + else + if [ -n "${IGNORE_UNSTABLE_TESTS:-""}" ] && [ $is_unstable -eq 1 ]; then + test_msg="[FAILED][UNSTABLE-IGNORED]" + else + test_msg="[FAILED]" + TESTSUITE_RESULT=1 + fi + fi + time_diff=$(ct_timestamp_diff "$time_beg" "$time_end") + printf -v TEST_SUMMARY "%s %s for '%s' %s (%s)\n" "${TEST_SUMMARY:-}" "${test_msg}" "${app_name}" "$test_case" "$time_diff" + [ -n "${FAIL_QUICKLY:-}" ] && return 1 + done; +} + +# ct_timestamp_s +# -------------- +# Returns timestamp in seconds since unix era -- a large integer +function ct_timestamp_s() { + date '+%s' +} + +# ct_timestamp_pretty +# ----------------- +# Returns timestamp readable to a human, like 2022-05-18 10:52:44+02:00 +function ct_timestamp_pretty() { + date --rfc-3339=seconds +} + +# ct_timestamp_diff +# ----------------- +# Computes a time diff between two timestamps +# Argument: start_date - Beginning (in seconds since unix era -- a large integer) +# Argument: final_date - End (in seconds since unix era -- a large integer) +# Returns: Time difference in format HH:MM:SS +function ct_timestamp_diff() { + local start_date=$1 + local final_date=$2 + date -u -d "0 $final_date seconds - $start_date seconds" +"%H:%M:%S" +} + # vim: set tabstop=2:shiftwidth=2:expandtab: