diff --git a/README.md b/README.md index 13e9564..fb03f55 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ Python 3.9 container 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 RHEL images are available in the [Red Hat Container Catalog](https://access.redhat.com/containers/), -the CentOS images are available on [Docker Hub](https://hub.docker.com/r/centos/), +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). @@ -29,28 +29,104 @@ modules for their web applications. There is no guarantee for any specific npm o 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 ---------------------- +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/s2i-python-container/tree/master/3.9/test/setup-test-app) application +Building a simple [python-sample-app](https://github.com/sclorg/django-ex.git) application in Openshift can be achieved with the following step: ``` - oc new-app python:3.9~https://github.com/sclorg/s2i-python-container.git --context-dir=3.9/test/setup-test-app/ + oc new-app python:3.9~https://github.com/sclorg/django-ex.git ``` -The same application can also be built using the standalone [S2I](https://github.com/openshift/source-to-image) application on systems that have it available: - - ``` - $ s2i build https://github.com/sclorg/s2i-python-container.git --context-dir=3.9/test/setup-test-app/ python-sample-app - ``` - -Where `` is the s2i-python image you [downloaded from RHEL, Centos or Fedora registry](../README.md#Download) or [built](../README.md#Build) from these sources. For example ubi8/python-36, centos/python-36-centos7 or f31/python3. - **Accessing the application:** ``` -$ curl 127.0.0.1:8080 +$ oc get pods +$ oc exec -- 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. + +To support the Source-to-Image framework, important scripts are included in the builder image: + +* 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. + +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. + +To use the Python image in a Dockerfile, follow these steps: +#### 1. Pull a base builder image to build on + +``` +podman pull +``` + +#### 2. Pull and application code + +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 + +``` +git clone https://github.com/sclorg/django-ex.git app-src +``` + +#### 3. Prepare an application inside a container + +This step usually consists of at least these parts: + +* putting the application source into the container +* installing the dependencies +* setting the default command in the resulting image + +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. + +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. + +To use the Source-to-Image scripts and build an image using a Dockerfile, create a Dockerfile with this content: + +``` +FROM +# 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 +USER 1001 +# Install the dependencies +RUN /usr/libexec/s2i/assemble +# Set the default command for the resulting image +CMD /usr/libexec/s2i/run +``` +#### 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 @@ -244,7 +320,7 @@ following ways, in precedence order: application. Hot deploy ---------------------- +---------- If you are using Django, hot deploy will work out of the box. diff --git a/test/check_imagestreams.py b/test/check_imagestreams.py index 25ae6a1..6da0f63 100755 --- a/test/check_imagestreams.py +++ b/test/check_imagestreams.py @@ -3,6 +3,7 @@ import sys import json import logging +import os from pathlib import Path from typing import Dict @@ -47,6 +48,9 @@ class ImageStreamChecker(object): 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)): diff --git a/test/from-dockerfile/Dockerfile.tpl b/test/from-dockerfile/Dockerfile.tpl new file mode 100644 index 0000000..2a12b8c --- /dev/null +++ b/test/from-dockerfile/Dockerfile.tpl @@ -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 chown -R 1001:0 /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 diff --git a/test/run b/test/run index 45e09ce..42a52d5 100755 --- a/test/run +++ b/test/run @@ -187,15 +187,11 @@ test_application() { cleanup_app } -test_latest_imagestreams() { - local result=1 - info "Testing the latest version in imagestreams" - # Switch to root directory of a container - pushd "${test_dir}/../.." >/dev/null || return 1 - ct_check_latest_imagestreams - result=$? - popd >/dev/null || return 1 - check_result $result +test_from_dockerfile(){ + info "Test from Dockerfile" + 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 + check_result $? } # Since we built the candidate image locally, we don't want S2I attempt to pull @@ -234,10 +230,24 @@ for app in ${@:-${WEB_APPS[@]}}; do # test application with init wrapper CONTAINER_ARGS="-e ENABLE_INIT_WRAPPER=true" test_application - test_latest_imagestreams - 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 + + 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 + +test_from_dockerfile + info "All tests finished successfully." diff --git a/test/run-openshift b/test/run-openshift index 4a09812..35d3b7d 100755 --- a/test/run-openshift +++ b/test/run-openshift @@ -7,6 +7,7 @@ # 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" @@ -16,6 +17,14 @@ 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 @@ -44,6 +53,9 @@ 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 diff --git a/test/test-lib-openshift.sh b/test/test-lib-openshift.sh index 9c3bfd5..c11a025 100644 --- a/test/test-lib-openshift.sh +++ b/test/test-lib-openshift.sh @@ -769,7 +769,9 @@ function ct_os_test_template_app_func() { else echo "Uploading image ${image_name} as ${image_tagged}" ct_os_upload_image "${image_name}" "${image_tagged}" - + fi + fi + if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'false' ] ; then # upload also other images, that template might need (list of pairs in the format | local image_tag_a local i_t @@ -777,9 +779,12 @@ function ct_os_test_template_app_func() { echo "${i_t}" IFS='|' read -ra image_tag_a <<< "${i_t}" docker pull "${image_tag_a[0]}" - ct_os_upload_image "${image_tag_a[0]}" "${image_tag_a[1]}" + if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then + ct_os_import_image_ocp4 "${image_tag_a[0]}" "${image_tag_a[1]}" + else + ct_os_upload_image "${image_tag_a[0]}" "${image_tag_a[1]}" + fi done - fi fi # get the template file from remote or local location; if not found, it is diff --git a/test/test-lib.sh b/test/test-lib.sh index bac6787..b2e092e 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -27,6 +27,7 @@ EXPECTED_EXIT_CODE=0 function ct_cleanup() { ct_show_resources for cid_file in "$CID_FILE_DIR"/* ; do + [ -f "$cid_file" ] || continue local container container=$(cat "$cid_file") @@ -69,6 +70,7 @@ function ct_check_envs_set { 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=") @@ -340,7 +342,7 @@ function ct_binary_found_from_df() { # Create Dockerfile that looks for the binary cat <"$tmpdir/Dockerfile" FROM $IMAGE_NAME -RUN which $binary | grep "$binary_path" +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 @@ -558,7 +560,7 @@ ct_registry_from_os() { registry=registry.redhat.io ;; *) - registry=docker.io + registry=quay.io ;; esac echo "$registry" @@ -584,9 +586,9 @@ ct_get_public_image_name() { elif [ "x$os" == "xrhel8" ]; then public_image_name=$registry/rhel8/$base_image_name-${version//./} elif [ "x$os" == "xcentos7" ]; then - public_image_name=$registry/centos/$base_image_name-${version//./}-centos7 + public_image_name=$registry/centos7/$base_image_name-${version//./}-centos7 elif [ "x$os" == "xcentos8" ]; then - public_image_name=$registry/centos/$base_image_name-${version//./}-centos8 + public_image_name=$registry/centos8/$base_image_name-${version//./}-centos8 fi echo "$public_image_name"