From 05ce698812cce111e64e23ceba220a1bf9a560d4 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Mon, 3 Feb 2020 18:34:19 +0100 Subject: [PATCH 01/19] Update version of the base image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c76509d..493e2b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fedoraproject.org/f31/s2i-core:latest +FROM registry.fedoraproject.org/f30/s2i-core:latest # Redis image based on Software Collections packages # From 7710ad2977a9f3aa3bcccc2b92b8b9440ae3cea4 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Mon, 3 Feb 2020 18:38:19 +0100 Subject: [PATCH 02/19] Remove obsoleted labels --- Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index c76509d..b5ed5d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,6 @@ FROM registry.fedoraproject.org/f31/s2i-core:latest ENV NAME=redis \ VERSION=5 \ - RELEASE=1 \ ARCH=x86_64 ENV REDIS_VERSION=$VERSION \ @@ -33,8 +32,6 @@ LABEL summary="$SUMMARY" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ version="$VERSION" \ - release="$RELEASE.$DISTTAG" \ - architecture="$ARCH" \ usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ maintainer="SoftwareCollections.org " From f931d6ee9a88209d8b17d6c64249306e3942aee6 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 4 Feb 2020 09:11:11 +0100 Subject: [PATCH 03/19] Update the README.md in the root --- README.md | 80 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8f5cf3d..0772ce6 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,36 @@ -Redis Docker image +Redis 5 in-memory data structure store container image ==================== -This container image includes Dockerfile for Redis 3.2 Docker image. -Users can choose between RHEL and CentOS based images. +This container image includes Redis 5 in-memory data structure store for OpenShift and general usage. +Users can choose between RHEL, CentOS and Fedora based 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/), +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). -Dockerfile for CentOS is called Dockerfile, Dockerfile for RHEL is called -Dockerfile.rhel7. +Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments -Environment variables and volumes ----------------------------------- +Description +----------- -| Variable name | Description | -| :--------------------- | ----------------------------------------- | -| `REDIS_PASSWORD` | Password for the server access | +Redis 5 available as container, is an advanced key-value store. +It is often referred to as a data structure server since keys can contain strings, hashes, lists, +sets and sorted sets. You can run atomic operations on these types, like appending to a string; +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist +it either by dumping the dataset to disk every once in a while, or by appending each command to a log. -TBD - -You can also set the following mount points by passing the `-v /host:/container:Z` flag to Docker. - -| Volume mount point | Description | -| :----------------------- | -------------------- | -| `/var/lib/redis/data` | Redis data directory | - -**Notice: When mouting a directory from the host into the container, ensure that the mounted -directory has the appropriate permissions and that the owner and group of the directory -matches the user UID or name which is running inside the container.** Usage ---------------------------------- +----- -For this, we will assume that you are using the `rhscl/redis-32-rhel7` image. +For this, we will assume that you are using the `rhel8/redis-5` image. If you want to set only the mandatory environment variables and not store the database in a host directory, execute the following command: ``` -$ docker run -d --name redis_database -p 6379:6379 rhscl/redis-32-rhel7 +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-5 ``` This will create a container named `redis_database`. Port 6379 will be exposed and mapped @@ -47,9 +43,43 @@ For protecting Redis data by a password, pass `REDIS_PASSWORD` environment varia to the container like this: ``` -$ docker run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhscl/redis-32-rhel7 +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-5 ``` **Warning: since Redis is pretty fast an outside user can try up to 150k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.** + + +Environment variables and volumes +---------------------------------- + +**`REDIS_PASSWORD`** + Password for the server access + + +You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. + +**`/var/lib/redis/data`** + Redis data directory + + +**Notice: When mouting a directory from the host into the container, ensure that the mounted +directory has the appropriate permissions and that the owner and group of the directory +matches the user UID or name which is running inside the container.** + + +Troubleshooting +--------------- +Redis logs into standard output, so the log is available in the container log. The log can be examined by running: + + podman logs + + +See also +-------- +Dockerfile and other sources for this container image are available on +https://github.com/sclorg/redis-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. From 8018a310d21a0739a2b44e2aa13243bb10372889 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Mon, 3 Feb 2020 18:38:19 +0100 Subject: [PATCH 04/19] Remove obsoleted labels --- Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 493e2b0..198beb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,6 @@ FROM registry.fedoraproject.org/f30/s2i-core:latest ENV NAME=redis \ VERSION=5 \ - RELEASE=1 \ ARCH=x86_64 ENV REDIS_VERSION=$VERSION \ @@ -33,8 +32,6 @@ LABEL summary="$SUMMARY" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ version="$VERSION" \ - release="$RELEASE.$DISTTAG" \ - architecture="$ARCH" \ usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ maintainer="SoftwareCollections.org " From 0da1173d6f274ee16368ec3c587e5056dccc998a Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 4 Feb 2020 09:11:11 +0100 Subject: [PATCH 05/19] Update the README.md in the root --- README.md | 80 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8f5cf3d..0772ce6 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,36 @@ -Redis Docker image +Redis 5 in-memory data structure store container image ==================== -This container image includes Dockerfile for Redis 3.2 Docker image. -Users can choose between RHEL and CentOS based images. +This container image includes Redis 5 in-memory data structure store for OpenShift and general usage. +Users can choose between RHEL, CentOS and Fedora based 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/), +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). -Dockerfile for CentOS is called Dockerfile, Dockerfile for RHEL is called -Dockerfile.rhel7. +Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments -Environment variables and volumes ----------------------------------- +Description +----------- -| Variable name | Description | -| :--------------------- | ----------------------------------------- | -| `REDIS_PASSWORD` | Password for the server access | +Redis 5 available as container, is an advanced key-value store. +It is often referred to as a data structure server since keys can contain strings, hashes, lists, +sets and sorted sets. You can run atomic operations on these types, like appending to a string; +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist +it either by dumping the dataset to disk every once in a while, or by appending each command to a log. -TBD - -You can also set the following mount points by passing the `-v /host:/container:Z` flag to Docker. - -| Volume mount point | Description | -| :----------------------- | -------------------- | -| `/var/lib/redis/data` | Redis data directory | - -**Notice: When mouting a directory from the host into the container, ensure that the mounted -directory has the appropriate permissions and that the owner and group of the directory -matches the user UID or name which is running inside the container.** Usage ---------------------------------- +----- -For this, we will assume that you are using the `rhscl/redis-32-rhel7` image. +For this, we will assume that you are using the `rhel8/redis-5` image. If you want to set only the mandatory environment variables and not store the database in a host directory, execute the following command: ``` -$ docker run -d --name redis_database -p 6379:6379 rhscl/redis-32-rhel7 +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-5 ``` This will create a container named `redis_database`. Port 6379 will be exposed and mapped @@ -47,9 +43,43 @@ For protecting Redis data by a password, pass `REDIS_PASSWORD` environment varia to the container like this: ``` -$ docker run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhscl/redis-32-rhel7 +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-5 ``` **Warning: since Redis is pretty fast an outside user can try up to 150k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.** + + +Environment variables and volumes +---------------------------------- + +**`REDIS_PASSWORD`** + Password for the server access + + +You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. + +**`/var/lib/redis/data`** + Redis data directory + + +**Notice: When mouting a directory from the host into the container, ensure that the mounted +directory has the appropriate permissions and that the owner and group of the directory +matches the user UID or name which is running inside the container.** + + +Troubleshooting +--------------- +Redis logs into standard output, so the log is available in the container log. The log can be examined by running: + + podman logs + + +See also +-------- +Dockerfile and other sources for this container image are available on +https://github.com/sclorg/redis-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. From 8c262916d3b97f9369da9f7ff4a67ab03fa43a36 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 4 Feb 2020 11:41:53 +0100 Subject: [PATCH 06/19] Add a release label temporarily to add a suffix --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index b5ed5d8..5bdb333 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,6 +32,7 @@ LABEL summary="$SUMMARY" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ version="$VERSION" \ +release="3.container" \ usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ maintainer="SoftwareCollections.org " From df9c34c267332d685985e083b43908425e34b943 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 4 Feb 2020 11:48:49 +0100 Subject: [PATCH 07/19] Remove the release label temporarily added for a suffix --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5bdb333..b5ed5d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,6 @@ LABEL summary="$SUMMARY" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ version="$VERSION" \ -release="3.container" \ usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ maintainer="SoftwareCollections.org " From 5b189bc2aeb9f55da8face8fc21cb3a874f99e21 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 30 Jun 2020 17:05:24 +0200 Subject: [PATCH 08/19] Update the parent image version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b5ed5d8..60246a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fedoraproject.org/f31/s2i-core:latest +FROM registry.fedoraproject.org/f32/s2i-core:latest # Redis image based on Software Collections packages # From 769add4ee4088fc2937957bdd5f8731a0a69abe9 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 30 Jun 2020 17:09:03 +0200 Subject: [PATCH 09/19] Update to redis 6 --- Dockerfile | 8 ++++---- README.md | 14 +++++++------- root/help.1 | 14 +++++++------- root/usr/libexec/container-setup | 8 ++++---- root/usr/share/container-scripts/redis/README.md | 14 +++++++------- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Dockerfile b/Dockerfile index 60246a1..2f0eceb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fedoraproject.org/f32/s2i-core:latest +FROM registry.fedoraproject.org/f33/s2i-core:latest # Redis image based on Software Collections packages # @@ -8,7 +8,7 @@ FROM registry.fedoraproject.org/f32/s2i-core:latest # * $REDIS_PASSWORD - Database password ENV NAME=redis \ - VERSION=5 \ + VERSION=6 \ ARCH=x86_64 ENV REDIS_VERSION=$VERSION \ @@ -26,9 +26,9 @@ it either by dumping the dataset to disk every once in a while, or by appending LABEL summary="$SUMMARY" \ description="$DESCRIPTION" \ io.k8s.description="$SUMMARY" \ - io.k8s.display-name="Redis 5" \ + io.k8s.display-name="Redis 6" \ io.openshift.expose-services="6379:redis" \ - io.openshift.tags="database,redis,redis5" \ + io.openshift.tags="database,redis,redis6" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ version="$VERSION" \ diff --git a/README.md b/README.md index 0772ce6..e9446d4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -Redis 5 in-memory data structure store container image +Redis 6 in-memory data structure store container image ==================== -This container image includes Redis 5 in-memory data structure store for OpenShift and general usage. +This container image includes Redis 6 in-memory data structure store for OpenShift and general usage. Users can choose between RHEL, CentOS and Fedora based 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/), @@ -13,7 +13,7 @@ Note: while the examples in this README are calling `podman`, you can replace an Description ----------- -Redis 5 available as container, is an advanced key-value store. +Redis 6 available as container, is an advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. You can run atomic operations on these types, like appending to a string; incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; @@ -25,12 +25,12 @@ it either by dumping the dataset to disk every once in a while, or by appending Usage ----- -For this, we will assume that you are using the `rhel8/redis-5` image. +For this, we will assume that you are using the `rhel8/redis-6` image. If you want to set only the mandatory environment variables and not store the database in a host directory, execute the following command: ``` -$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-5 +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-6 ``` This will create a container named `redis_database`. Port 6379 will be exposed and mapped @@ -43,11 +43,11 @@ For protecting Redis data by a password, pass `REDIS_PASSWORD` environment varia to the container like this: ``` -$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-5 +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-6 ``` **Warning: since Redis is pretty fast an outside user can try up to -150k passwords per second against a good box. This means that you should +160k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.** diff --git a/root/help.1 b/root/help.1 index f0a8059..3174112 100644 --- a/root/help.1 +++ b/root/help.1 @@ -1,6 +1,6 @@ -.TH Redis 5 in\-memory data structure store container image +.TH Redis 6 in\-memory data structure store container image .PP -This container image includes Redis 5 in\-memory data structure store for OpenShift and general usage. +This container image includes Redis 6 in\-memory data structure store for OpenShift and general usage. Users can choose between RHEL, CentOS and Fedora based images. The RHEL images are available in the Red Hat Container Catalog \[la]https://access.redhat.com/containers/\[ra], @@ -16,7 +16,7 @@ Note: while the examples in this README are calling \fB\fCpodman\fR, you can rep .SH Description .PP -Redis 5 available as container, is an advanced key\-value store. +Redis 6 available as container, is an advanced key\-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. You can run atomic operations on these types, like appending to a string; incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; @@ -26,7 +26,7 @@ it either by dumping the dataset to disk every once in a while, or by appending .SH Usage .PP -For this, we will assume that you are using the \fB\fCrhel8/redis\-5\fR image. +For this, we will assume that you are using the \fB\fCrhel8/redis\-6\fR image. If you want to set only the mandatory environment variables and not store the database in a host directory, execute the following command: @@ -34,7 +34,7 @@ the database in a host directory, execute the following command: .RS .nf -$ podman run \-d \-\-name redis\_database \-p 6379:6379 rhel8/redis\-5 +$ podman run \-d \-\-name redis\_database \-p 6379:6379 rhel8/redis\-6 .fi .RE @@ -55,14 +55,14 @@ to the container like this: .RS .nf -$ podman run \-d \-\-name redis\_database \-e REDIS\_PASSWORD=strongpassword rhel8/redis\-5 +$ podman run \-d \-\-name redis\_database \-e REDIS\_PASSWORD=strongpassword rhel8/redis\-6 .fi .RE .PP \fBWarning: since Redis is pretty fast an outside user can try up to -150k passwords per second against a good box. This means that you should +160k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.\fP .SH Environment variables and volumes diff --git a/root/usr/libexec/container-setup b/root/usr/libexec/container-setup index 0375238..a4b7cfe 100755 --- a/root/usr/libexec/container-setup +++ b/root/usr/libexec/container-setup @@ -5,8 +5,8 @@ set -eu # setup config file if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then - mv /etc/opt/rh/rh-redis5/redis.conf /etc/redis.conf - ln -s /etc/redis.conf /etc/opt/rh/rh-redis5/redis.conf + mv /etc/opt/rh/rh-redis6/redis.conf /etc/redis.conf + ln -s /etc/redis.conf /etc/opt/rh/rh-redis6/redis.conf fi # setup directory for data @@ -15,8 +15,8 @@ restorecon -R "${HOME}" /etc/redis.conf # create a symlink for SCL datadir, so there is some reasonable content there if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then - rmdir /var/opt/rh/rh-redis5/lib/redis/ - ln -s /var/lib/redis /var/opt/rh/rh-redis5/lib/redis + rmdir /var/opt/rh/rh-redis6/lib/redis/ + ln -s /var/lib/redis /var/opt/rh/rh-redis6/lib/redis fi # Loosen permission bits for group to avoid problems running container with diff --git a/root/usr/share/container-scripts/redis/README.md b/root/usr/share/container-scripts/redis/README.md index 0772ce6..e9446d4 100644 --- a/root/usr/share/container-scripts/redis/README.md +++ b/root/usr/share/container-scripts/redis/README.md @@ -1,7 +1,7 @@ -Redis 5 in-memory data structure store container image +Redis 6 in-memory data structure store container image ==================== -This container image includes Redis 5 in-memory data structure store for OpenShift and general usage. +This container image includes Redis 6 in-memory data structure store for OpenShift and general usage. Users can choose between RHEL, CentOS and Fedora based 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/), @@ -13,7 +13,7 @@ Note: while the examples in this README are calling `podman`, you can replace an Description ----------- -Redis 5 available as container, is an advanced key-value store. +Redis 6 available as container, is an advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. You can run atomic operations on these types, like appending to a string; incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; @@ -25,12 +25,12 @@ it either by dumping the dataset to disk every once in a while, or by appending Usage ----- -For this, we will assume that you are using the `rhel8/redis-5` image. +For this, we will assume that you are using the `rhel8/redis-6` image. If you want to set only the mandatory environment variables and not store the database in a host directory, execute the following command: ``` -$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-5 +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-6 ``` This will create a container named `redis_database`. Port 6379 will be exposed and mapped @@ -43,11 +43,11 @@ For protecting Redis data by a password, pass `REDIS_PASSWORD` environment varia to the container like this: ``` -$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-5 +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-6 ``` **Warning: since Redis is pretty fast an outside user can try up to -150k passwords per second against a good box. This means that you should +160k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.** From 9d03b2aeea4e99278d6c24f882d827e371549b05 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Fri, 3 Jul 2020 13:54:31 +0200 Subject: [PATCH 10/19] Improve comments and introduce release label temporarily --- Dockerfile | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2f0eceb..f86e539 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,7 @@ LABEL summary="$SUMMARY" \ io.openshift.tags="database,redis,redis6" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ + release="2.container" \ version="$VERSION" \ usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ maintainer="SoftwareCollections.org " @@ -39,18 +40,22 @@ EXPOSE 6379 # Create user for redis that has known UID # We need to do this before installing the RPMs which would create user with random UID +# The UID is the one used by the default user from the parent layer (1001), +# and since the user exists already, do not create a new one, but only rename +# the existing +# This image must forever use UID 1001 for redis user so our volumes are +# safe in the future. This should *never* change, the last test is there +# to make sure of that. RUN getent group redis &> /dev/null || groupadd -r redis &> /dev/null && \ usermod -l redis -g redis -c 'Redis Server' default &> /dev/null && \ # Install gettext for envsubst command -# This image must forever use UID 964 for redis user so our volumes are -# safe in the future. This should *never* change, the last test is there -# to make sure of that. dnf install -y yum-utils gettext policycoreutils && \ INSTALL_PKGS="redis" && \ dnf install -y --setopt=tsflags=nodocs --nogpgcheck $INSTALL_PKGS && \ rpm -V $INSTALL_PKGS && \ dnf clean all && \ - mkdir -p /var/lib/redis/data && chown -R redis.0 /var/lib/redis + mkdir -p /var/lib/redis/data && chown -R redis.0 /var/lib/redis && \ + [[ "$(id redis)" == "uid=1001(redis)"* ]] # Get prefix path and path to scripts rather than hard-code them in scripts ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/redis \ @@ -65,6 +70,10 @@ RUN /usr/libexec/container-setup VOLUME ["/var/lib/redis/data"] +# Using a numeric value because of a comment in [1]: +# If your S2I image does not include a USER declaration with a numeric user, +# your builds will fail by default. +# [1] https://docs.openshift.com/container-platform/4.4/openshift_images/create-images.html#images-create-guide-openshift_create-images USER 1001 ENTRYPOINT ["container-entrypoint"] From b3078e78f58a064adff10e3bcd0621c534864fbf Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Mon, 20 Jul 2020 16:58:44 +0200 Subject: [PATCH 11/19] Removing release and arch spec --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index f86e539..9338462 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,8 +8,7 @@ FROM registry.fedoraproject.org/f33/s2i-core:latest # * $REDIS_PASSWORD - Database password ENV NAME=redis \ - VERSION=6 \ - ARCH=x86_64 + VERSION=6 ENV REDIS_VERSION=$VERSION \ HOME=/var/lib/redis @@ -31,7 +30,6 @@ LABEL summary="$SUMMARY" \ io.openshift.tags="database,redis,redis6" \ com.redhat.component="$NAME" \ name="$FGC/$NAME" \ - release="2.container" \ version="$VERSION" \ usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ maintainer="SoftwareCollections.org " From fe6dd5c2feeafc152794b8ee9c1cf9cb6922b2e3 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 13 Sep 2021 10:51:05 +0200 Subject: [PATCH 12/19] Add support for sync upstream -> downstream Signed-off-by: Petr "Stone" Hracek --- bot-cfg.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bot-cfg.yml diff --git a/bot-cfg.yml b/bot-cfg.yml new file mode 100644 index 0000000..3a3b98b --- /dev/null +++ b/bot-cfg.yml @@ -0,0 +1,20 @@ +--- +version: "1" + +betka: + # is betka enabled for this repository + # optional - defaults to true + enabled: true + notifications: + email_addresses: ["pkubat@redhat.com", "phracek@redhat.com", "hhorak@redhat.com"] + + # Specify if master branch in upstream repository is synced + master_checker: true + # Should pull requests be synced? + pr_checker: false + # Path to directory with dockerfile withing upstream repository + upstream_git_path: "6" + # Github comment message to enforce sync of a pull request + pr_comment_message: "[test]" + # Specify URL to an image used for dist-git source generation. Like + image_url: "quay.io/rhscl/cwt-generator" From 91b69a2378f3ef931e70d29e6e063e89f2bd045a Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Mon, 13 Sep 2021 10:51:59 +0200 Subject: [PATCH 13/19] Add support for sync upstream -> downstream Signed-off-by: Petr "Stone" Hracek --- bot-cfg.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 bot-cfg.yml diff --git a/bot-cfg.yml b/bot-cfg.yml new file mode 100644 index 0000000..3a3b98b --- /dev/null +++ b/bot-cfg.yml @@ -0,0 +1,20 @@ +--- +version: "1" + +betka: + # is betka enabled for this repository + # optional - defaults to true + enabled: true + notifications: + email_addresses: ["pkubat@redhat.com", "phracek@redhat.com", "hhorak@redhat.com"] + + # Specify if master branch in upstream repository is synced + master_checker: true + # Should pull requests be synced? + pr_checker: false + # Path to directory with dockerfile withing upstream repository + upstream_git_path: "6" + # Github comment message to enforce sync of a pull request + pr_comment_message: "[test]" + # Specify URL to an image used for dist-git source generation. Like + image_url: "quay.io/rhscl/cwt-generator" From 85d49c94210a366051d7af8c2e13c091e63529e3 Mon Sep 17 00:00:00 2001 From: phracek Date: Thu, 16 Sep 2021 12:42:22 +0000 Subject: [PATCH 14/19] Update build-and-push.yml --- 6 | 1 + Dockerfile | 79 + Dockerfile.fedora | 1 + README.md | 1 + help.md | 1 + root/usr/bin/container-entrypoint | 2 + root/usr/bin/run-redis | 27 + root/usr/bin/usage | 4 + root/usr/libexec/container-setup | 31 + .../share/container-scripts/redis/README.md | 85 ++ .../redis/base.conf.template | 1 + .../share/container-scripts/redis/common.sh | 26 + .../share/container-scripts/redis/helpers.sh | 24 + .../redis/password.conf.template | 3 + .../container-scripts/redis/post-init.sh | 6 + .../share/container-scripts/redis/scl_enable | 3 + .../redis/validate-variables.sh | 15 + test/examples/redis-ephemeral-template.json | 216 +++ test/examples/redis-persistent-template.json | 240 +++ test/imagestreams/redis-centos.json | 85 ++ test/imagestreams/redis-rhel-aarch64.json | 67 + test/imagestreams/redis-rhel.json | 103 ++ test/redis-ephemeral-template.json | 216 +++ test/run | 359 +++++ test/run-openshift | 1 + test/run-openshift-local-cluster | 119 ++ test/run-openshift-remote-cluster | 37 + test/test-lib-openshift.sh | 1343 +++++++++++++++++ test/test-lib-redis.sh | 37 + test/test-lib-remote-openshift.sh | 112 ++ test/test-lib.sh | 918 +++++++++++ 31 files changed, 4163 insertions(+) create mode 120000 6 create mode 100644 Dockerfile create mode 120000 Dockerfile.fedora create mode 120000 README.md create mode 120000 help.md create mode 100755 root/usr/bin/container-entrypoint create mode 100755 root/usr/bin/run-redis create mode 100755 root/usr/bin/usage create mode 100755 root/usr/libexec/container-setup create mode 100644 root/usr/share/container-scripts/redis/README.md create mode 100644 root/usr/share/container-scripts/redis/base.conf.template create mode 100644 root/usr/share/container-scripts/redis/common.sh create mode 100644 root/usr/share/container-scripts/redis/helpers.sh create mode 100644 root/usr/share/container-scripts/redis/password.conf.template create mode 100644 root/usr/share/container-scripts/redis/post-init.sh create mode 100644 root/usr/share/container-scripts/redis/scl_enable create mode 100644 root/usr/share/container-scripts/redis/validate-variables.sh create mode 100644 test/examples/redis-ephemeral-template.json create mode 100644 test/examples/redis-persistent-template.json create mode 100644 test/imagestreams/redis-centos.json create mode 100644 test/imagestreams/redis-rhel-aarch64.json create mode 100644 test/imagestreams/redis-rhel.json create mode 100644 test/redis-ephemeral-template.json create mode 100755 test/run create mode 120000 test/run-openshift create mode 100755 test/run-openshift-local-cluster create mode 100755 test/run-openshift-remote-cluster create mode 100644 test/test-lib-openshift.sh create mode 100644 test/test-lib-redis.sh create mode 100644 test/test-lib-remote-openshift.sh create mode 100644 test/test-lib.sh diff --git a/6 b/6 new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/6 @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..659d2b5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,79 @@ +FROM registry.fedoraproject.org/f34/s2i-core:latest + +# Redis image based on Software Collections packages +# +# Volumes: +# * /var/lib/redis/data - Datastore for Redis +# Environment: +# * $REDIS_PASSWORD - Database password + +ENV NAME=redis \ + RELEASE=\"2" \ + VERSION=6 + +ENV REDIS_VERSION=$VERSION \ + HOME=/var/lib/redis + +ENV SUMMARY="Redis in-memory data structure store, used as database, cache and message broker" \ + DESCRIPTION="Redis $REDIS_VERSION available as container, is an advanced key-value store. \ +It is often referred to as a data structure server since keys can contain strings, hashes, lists, \ +sets and sorted sets. You can run atomic operations on these types, like appending to a string; \ +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; \ +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding \ +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist \ +it either by dumping the dataset to disk every once in a while, or by appending each command to a log." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$SUMMARY" \ + io.k8s.display-name="Redis 6" \ + io.openshift.expose-services="6379:redis" \ + io.openshift.tags="database,redis,redis6" \ + com.redhat.component="$NAME" \ + name="$FGC/$NAME" \ + version="$VERSION" \ + usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ + maintainer="SoftwareCollections.org " + +EXPOSE 6379 + +# Create user for redis that has known UID +# We need to do this before installing the RPMs which would create user with random UID +# The UID is the one used by the default user from the parent layer (1001), +# and since the user exists already, do not create a new one, but only rename +# the existing +# This image must forever use UID 1001 for redis user so our volumes are +# safe in the future. This should *never* change, the last test is there +# to make sure of that. +RUN getent group redis &> /dev/null || groupadd -r redis &> /dev/null && \ + usermod -l redis -aG redis -c 'Redis Server' default &> /dev/null && \ +# Install gettext for envsubst command + dnf install -y yum-utils gettext policycoreutils && \ + INSTALL_PKGS="redis" && \ + dnf install -y --setopt=tsflags=nodocs --nogpgcheck $INSTALL_PKGS && \ + rpm -V $INSTALL_PKGS && \ + dnf clean all && \ + mkdir -p /var/lib/redis/data && chown -R redis.0 /var/lib/redis && \ + [[ "$(id redis)" == "uid=1001(redis)"* ]] + +# Get prefix path and path to scripts rather than hard-code them in scripts +ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/redis \ + REDIS_PREFIX=/usr + +COPY root / + +# this is needed due to issues with squash +# when this directory gets rm'd by the container-setup +# script. +RUN /usr/libexec/container-setup + +VOLUME ["/var/lib/redis/data"] + +# Using a numeric value because of a comment in [1]: +# If your S2I image does not include a USER declaration with a numeric user, +# your builds will fail by default. +# [1] https://docs.openshift.com/container-platform/4.4/openshift_images/create-images.html#images-create-guide-openshift_create-images +USER 1001 + +ENTRYPOINT ["container-entrypoint"] +CMD ["run-redis"] 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 new file mode 120000 index 0000000..0b7f519 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +root/usr/share/container-scripts/redis/README.md \ No newline at end of file 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/root/usr/bin/container-entrypoint b/root/usr/bin/container-entrypoint new file mode 100755 index 0000000..9d8ad4d --- /dev/null +++ b/root/usr/bin/container-entrypoint @@ -0,0 +1,2 @@ +#!/bin/bash +exec "$@" diff --git a/root/usr/bin/run-redis b/root/usr/bin/run-redis new file mode 100755 index 0000000..d739867 --- /dev/null +++ b/root/usr/bin/run-redis @@ -0,0 +1,27 @@ +#!/bin/bash + +export_vars=$(cgroup-limits); export $export_vars +source ${CONTAINER_SCRIPTS_PATH}/common.sh +set -eu + +[ -f ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh ] && source ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh + +# Process the Redis configuration files +log_info 'Processing Redis configuration files ...' +if [[ -v REDIS_PASSWORD ]]; then + envsubst < ${CONTAINER_SCRIPTS_PATH}/password.conf.template >> /etc/redis/redis.conf +else + log_info 'WARNING: setting REDIS_PASSWORD is recommended' +fi + +# Source post-init source if exists +if [ -f ${CONTAINER_SCRIPTS_PATH}/post-init.sh ]; then + log_info 'Sourcing post-init.sh ...' + source ${CONTAINER_SCRIPTS_PATH}/post-init.sh +fi + +# Restart the Redis server with public IP bindings +unset_env_vars +log_volume_info "${REDIS_DATADIR}" +log_info 'Running final exec -- Only Redis logs after this point' +exec ${REDIS_PREFIX}/bin/redis-server /etc/redis/redis.conf --daemonize no "$@" 2>&1 diff --git a/root/usr/bin/usage b/root/usr/bin/usage new file mode 100755 index 0000000..d204ed2 --- /dev/null +++ b/root/usr/bin/usage @@ -0,0 +1,4 @@ +#!/bin/bash + +cat /usr/share/container-scripts/redis/README.md + diff --git a/root/usr/libexec/container-setup b/root/usr/libexec/container-setup new file mode 100755 index 0000000..d918e9c --- /dev/null +++ b/root/usr/libexec/container-setup @@ -0,0 +1,31 @@ +#!/bin/bash + +source ${CONTAINER_SCRIPTS_PATH}/common.sh +set -eu + +# setup config file +if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then + mv /etc/opt/rh/rh-redis6/redis.conf /etc/redis/redis.conf + ln -s /etc/redis/redis.conf /etc/opt/rh/rh-redis6/redis.conf +fi + +# setup directory for data +chown -R redis:0 "${HOME}" /etc/redis/redis.conf +restorecon -R "${HOME}" /etc/redis/redis.conf + +# create a symlink for SCL datadir, so there is some reasonable content there +if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then + rmdir /var/opt/rh/rh-redis6/lib/redis/ + ln -s /var/lib/redis /var/opt/rh/rh-redis6/lib/redis +fi + +# Loosen permission bits for group to avoid problems running container with +# arbitrary UID +# When only specifying user, group is 0, that's why /var/lib/redis must have +# owner redis.0; that allows to avoid a+rwx for this dir +chmod 0770 "${HOME}" "${REDIS_DATADIR}" +chmod 0660 /etc/redis/redis.conf + +# adjust config with changes we do every-time +clear_config +envsubst < ${CONTAINER_SCRIPTS_PATH}/base.conf.template >> /etc/redis/redis.conf diff --git a/root/usr/share/container-scripts/redis/README.md b/root/usr/share/container-scripts/redis/README.md new file mode 100644 index 0000000..97db572 --- /dev/null +++ b/root/usr/share/container-scripts/redis/README.md @@ -0,0 +1,85 @@ +Redis 6 in-memory data structure store container image +====================================================== + +This container image includes Redis 6 in-memory data structure store for OpenShift and general usage. +Users can choose between RHEL, CentOS and Fedora based images. +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). + +Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments + +Description +----------- + +Redis 6 available as container, is an advanced key-value store. +It is often referred to as a data structure server since keys can contain strings, hashes, lists, +sets and sorted sets. You can run atomic operations on these types, like appending to a string; +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist +it either by dumping the dataset to disk every once in a while, or by appending each command to a log. + + +Usage +----- + +For this, we will assume that you are using the `rhel8/redis-6` image. +If you want to set only the mandatory environment variables and not store +the database in a host directory, execute the following command: + +``` +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-6 +``` + +This will create a container named `redis_database`. Port 6379 will be exposed and mapped +to the host. + +If you want your database to be persistent across container executions, also add a +`-v /host/db/path:/var/lib/redis/data:Z` argument. This will be the Redis data directory. + +For protecting Redis data by a password, pass `REDIS_PASSWORD` environment variable +to the container like this: + +``` +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-6 +``` + +**Warning: since Redis is pretty fast an outside user can try up to +150k passwords per second against a good box. This means that you should +use a very strong password otherwise it will be very easy to break.** + + +Environment variables and volumes +---------------------------------- + +**`REDIS_PASSWORD`** + Password for the server access + + +You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. + +**`/var/lib/redis/data`** + Redis data directory + + +**Notice: When mouting a directory from the host into the container, ensure that the mounted +directory has the appropriate permissions and that the owner and group of the directory +matches the user UID or name which is running inside the container.** + + +Troubleshooting +--------------- +Redis logs into standard output, so the log is available in the container log. The log can be examined by running: + + podman logs + + +See also +-------- +Dockerfile and other sources for this container image are available on +https://github.com/sclorg/redis-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. diff --git a/root/usr/share/container-scripts/redis/base.conf.template b/root/usr/share/container-scripts/redis/base.conf.template new file mode 100644 index 0000000..9d3f01b --- /dev/null +++ b/root/usr/share/container-scripts/redis/base.conf.template @@ -0,0 +1 @@ +dir ${REDIS_DATADIR} diff --git a/root/usr/share/container-scripts/redis/common.sh b/root/usr/share/container-scripts/redis/common.sh new file mode 100644 index 0000000..da365c3 --- /dev/null +++ b/root/usr/share/container-scripts/redis/common.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source ${CONTAINER_SCRIPTS_PATH}/helpers.sh + +# Data directory where Redis database files live. The data subdirectory is here +# because .bashrc lives in /var/lib/redis/ and we don't want a +# volume to override it. +export REDIS_DATADIR=/var/lib/redis/data + +# Be paranoid and stricter than we should be. +redis_password_regex='^[a-zA-Z0-9_~!@#$%^&*()-=<>,.?;:|]+$' + +# Make sure env variables don't propagate to redis process. +function unset_env_vars() { + log_info 'Cleaning up environment variable REDIS_PASSWORD ...' + unset REDIS_PASSWORD +} + +# Comment out settings that we'll set in container specifically +function clear_config() { + sed -e "s/^bind/#bind/" \ + -e "s/^logfile/#logfile/" \ + -e "s/^dir /#dir /" \ + -e "/^protected-mode/s/yes/no/" \ + -i /etc/redis/redis.conf +} diff --git a/root/usr/share/container-scripts/redis/helpers.sh b/root/usr/share/container-scripts/redis/helpers.sh new file mode 100644 index 0000000..4e832fc --- /dev/null +++ b/root/usr/share/container-scripts/redis/helpers.sh @@ -0,0 +1,24 @@ +function log_info { + echo "---> `date +%T` $@" +} + +function log_and_run { + log_info "Running $@" + "$@" +} + +function log_volume_info { + CONTAINER_DEBUG=${CONTAINER_DEBUG:-} + if [[ "${CONTAINER_DEBUG,,}" != "true" ]]; then + return + fi + + log_info "Volume info for $@:" + set +e + log_and_run mount + while [ $# -gt 0 ]; do + log_and_run ls -alZ $1 + shift + done + set -e +} diff --git a/root/usr/share/container-scripts/redis/password.conf.template b/root/usr/share/container-scripts/redis/password.conf.template new file mode 100644 index 0000000..bd2eef3 --- /dev/null +++ b/root/usr/share/container-scripts/redis/password.conf.template @@ -0,0 +1,3 @@ +# password for the server +requirepass "${REDIS_PASSWORD}" + diff --git a/root/usr/share/container-scripts/redis/post-init.sh b/root/usr/share/container-scripts/redis/post-init.sh new file mode 100644 index 0000000..5ee2c96 --- /dev/null +++ b/root/usr/share/container-scripts/redis/post-init.sh @@ -0,0 +1,6 @@ +# This file serves for extending the container image, typically by changing +# the configuration, loading some data etc. + +# Feel free to add content to this file or rewrite it at all. +# You may also start redis server locally to load some data for example, +# but do not forget to stop it after it, so it can be restarted after it. diff --git a/root/usr/share/container-scripts/redis/scl_enable b/root/usr/share/container-scripts/redis/scl_enable new file mode 100644 index 0000000..5a25432 --- /dev/null +++ b/root/usr/share/container-scripts/redis/scl_enable @@ -0,0 +1,3 @@ +# This will make scl collection binaries work out of box. +unset BASH_ENV PROMPT_COMMAND ENV +source scl_source enable ${ENABLED_COLLECTIONS} diff --git a/root/usr/share/container-scripts/redis/validate-variables.sh b/root/usr/share/container-scripts/redis/validate-variables.sh new file mode 100644 index 0000000..57138a4 --- /dev/null +++ b/root/usr/share/container-scripts/redis/validate-variables.sh @@ -0,0 +1,15 @@ +function usage() { + [ $# == 1 ] && echo "error: $1" + echo "You can specify the following environment variables:" + echo " REDIS_PASSWORD (regex: '$redis_password_regex')" + exit 1 +} + +function validate_variables() { + # Check basic sanity of specified variables + if [[ -v REDIS_PASSWORD ]]; then + [[ "$REDIS_PASSWORD" =~ $redis_password_regex ]] || usage "Invalid password" + fi +} + +validate_variables diff --git a/test/examples/redis-ephemeral-template.json b/test/examples/redis-ephemeral-template.json new file mode 100644 index 0000000..387ce62 --- /dev/null +++ b/test/examples/redis-ephemeral-template.json @@ -0,0 +1,216 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "redis-ephemeral", + "annotations": { + "openshift.io/display-name": "Redis (Ephemeral)", + "description": "Redis in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", + "iconClass": "icon-redis", + "tags": "database,redis", + "openshift.io/long-description": "This template provides a standalone Redis server. The data is not stored on persistent storage, so any restart of the service will result in all data being lost.", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "openshift.io/documentation-url": "https://github.com/sclorg/redis-container/tree/master/5", + "openshift.io/support-url": "https://access.redhat.com" + } + }, + "message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Password: ${REDIS_PASSWORD}\n Connection URL: redis://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.", + "labels": { + "template": "redis-ephemeral-template" + }, + "objects": [ + { + "kind": "Secret", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.openshift.io/expose-password": "{.data['database-password']}" + } + }, + "stringData" : { + "database-password" : "${REDIS_PASSWORD}" + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" + } + }, + "spec": { + "ports": [ + { + "name": "redis", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379, + "nodePort": 0 + } + ], + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } + }, + "spec": { + "strategy": { + "type": "Recreate" + }, + "triggers": [ + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "redis" + ], + "from": { + "kind": "ImageStreamTag", + "name": "redis:${REDIS_VERSION}", + "namespace": "${NAMESPACE}" + }, + "lastTriggeredImage": "" + } + }, + { + "type": "ConfigChange" + } + ], + "replicas": 1, + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "template": { + "metadata": { + "labels": { + "name": "${DATABASE_SERVICE_NAME}" + } + }, + "spec": { + "containers": [ + { + "name": "redis", + "image": " ", + "ports": [ + { + "containerPort": 6379, + "protocol": "TCP" + } + ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "test \"$(redis-cli -h 127.0.0.1 -a $REDIS_PASSWORD ping)\" == \"PONG\""] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 6379 + } + }, + "env": [ + { + "name": "REDIS_PASSWORD", + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-password" + } + } + } + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, + "volumeMounts": [ + { + "name": "${DATABASE_SERVICE_NAME}-data", + "mountPath": "/var/lib/redis/data" + } + ], + "terminationMessagePath": "/dev/termination-log", + "imagePullPolicy": "IfNotPresent", + "capabilities": {}, + "securityContext": { + "capabilities": {}, + "privileged": false + } + } + ], + "volumes": [ + { + "name": "${DATABASE_SERVICE_NAME}-data", + "emptyDir": { + "medium": "" + } + } + ], + "restartPolicy": "Always", + "dnsPolicy": "ClusterFirst" + } + } + }, + "status": {} + } + ], + "parameters": [ + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi", + "required": true + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "DATABASE_SERVICE_NAME", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", + "value": "redis", + "required": true + }, + { + "name": "REDIS_PASSWORD", + "displayName": "Redis Connection Password", + "description": "Password for the Redis connection user.", + "generate": "expression", + "from": "[a-zA-Z0-9]{16}", + "required": true + }, + { + "name": "REDIS_VERSION", + "displayName": "Version of Redis Image", + "description": "Version of Redis image to be used (5-el7, 5-el8, or latest).", + "value": "5-el8", + "required": true + } + ] +} diff --git a/test/examples/redis-persistent-template.json b/test/examples/redis-persistent-template.json new file mode 100644 index 0000000..253e0f8 --- /dev/null +++ b/test/examples/redis-persistent-template.json @@ -0,0 +1,240 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "redis-persistent", + "annotations": { + "openshift.io/display-name": "Redis", + "description": "Redis in-memory data structure store, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", + "iconClass": "icon-redis", + "tags": "database,redis", + "openshift.io/long-description": "This template provides a standalone Redis server. The data is stored on persistent storage.", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "openshift.io/documentation-url": "https://github.com/sclorg/redis-container/tree/master/5", + "openshift.io/support-url": "https://access.redhat.com" + } + }, + "message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Password: ${REDIS_PASSWORD}\n Connection URL: redis://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.", + "labels": { + "template": "redis-persistent-template" + }, + "objects": [ + { + "kind": "Secret", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.openshift.io/expose-password": "{.data['database-password']}" + } + }, + "stringData" : { + "database-password" : "${REDIS_PASSWORD}" + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" + } + }, + "spec": { + "ports": [ + { + "name": "redis", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379, + "nodePort": 0 + } + ], + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "PersistentVolumeClaim", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "${VOLUME_CAPACITY}" + } + } + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } + }, + "spec": { + "strategy": { + "type": "Recreate" + }, + "triggers": [ + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "redis" + ], + "from": { + "kind": "ImageStreamTag", + "name": "redis:${REDIS_VERSION}", + "namespace": "${NAMESPACE}" + }, + "lastTriggeredImage": "" + } + }, + { + "type": "ConfigChange" + } + ], + "replicas": 1, + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "template": { + "metadata": { + "labels": { + "name": "${DATABASE_SERVICE_NAME}" + } + }, + "spec": { + "containers": [ + { + "name": "redis", + "image": " ", + "ports": [ + { + "containerPort": 6379, + "protocol": "TCP" + } + ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "test \"$(redis-cli -h 127.0.0.1 -a $REDIS_PASSWORD ping)\" == \"PONG\""] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 6379 + } + }, + "env": [ + { + "name": "REDIS_PASSWORD", + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-password" + } + } + } + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, + "volumeMounts": [ + { + "name": "${DATABASE_SERVICE_NAME}-data", + "mountPath": "/var/lib/redis/data" + } + ], + "terminationMessagePath": "/dev/termination-log", + "imagePullPolicy": "IfNotPresent", + "capabilities": {}, + "securityContext": { + "capabilities": {}, + "privileged": false + } + } + ], + "volumes": [ + { + "name": "${DATABASE_SERVICE_NAME}-data", + "persistentVolumeClaim": { + "claimName": "${DATABASE_SERVICE_NAME}" + } + } + ], + "restartPolicy": "Always", + "dnsPolicy": "ClusterFirst" + } + } + }, + "status": {} + } + ], + "parameters": [ + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi", + "required": true + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "DATABASE_SERVICE_NAME", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", + "value": "redis", + "required": true + }, + { + "name": "REDIS_PASSWORD", + "displayName": "Redis Connection Password", + "description": "Password for the Redis connection user.", + "generate": "expression", + "from": "[a-zA-Z0-9]{16}", + "required": true + }, + { + "name": "VOLUME_CAPACITY", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", + "value": "1Gi", + "required": true + }, + { + "name": "REDIS_VERSION", + "displayName": "Version of Redis Image", + "description": "Version of Redis image to be used (5-el7, 5-el8, or latest).", + "value": "5-el8", + "required": true + } + ] +} diff --git a/test/imagestreams/redis-centos.json b/test/imagestreams/redis-centos.json new file mode 100644 index 0000000..d22b7a1 --- /dev/null +++ b/test/imagestreams/redis-centos.json @@ -0,0 +1,85 @@ +{ + "apiVersion": "v1", + "kind": "ImageStream", + "metadata": { + "annotations": { + "openshift.io/display-name": "Redis" + }, + "name": "redis" + }, + "spec": { + "tags": [ + { + "annotations": { + "description": "Provides a Redis database on CentOS. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Redis available on OpenShift, including major version updates.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis (Latest)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis" + }, + "from": { + "kind": "ImageStreamTag", + "name": "5-el8" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "latest" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on CentOS 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5 (CentOS 8)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "docker.io/centos/redis-5-centos8:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5-el8" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on CentOS 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5 (CentOS 7)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "quay.io/centos7/redis-5-centos7:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5-el7" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on CentOS 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis,hidden", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "quay.io/centos7/redis-5-centos7:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5" + } + ] + } +} diff --git a/test/imagestreams/redis-rhel-aarch64.json b/test/imagestreams/redis-rhel-aarch64.json new file mode 100644 index 0000000..ad6f208 --- /dev/null +++ b/test/imagestreams/redis-rhel-aarch64.json @@ -0,0 +1,67 @@ +{ + "apiVersion": "v1", + "kind": "ImageStream", + "metadata": { + "annotations": { + "openshift.io/display-name": "Redis" + }, + "name": "redis" + }, + "spec": { + "tags": [ + { + "annotations": { + "description": "Provides a Redis database on RHEL. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Redis available on OpenShift, including major version updates.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis (Latest)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis" + }, + "from": { + "kind": "ImageStreamTag", + "name": "6-el8" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "latest" + }, + { + "annotations": { + "description": "Provides a Redis 6 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 6 (RHEL 8)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "6" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhel8/redis-6:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "6-el8" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5 (RHEL 8)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhel8/redis-5:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5-el8" + } + ] + } +} diff --git a/test/imagestreams/redis-rhel.json b/test/imagestreams/redis-rhel.json new file mode 100644 index 0000000..c74e444 --- /dev/null +++ b/test/imagestreams/redis-rhel.json @@ -0,0 +1,103 @@ +{ + "apiVersion": "v1", + "kind": "ImageStream", + "metadata": { + "annotations": { + "openshift.io/display-name": "Redis" + }, + "name": "redis" + }, + "spec": { + "tags": [ + { + "annotations": { + "description": "Provides a Redis database on RHEL. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Redis available on OpenShift, including major version updates.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis (Latest)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis" + }, + "from": { + "kind": "ImageStreamTag", + "name": "6-el8" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "latest" + }, + { + "annotations": { + "description": "Provides a Redis 6 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 6 (RHEL 8)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "6" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhel8/redis-6:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "6-el8" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5 (RHEL 8)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhel8/redis-5:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5-el8" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on RHEL 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5 (RHEL 7)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhscl/redis-5-rhel7:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5-el7" + }, + { + "annotations": { + "description": "Provides a Redis 5 database on RHEL 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 5", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis,hidden", + "version": "5" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhscl/redis-5-rhel7:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "5" + } + ] + } +} diff --git a/test/redis-ephemeral-template.json b/test/redis-ephemeral-template.json new file mode 100644 index 0000000..387ce62 --- /dev/null +++ b/test/redis-ephemeral-template.json @@ -0,0 +1,216 @@ +{ + "kind": "Template", + "apiVersion": "v1", + "metadata": { + "name": "redis-ephemeral", + "annotations": { + "openshift.io/display-name": "Redis (Ephemeral)", + "description": "Redis in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", + "iconClass": "icon-redis", + "tags": "database,redis", + "openshift.io/long-description": "This template provides a standalone Redis server. The data is not stored on persistent storage, so any restart of the service will result in all data being lost.", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "openshift.io/documentation-url": "https://github.com/sclorg/redis-container/tree/master/5", + "openshift.io/support-url": "https://access.redhat.com" + } + }, + "message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Password: ${REDIS_PASSWORD}\n Connection URL: redis://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.", + "labels": { + "template": "redis-ephemeral-template" + }, + "objects": [ + { + "kind": "Secret", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.openshift.io/expose-password": "{.data['database-password']}" + } + }, + "stringData" : { + "database-password" : "${REDIS_PASSWORD}" + } + }, + { + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" + } + }, + "spec": { + "ports": [ + { + "name": "redis", + "protocol": "TCP", + "port": 6379, + "targetPort": 6379, + "nodePort": 0 + } + ], + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } + }, + { + "kind": "DeploymentConfig", + "apiVersion": "v1", + "metadata": { + "name": "${DATABASE_SERVICE_NAME}", + "annotations": { + "template.alpha.openshift.io/wait-for-ready": "true" + } + }, + "spec": { + "strategy": { + "type": "Recreate" + }, + "triggers": [ + { + "type": "ImageChange", + "imageChangeParams": { + "automatic": true, + "containerNames": [ + "redis" + ], + "from": { + "kind": "ImageStreamTag", + "name": "redis:${REDIS_VERSION}", + "namespace": "${NAMESPACE}" + }, + "lastTriggeredImage": "" + } + }, + { + "type": "ConfigChange" + } + ], + "replicas": 1, + "selector": { + "name": "${DATABASE_SERVICE_NAME}" + }, + "template": { + "metadata": { + "labels": { + "name": "${DATABASE_SERVICE_NAME}" + } + }, + "spec": { + "containers": [ + { + "name": "redis", + "image": " ", + "ports": [ + { + "containerPort": 6379, + "protocol": "TCP" + } + ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "test \"$(redis-cli -h 127.0.0.1 -a $REDIS_PASSWORD ping)\" == \"PONG\""] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 6379 + } + }, + "env": [ + { + "name": "REDIS_PASSWORD", + "valueFrom": { + "secretKeyRef" : { + "name" : "${DATABASE_SERVICE_NAME}", + "key" : "database-password" + } + } + } + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, + "volumeMounts": [ + { + "name": "${DATABASE_SERVICE_NAME}-data", + "mountPath": "/var/lib/redis/data" + } + ], + "terminationMessagePath": "/dev/termination-log", + "imagePullPolicy": "IfNotPresent", + "capabilities": {}, + "securityContext": { + "capabilities": {}, + "privileged": false + } + } + ], + "volumes": [ + { + "name": "${DATABASE_SERVICE_NAME}-data", + "emptyDir": { + "medium": "" + } + } + ], + "restartPolicy": "Always", + "dnsPolicy": "ClusterFirst" + } + } + }, + "status": {} + } + ], + "parameters": [ + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi", + "required": true + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "DATABASE_SERVICE_NAME", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", + "value": "redis", + "required": true + }, + { + "name": "REDIS_PASSWORD", + "displayName": "Redis Connection Password", + "description": "Password for the Redis connection user.", + "generate": "expression", + "from": "[a-zA-Z0-9]{16}", + "required": true + }, + { + "name": "REDIS_VERSION", + "displayName": "Version of Redis Image", + "description": "Version of Redis image to be used (5-el7, 5-el8, or latest).", + "value": "5-el8", + "required": true + } + ] +} diff --git a/test/run b/test/run new file mode 100755 index 0000000..567ed16 --- /dev/null +++ b/test/run @@ -0,0 +1,359 @@ +#!/bin/bash +# +# Test the Redis image. +# +# IMAGE_NAME specifies the name of the candidate image used for testing. +# The image has to be available before this script is executed. +# + +set -o errexit +set -o nounset +shopt -s nullglob + +[ "${DEBUG:-0}" -eq 1 ] && set -x + +test -n "${IMAGE_NAME-}" || { echo 'make sure $IMAGE_NAME is defined' && false ;} +test -n "${VERSION-}" || { echo 'make sure $VERSION is defined' && false; } +test -n "${OS-}" || { echo 'make sure $OS is defined' && false; } + +test_short_summary='' +TESTSUITE_RESULT=0 + +TEST_LIST="\ +run_container_creation_tests +run_tests_no_root +run_tests_no_pass +run_tests_no_pass_altuid +run_tests_no_root_altuid +run_change_password_test +run_doc_test +" + +CIDFILE_DIR=$(mktemp --suffix=redis_test_cidfiles -d) + +function cleanup() { + local cidfile + for cidfile in $CIDFILE_DIR/* ; do + local CONTAINER + CONTAINER=$(cat $cidfile) + + echo "Stopping and removing container $CONTAINER..." + docker stop $CONTAINER >/dev/null + local exit_status + exit_status=$(docker inspect -f '{{.State.ExitCode}}' $CONTAINER) + if [ "$exit_status" != "0" ]; then + echo "Inspecting container $CONTAINER" + docker inspect $CONTAINER + echo "Dumping logs for $CONTAINER" + docker logs $CONTAINER + fi + docker rm -v $CONTAINER >/dev/null + rm $cidfile + echo "Done." + done + rmdir $CIDFILE_DIR + + 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 +} +trap cleanup EXIT SIGINT + +check_result() { + local result="$1" + if [[ "$result" != "0" ]]; then + TESTCASE_RESULT=1 + fi + return $result +} + +function get_cid() { + local id="$1" ; shift || return 1 + echo $(cat "$CIDFILE_DIR/$id") +} + +function get_container_ip() { + local id="$1" ; shift + docker inspect --format='{{.NetworkSettings.IPAddress}}' $(get_cid "$id") +} + +function connection_works() { + local container_ip="$1"; shift + local password="$1"; shift + if [ "$(redis_cmd "$container_ip" "$password" ping)" == "PONG" ] ; then + return 0 + fi + return 1 +} + +function redis_cmd() { + local container_ip="$1"; shift + local password="$1"; shift + # if empty password is given, then no password will be specified + docker run --rm "$IMAGE_NAME" redis-cli -h "$container_ip" ${password:+-a "$password"} "$@" +} + +function test_connection() { + local name=$1 ; shift + local password=$1 ; shift + local ip + ip=$(get_container_ip $name) + echo " Testing Redis connection to $ip (password='${password:-}')..." + local max_attempts=10 + local sleep_time=2 + local i + for i in $(seq $max_attempts); do + echo " Trying to connect..." + if connection_works "$ip" "$password" ; then + echo " Success!" + echo + return 0 + fi + sleep $sleep_time + done + echo " Giving up: Failed to connect. Logs:" + docker logs $(get_cid $name) + return 1 +} + +function test_redis() { + local container_ip="$1" + local password="$2" + + echo " Testing Redis (password='${password:-}')" + redis_cmd "$container_ip" "$password" set a 1 >/dev/null + check_result $? + redis_cmd "$container_ip" "$password" set b 2 >/dev/null + check_result $? + test "$(redis_cmd "$container_ip" "$password" get b)" == '2' + echo " Success!" + echo +} + +function create_container() { + local name=$1 ; shift + cidfile="$CIDFILE_DIR/$name" + # create container with a cidfile in a directory for cleanup + local container_id + [ "${DEBUG:-0}" -eq 1 ] && echo "DEBUG: docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d \"$@\" $IMAGE_NAME ${CONTAINER_ARGS:-}" >&2 + container_id="$(docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d "$@" $IMAGE_NAME ${CONTAINER_ARGS:-})" + [ "${DEBUG:-0}" -eq 1 ] && echo "Created container $container_id" + [ x"$container_id" == "x" ] && return 1 || return 0 +} + +function run_change_password_test() { + local tmpdir=$(mktemp -d) + mkdir "${tmpdir}/data" && chmod -R a+rwx "${tmpdir}" + + # Create Redis container with persistent volume and set the initial password + create_container "testpass1" -e REDIS_PASSWORD=foo \ + -v ${tmpdir}:/var/lib/redis/data:Z + check_result $? + test_connection testpass1 foo + check_result $? + docker stop $(get_cid testpass1) >/dev/null + + # Create second container with changed password + create_container "testpass2" -e REDIS_PASSWORD=bar \ + -v ${tmpdir}:/var/lib/redis/data:Z + check_result $? + test_connection testpass2 bar + check_result $? + # The old password should not work anymore + container_ip="$(get_container_ip testpass2)" + connection_works "$container_ip" foo + check_result $? +} + +function assert_login_access() { + local container_ip=$1; shift + local PASS=$1 ; shift + local success=$1 ; shift + + if connection_works "$container_ip" "$PASS" ; then + if $success ; then + echo " Connection ($PASS) access granted as expected" + return 0 + fi + else + if ! $success ; then + echo " Connection ($PASS) access denied as expected" + return 0 + fi + fi + echo " Connection ($PASS) login assertion failed" + return 1 +} + +function assert_local_access() { + local id="$1" ; shift + docker exec $(get_cid "$id") bash -c 'redis-cli ping' +} + +# Make sure the invocation of docker run fails. +function assert_container_creation_fails() { + + # Time the docker run command. It should fail. If it doesn't fail, + # redis will keep running so we kill it with SIGKILL to make sure + # timeout returns a non-zero value. + local ret=0 + timeout -s 9 --preserve-status 60s docker run --rm "$@" $IMAGE_NAME >/dev/null || ret=$? + + # Timeout will exit with a high number. + if [ $ret -gt 10 ]; then + return 1 + fi +} + +function try_image_invalid_combinations() { + assert_container_creation_fails -e REDIS_PASSWORD="pass with space" "$@" + check_result $? +} + +function run_container_creation_tests() { + local ret + echo " Testing image entrypoint usage" + try_image_invalid_combinations + ret=$? + if [ $ret -eq 0 ]; then + echo " Success!" + else + echo " Failed!" + fi + echo + return $ret +} + +test_scl_usage() { + local name="$1" + local run_cmd="$2" + local expected="$3" + + echo " Testing the image SCL enable" + local out + out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}") + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi + out=$(docker exec $(get_cid $name) /bin/bash -c "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi + out=$(docker exec $(get_cid $name) /bin/sh -ic "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi +} + +run_doc_test() { + local tmpdir=$(mktemp -d) + local f + echo " Testing documentation in the container image" + # Extract the help.1 file from the container + docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /help.1" >${tmpdir}/help.1 + # Check whether the help.1 file includes some important information + for term in 6379 "REDIS\_PASSWORD" volume; do + if ! cat ${tmpdir}/help.1 | grep -F -q -e "${term}" ; then + echo "ERROR: File /help.1 does not include '${term}'." + return 1 + fi + done + # Check whether the file uses the correct format + if ! file ${tmpdir}/help.1 | grep -q roff ; then + echo "ERROR: /help.1 is not in troff or groff format" + return 1 + fi + echo " Success!" + echo +} + +function run_tests() { + local name=$1 ; shift + local ret + envs=${PASS:+"-e REDIS_PASSWORD=$PASS"} + PASS=${PASS:-} + create_container $name $envs + ret=$? + check_result $ret + test_connection "$name" "$PASS" + ret=$? + check_result $ret + # Only check version on rhel/centos builds + if [ "$OS" != "fedora" ]; then + echo " Testing scl usage" + test_scl_usage $name 'redis-server --version' "$VERSION" + check_result $? + fi + echo " Testing login accesses" + local container_ip + container_ip=$(get_container_ip $name) + assert_login_access "$container_ip" "$PASS" true + ret=$? + check_result $ret + if [ -n "$PASS" ] ; then + assert_login_access "$container_ip" "${PASS}_foo" false + check_result $? + fi + assert_local_access "$name" + ret=$? + check_result $ret + if [ $ret -ne 0 ]; then + echo " Local access FAILED." + else + echo " Local access SUCCESS." + fi + echo + test_redis "$container_ip" "$PASS" + check_result $? +} + +function run_tests_no_root() { + # Normal tests with password + PASS=pass run_tests no_root +} + +function run_tests_no_pass() { + # Normal tests without password + run_tests no_pass +} + +function run_tests_no_pass_altuid() { + # Test with arbitrary uid for the container without password + DOCKER_ARGS="-u 12345" run_tests no_pass_altuid +} + +function run_tests_no_root_altuid() { + # Test with arbitrary uid for the container with password + DOCKER_ARGS="-u 12345" PASS=pass run_tests no_root_altuid +} + +function run_all_tests() { + for test_case in $TEST_SET; do + echo "Running test $test_case ...." + TESTCASE_RESULT=0 + $test_case + check_result $? + local test_msg + if [ $TESTCASE_RESULT -eq 0 ]; then + test_msg="[PASSED]" + else + test_msg="[FAILED]" + TESTSUITE_RESULT=1 + fi + printf -v test_short_summary "%s %s for '%s' %s\n" "${test_short_summary}" "${test_msg}" "$test_case" + [ -n "${FAIL_QUICKLY:-}" ] && cleanup "${APP_NAME}" && return 1 + done; +} + +TEST_SET=${TESTS:-$TEST_LIST} run_all_tests + +echo "Success!" +cleanup diff --git a/test/run-openshift b/test/run-openshift new file mode 120000 index 0000000..d84575f --- /dev/null +++ b/test/run-openshift @@ -0,0 +1 @@ +run-openshift-local-cluster \ No newline at end of file diff --git a/test/run-openshift-local-cluster b/test/run-openshift-local-cluster new file mode 100755 index 0000000..e4dbbc1 --- /dev/null +++ b/test/run-openshift-local-cluster @@ -0,0 +1,119 @@ +#!/bin/bash +# +# Test the Redis 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-openshift.sh +source ${THISDIR}/test-lib-redis.sh + +set -eo nounset + +trap ct_os_cleanup EXIT SIGINT + +ct_os_check_compulsory_vars + +ct_os_enable_print_logs + +function check_redis_os_service_connection() { + local util_image_name=$1 ; shift + local service_name=$1 ; shift + local pass=$1 ; shift + local timeout=${1:-60} ; shift || : + local pod_ip=$(ct_os_get_service_ip ${service_name}) + + : " Service ${service_name} check ..." + + local cmd="timeout 15 redis-cli -h $pod_ip -a $pass ping" + local expected_value="PONG" + local output + local ret + SECONDS=0 + + echo -n "Waiting for ${service_name} service becoming ready ..." + while true ; do + output=$(docker run --rm ${util_image_name} bash -c "${cmd}" || :) + echo "${output}" | grep -qe "${expected_value}" && ret=0 || ret=1 + if [ ${ret} -eq 0 ] ; then + echo " PASS" + return 0 + fi + echo -n "." + [ ${SECONDS} -gt ${timeout} ] && break + sleep 3 + done + echo " FAIL" + return 1 +} + +function test_redis_pure_image() { + local image_name=$1 + local image_name_no_namespace=${image_name##*/} + local service_name="${image_name_no_namespace%%:*}-testing" + + ct_os_new_project + # Create a specific imagestream tag for the image so that oc cannot use anything else + ct_os_upload_image "${image_name}" "$image_name_no_namespace" + + ct_os_deploy_pure_image "$image_name_no_namespace" \ + --name "${service_name}" \ + --env REDIS_PASSWORD=pass + + ct_os_wait_pod_ready "${service_name}" 60 + check_redis_os_service_connection "${image_name}" "${service_name}" pass + + ct_os_delete_project +} + +function test_redis_template() { + local image_name=${1:-quay.io/centos7/redis-5-centos7} + local image_name_no_namespace=${image_name##*/} + local service_name="${image_name_no_namespace%%:*}-testing" + + ct_os_new_project + ct_os_upload_image "${image_name}" "redis:$VERSION" + + ct_os_deploy_template_image "$THISDIR/redis-ephemeral-template.json" \ + NAMESPACE="$(oc project -q)" \ + REDIS_VERSION="$VERSION" \ + DATABASE_SERVICE_NAME="${service_name}" \ + REDIS_PASSWORD=pass + + ct_os_wait_pod_ready "${service_name}" 60 + check_redis_os_service_connection "${image_name}" "${service_name}" pass + + ct_os_delete_project +} + +ct_os_cluster_up +test_redis_pure_image "${IMAGE_NAME}" +test_redis_template "${IMAGE_NAME}" + +# test with the just built image and an integrated template +test_redis_integration "${IMAGE_NAME}" + +# test with a released image and an integrated template +PUBLIC_IMAGE_NAME=${PUBLIC_IMAGE_NAME:-$(ct_get_public_image_name "${OS}" "${BASE_IMAGE_NAME}" "${VERSION}")} + +# Try pulling the image first to see if it is accessible +if ct_check_image_availability "$PUBLIC_IMAGE_NAME"; then + test_redis_integration "${PUBLIC_IMAGE_NAME}" +else + echo "Warning: ${PUBLIC_IMAGE_NAME} could not be downloaded via 'docker'" + # ignore possible failure of this test for centos images + [ "${OS}" == "rhel7" ] && false "ERROR: Failed to pull image" +fi + +# Check the imagestream +test_redis_imagestream + +OS_TESTSUITE_RESULT=0 + +ct_os_cluster_down + +# vim: set tabstop=2:shiftwidth=2:expandtab: + diff --git a/test/run-openshift-remote-cluster b/test/run-openshift-remote-cluster new file mode 100755 index 0000000..2f717db --- /dev/null +++ b/test/run-openshift-remote-cluster @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Test the Redis image in OpenShift (remote cluster) +# +# IMAGE_NAME specifies a name of the candidate image used for testing. +# The image has to be available before this script is executed. +# VERSION specifies the major version of the Redis in format of X.Y +# OS specifies RHEL version (e.g. OS=rhel7) +# + +THISDIR=$(dirname ${BASH_SOURCE[0]}) + +source ${THISDIR}/test-lib-redis.sh + +set -eo nounset + +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 external registry +export CT_EXTERNAL_REGISTRY=true + +ct_os_set_ocp4 + +# Check the template +test_redis_integration "${IMAGE_NAME}" + +# Check the imagestream +test_redis_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 new file mode 100644 index 0000000..fa15eae --- /dev/null +++ b/test/test-lib-openshift.sh @@ -0,0 +1,1343 @@ +# 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 +OS_TESTSUITE_RESULT=1 +OS_CLUSTER_STARTED_BY_TEST=0 + +function ct_os_cleanup() { + if [ $OS_TESTSUITE_RESULT -eq 0 ] ; then + # shellcheck disable=SC2153 + echo "OpenShift tests for ${IMAGE_NAME} succeeded." + else + # shellcheck disable=SC2153 + echo "OpenShift tests for ${IMAGE_NAME} failed." + fi +} + +# ct_os_check_compulsory_vars +# --------------------------- +# Check the compulsory variables: +# * IMAGE_NAME specifies a name of the candidate image used for testing. +# * VERSION specifies the major version of the MariaDB in format of X.Y +# * OS specifies RHEL version (e.g. OS=rhel7) +function ct_os_check_compulsory_vars() { + # shellcheck disable=SC2016 + test -n "${IMAGE_NAME-}" || ( echo 'make sure $IMAGE_NAME is defined' >&2 ; exit 1) + # shellcheck disable=SC2016 + test -n "${VERSION-}" || ( echo 'make sure $VERSION is defined' >&2 ; exit 1) + # shellcheck disable=SC2016 + test -n "${OS-}" || ( echo 'make sure $OS is defined' >&2 ; exit 1) +} + +# ct_os_get_status +# -------------------- +# Returns status of all objects to make debugging easier. +function ct_os_get_status() { + oc get all + oc status +} + +# ct_os_print_logs +# -------------------- +# Returns status of all objects and logs from all pods. +function ct_os_print_logs() { + ct_os_get_status + while read -r pod_name; do + echo "INFO: printing logs for pod ${pod_name}" + oc logs "${pod_name}" + done < <(oc get pods --no-headers=true -o custom-columns=NAME:.metadata.name) +} + +# ct_os_enable_print_logs +# -------------------- +# Enables automatic printing of pod logs on ERR. +function ct_os_enable_print_logs() { + set -E + trap ct_os_print_logs ERR +} + +# ct_get_public_ip +# -------------------- +# Returns best guess for the IP that the node is accessible from other computers. +# This is a bit funny heuristic, simply goes through all IPv4 addresses that +# hostname -I returns and de-prioritizes IP addresses commonly used for local +# addressing. The rest of addresses are taken as public with higher probability. +function ct_get_public_ip() { + local hostnames + local public_ip='' + local found_ip + hostnames=$(hostname -I) + for guess_exp in '127\.0\.0\.1' '192\.168\.[0-9\.]*' '172\.[0-9\.]*' \ + '10\.[0-9\.]*' '[0-9\.]*' ; do + found_ip=$(echo "${hostnames}" | grep -oe "${guess_exp}") + if [ -n "${found_ip}" ] ; then + # shellcheck disable=SC2001 + hostnames=$(echo "${hostnames}" | sed -e "s/${found_ip}//") + public_ip="${found_ip}" + fi + done + if [ -z "${public_ip}" ] ; then + echo "ERROR: public IP could not be guessed." >&2 + return 1 + fi + echo "${public_ip}" +} + +# ct_os_run_in_pod POD_NAME CMD +# -------------------- +# Runs [cmd] in the pod specified by prefix [pod_prefix]. +# Arguments: pod_name - full name of the pod +# Arguments: cmd - command to be run in the pod +function ct_os_run_in_pod() { + local pod_name="$1" ; shift + + oc exec "$pod_name" -- "$@" +} + +# ct_os_get_service_ip SERVICE_NAME +# -------------------- +# Returns IP of the service specified by [service_name]. +# Arguments: service_name - name of the service +function ct_os_get_service_ip() { + local service_name="${1}" ; shift + oc get "svc/${service_name}" -o yaml | grep clusterIP | \ + cut -d':' -f2 | grep -oe '172\.30\.[0-9\.]*' +} + + +# ct_os_get_all_pods_status +# -------------------- +# Returns status of all pods. +function ct_os_get_all_pods_status() { + oc get pods -o custom-columns=Ready:status.containerStatuses[0].ready,NAME:.metadata.name +} + +# ct_os_get_all_pods_name +# -------------------- +# Returns the full name of all pods. +function ct_os_get_all_pods_name() { + oc get pods --no-headers -o custom-columns=NAME:.metadata.name +} + +# ct_os_get_pod_status POD_PREFIX +# -------------------- +# Returns status of the pod specified by prefix [pod_prefix]. +# Note: Ignores -build and -deploy pods +# Arguments: pod_prefix - prefix or whole ID of the pod +function ct_os_get_pod_status() { + local pod_prefix="${1}" ; shift + ct_os_get_all_pods_status | grep -e "${pod_prefix}" | grep -Ev "(build|deploy)$" \ + | awk '{print $1}' | head -n 1 +} + +# ct_os_get_build_pod_status POD_PREFIX +# -------------------- +# Returns status of the build pod specified by prefix [pod_prefix]. +# Arguments: pod_prefix - prefix or whole ID of the pod +function ct_os_get_build_pod_status() { + local pod_prefix="${1}" ; shift + local query="custom-columns=NAME:.metadata.name,Ready:status.phase" + oc get pods -o "$query" | grep -e "${pod_prefix}" | grep -E "\-build\s" \ + | sort -u | awk '{print $2}' | tail -n 1 +} + +# ct_os_get_buildconfig_pod_name POD_PREFIX +# ---------------------------- +# Returns status of the buildconfig pod specified by prefix [pod_prefix]. +# Argument: pod_prefix - prefix +function ct_os_get_buildconfig_pod_name() { + local pod_prefix="${1}" ; shift + local query="custom-columns=NAME:.metadata.name" + oc get bc -o "$query" | grep -e "${pod_prefix}" | sort -u | tail -n 1 +} + +# ct_os_get_pod_name POD_PREFIX +# -------------------- +# Returns the full name of pods specified by prefix [pod_prefix]. +# Note: Ignores -build and -deploy pods +# Arguments: pod_prefix - prefix or whole ID of the pod +function ct_os_get_pod_name() { + local pod_prefix="${1}" ; shift + ct_os_get_all_pods_name | grep -e "^${pod_prefix}" | grep -Ev "(build|deploy)$" +} + +# ct_os_get_pod_ip POD_NAME +# -------------------- +# Returns the ip of the pod specified by [pod_name]. +# Arguments: pod_name - full name of the pod +function ct_os_get_pod_ip() { + local pod_name="${1}" + oc get pod "$pod_name" --no-headers -o custom-columns=IP:status.podIP +} + +# ct_os_get_sti_build_logs +# ----------------- +# Return logs from sti_build +# Arguments: pod_name +function ct_os_get_sti_build_logs() { + local pod_prefix="${1}" + pod_name=$(ct_os_get_buildconfig_pod_name "${pod_prefix}") + # Print logs but do not failed. Just for traces + if [ x"${pod_name}" != "x" ]; then + oc logs "bc/$pod_name" || return 0 + else + echo "Build config bc/$pod_name does not exist for some reason." + echo "Import probably failed." + fi +} + +# ct_os_check_pod_readiness POD_PREFIX STATUS +# -------------------- +# Checks whether the pod is ready. +# Arguments: pod_prefix - prefix or whole ID of the pod +# Arguments: status - expected status (true, false) +function ct_os_check_pod_readiness() { + local pod_prefix="${1}" ; shift + local status="${1}" ; shift + test "$(ct_os_get_pod_status "${pod_prefix}")" == "${status}" +} + +# ct_os_wait_pod_ready POD_PREFIX TIMEOUT +# -------------------- +# Wait maximum [timeout] for the pod becomming ready. +# Arguments: pod_prefix - prefix or whole ID of the pod +# Arguments: timeout - how many seconds to wait seconds +function ct_os_wait_pod_ready() { + local pod_prefix="${1}" ; shift + local timeout="${1}" ; shift + # If there is a build pod - wait for it to finish first + sleep 3 + if ct_os_get_all_pods_name | grep -E "${pod_prefix}.*-build"; then + SECONDS=0 + echo -n "Waiting for ${pod_prefix} build pod to finish ..." + while ! [ "$(ct_os_get_build_pod_status "${pod_prefix}")" == "Succeeded" ] ; do + echo -n "." + if [ "${SECONDS}" -gt "${timeout}0" ]; then + echo " FAIL" + ct_os_print_logs || : + ct_os_get_sti_build_logs "${pod_prefix}" || : + return 1 + fi + sleep 3 + done + echo " DONE" + fi + SECONDS=0 + echo -n "Waiting for ${pod_prefix} pod becoming ready ..." + while ! ct_os_check_pod_readiness "${pod_prefix}" "true" ; do + echo -n "." + if [ "${SECONDS}" -gt "${timeout}" ]; then + echo " FAIL"; + ct_os_print_logs || : + ct_os_get_sti_build_logs "${pod_prefix}" || : + return 1 + fi + sleep 3 + done + echo " DONE" +} + +# ct_os_wait_rc_ready POD_PREFIX TIMEOUT +# -------------------- +# Wait maximum [timeout] for the rc having desired number of replicas ready. +# Arguments: pod_prefix - prefix of the replication controller +# Arguments: timeout - how many seconds to wait seconds +function ct_os_wait_rc_ready() { + local pod_prefix="${1}" ; shift + local timeout="${1}" ; shift + SECONDS=0 + echo -n "Waiting for ${pod_prefix} having desired numbers of replicas ..." + while ! test "$( (oc get --no-headers statefulsets; oc get --no-headers rc) 2>/dev/null \ + | grep "^${pod_prefix}" | awk '$2==$3 {print "ready"}')" == "ready" ; do + echo -n "." + if [ "${SECONDS}" -gt "${timeout}" ]; then + echo " FAIL"; + ct_os_print_logs || : + ct_os_get_sti_build_logs "${pod_prefix}" || : + return 1 + fi + sleep 3 + done + echo " DONE" +} + +# ct_os_deploy_pure_image IMAGE [ENV_PARAMS, ...] +# -------------------- +# Runs [image] in the openshift and optionally specifies env_params +# as environment variables to the image. +# Arguments: image - prefix or whole ID of the pod to run the cmd in +# Arguments: env_params - environment variables parameters for the images. +function ct_os_deploy_pure_image() { + local image="${1}" ; shift + # ignore error exit code, because oc new-app returns error when image exists + oc new-app "${image}" "$@" || : + # let openshift cluster to sync to avoid some race condition errors + sleep 3 +} + +# ct_os_deploy_s2i_image IMAGE APP [ENV_PARAMS, ... ] +# -------------------- +# Runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. +# Arguments: image - prefix or whole ID of the pod to run the cmd in +# Arguments: app - url or local path to git repo with the application sources. +# Arguments: env_params - environment variables parameters for the images. +function ct_os_deploy_s2i_image() { + local image="${1}" ; shift + local app="${1}" ; shift + # ignore error exit code, because oc new-app returns error when image exists + oc new-app "${image}~${app}" --strategy=source "$@" || : + + # let openshift cluster to sync to avoid some race condition errors + sleep 3 +} + +# ct_os_deploy_template_image TEMPLATE [ENV_PARAMS, ...] +# -------------------- +# Runs template in the openshift and optionally gives env_params to use +# specific values in the template. +# Arguments: template - prefix or whole ID of the pod to run the cmd in +# Arguments: env_params - environment variables parameters for the template. +# Example usage: ct_os_deploy_template_image mariadb-ephemeral-template.yaml \ +# DATABASE_SERVICE_NAME=mysql-57-centos7 \ +# DATABASE_IMAGE=mysql-57-centos7 \ +# MYSQL_USER=testu \ +# MYSQL_PASSWORD=testp \ +# MYSQL_DATABASE=testdb +function ct_os_deploy_template_image() { + local template="${1}" ; shift + oc process -f "${template}" "$@" | oc create -f - + # let openshift cluster to sync to avoid some race condition errors + sleep 3 +} + +# _ct_os_get_uniq_project_name +# -------------------- +# Returns a uniq name of the OpenShift project. +function _ct_os_get_uniq_project_name() { + local r + while true ; do + r=${RANDOM} + mkdir /var/tmp/sclorg-test-${r} &>/dev/null && echo sclorg-test-${r} && break + done +} + +# ct_os_new_project [PROJECT] +# -------------------- +# Creates a new project in the openshfit using 'os' command. +# Arguments: project - project name, uses a new random name if omitted +# Expects 'os' command that is properly logged in to the OpenShift cluster. +# Not using mktemp, because we cannot use uppercase characters. +# The OPENSHIFT_CLUSTER_PULLSECRET_PATH environment variable can be set +# to contain a path to a k8s secret definition which will be used +# to authenticate to image registries. +# shellcheck disable=SC2120 +function ct_os_new_project() { + if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then + echo "Creating project skipped." + return + fi + local project_name="${1:-$(_ct_os_get_uniq_project_name)}" ; shift || : + oc new-project "${project_name}" + # let openshift cluster to sync to avoid some race condition errors + sleep 3 + if test -n "${OPENSHIFT_CLUSTER_PULLSECRET_PATH:-}" -a -e "${OPENSHIFT_CLUSTER_PULLSECRET_PATH:-}"; then + oc create -f "$OPENSHIFT_CLUSTER_PULLSECRET_PATH" + # add registry pullsecret to the serviceaccount if provided + secret_name=$(grep '^\s*name:' "$OPENSHIFT_CLUSTER_PULLSECRET_PATH" | awk '{ print $2 }') + oc secrets link --for=pull default "$secret_name" + fi +} + +# ct_os_delete_project [PROJECT] +# -------------------- +# Deletes the specified project in the openshfit +# Arguments: project - project name, uses the current project if omitted +# shellcheck disable=SC2120 +function ct_os_delete_project() { + if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then + echo "Deleting project skipped, cleaning objects only." + # when not having enough privileges (remote cluster), it might fail and + # it is not a big problem, so ignore failure in this case + ct_delete_all_objects || : + return + fi + local project_name="${1:-$(oc project -q)}" ; shift || : + if oc delete project "${project_name}" ; then + echo "Project ${project_name} was deleted properly" + else + echo "Project ${project_name} was not delete properly. But it does not block CI." + fi + +} + +# ct_delete_all_objects +# ----------------- +# Deletes all objects within the project. +# Handy when we have one project and want to run more tests. +function ct_delete_all_objects() { + for x in bc builds dc is isimage istag po pv pvc rc routes secrets svc ; do + oc delete "$x" --all + done + # for some objects it takes longer to be really deleted, so a dummy sleep + # to avoid some races when other test can see not-yet-deleted objects and can fail + sleep 10 +} + +# ct_os_docker_login +# -------------------- +# 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 + # docker login fails with "404 page not found" error sometimes, just try it more times + # shellcheck disable=SC2034 + for i in $(seq 12) ; do + # shellcheck disable=SC2015 + docker login -u developer -p "$(oc whoami -t)" "${REGISRTY_ADDRESS:-172.30.1.1:5000}" && return 0 || : + sleep 5 + done + return 1 +} + +# ct_os_upload_image IMAGE [IMAGESTREAM] +# -------------------- +# Uploads image from local registry to the OpenShift internal registry. +# Arguments: image - image name to upload +# Arguments: imagestream - name and tag to use for the internal registry. +# 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 input_name="${1}" ; shift + local image_name=${input_name##*/} + local imagestream=${1:-$image_name:latest} + local output_name + + output_name="${REGISRTY_ADDRESS:-172.30.1.1:5000}/$(oc project -q)/$imagestream" + + ct_os_docker_login + docker tag "${input_name}" "${output_name}" + docker push "${output_name}" +} + +# ct_os_is_tag_exists IS_NAME TAG +# -------------------- +# Checks whether the specified tag exists for an image stream +# Arguments: is_name - name of the image stream +# Arguments: tag - name of the tag (usually version) +function ct_os_is_tag_exists() { + local is_name=$1 ; shift + local tag=$1 ; shift + oc get is "${is_name}" -n openshift -o=jsonpath='{.spec.tags[*].name}' | grep -qw "${tag}" +} + +# ct_os_template_exists T_NAME +# -------------------- +# Checks whether the specified template exists for an image stream +# Arguments: t_name - template name of the image stream +function ct_os_template_exists() { + local t_name=$1 ; shift + oc get templates -n openshift | grep -q "^${t_name}\s" +} + +# ct_os_install_in_centos +# -------------------- +# Installs os cluster in CentOS +function ct_os_install_in_centos() { + yum install -y centos-release-openshift-origin + yum install -y wget git net-tools bind-utils iptables-services bridge-utils\ + bash-completion origin-clients docker origin-clients +} + + +# ct_os_cluster_up [DIR, IS_PUBLIC, CLUSTER_VERSION] +# -------------------- +# Runs the local OpenShift cluster using 'oc cluster up' and logs in as developer. +# Arguments: dir - directory to keep configuration data in, random if omitted +# Arguments: is_public - sets either private or public hostname for web-UI, +# use "true" for allow remote access to the web-UI, +# "false" is default +# Arguments: cluster_version - version of the OpenShift cluster to use, empty +# means default version of `oc`; example value: 3.7; +# also can be specified outside by OC_CLUSTER_VERSION +function ct_os_cluster_up() { + ct_os_cluster_running && echo "Cluster already running. Nothing is done." && return 0 + ct_os_logged_in && echo "Already logged in to a cluster. Nothing is done." && return 0 + + mkdir -p /var/tmp/openshift + local dir="${1:-$(mktemp -d /var/tmp/openshift/os-data-XXXXXX)}" ; shift || : + local is_public="${1:-'false'}" ; shift || : + local default_cluster_version=${OC_CLUSTER_VERSION:-} + local cluster_version=${1:-${default_cluster_version}} ; shift || : + if ! grep -qe '--insecure-registry.*172\.30\.0\.0' /etc/sysconfig/docker ; then + sed -i "s|OPTIONS='|OPTIONS='--insecure-registry 172.30.0.0/16 |" /etc/sysconfig/docker + fi + + systemctl stop firewalld || : + setenforce 0 + iptables -F + + systemctl restart docker + local cluster_ip="127.0.0.1" + [ "${is_public}" == "true" ] && cluster_ip=$(ct_get_public_ip) + + if [ -n "${cluster_version}" ] ; then + # if $cluster_version is not set, we simply use oc that is available + ct_os_set_path_oc "${cluster_version}" + fi + + mkdir -p "${dir}"/{config,data,pv} + case $(oc version| head -n 1) in + "oc v3.1"?.*) + oc cluster up --base-dir="${dir}/data" --public-hostname="${cluster_ip}" + ;; + "oc v3."*) + oc cluster up --host-data-dir="${dir}/data" --host-config-dir="${dir}/config" \ + --host-pv-dir="${dir}/pv" --use-existing-config --public-hostname="${cluster_ip}" + ;; + *) + echo "ERROR: Unexpected oc version." >&2 + return 1 + ;; + esac + oc version + oc login -u system:admin + oc project default + ct_os_wait_rc_ready docker-registry 180 + ct_os_wait_rc_ready router 30 + oc login -u developer -p developer + OS_CLUSTER_STARTED_BY_TEST=1 + # let openshift cluster to sync to avoid some race condition errors + sleep 3 +} + +# ct_os_cluster_down +# -------------------- +# Shuts down the local OpenShift cluster using 'oc cluster down' +function ct_os_cluster_down() { + if [ ${OS_CLUSTER_STARTED_BY_TEST:-0} -eq 1 ] ; then + echo "Cluster started by the test, shutting down." + oc cluster down + else + echo "Cluster not started by the test, shutting down skipped." + fi +} + +# ct_os_cluster_running +# -------------------- +# Returns 0 if oc cluster is running +function ct_os_cluster_running() { + oc cluster status &>/dev/null +} + +# ct_os_logged_in +# --------------- +# Returns 0 if logged in to a cluster (remote or local) +function ct_os_logged_in() { + oc whoami >/dev/null +} + +# ct_os_set_path_oc OC_VERSION +# -------------------- +# This is a trick that helps using correct version of the `oc`: +# The input is version of the openshift in format v3.6.0 etc. +# If the currently available version of oc is not of this version, +# it first takes a look into /usr/local/oc-/bin directory, +# and if not found there it downloads the community release from github. +# In the end the PATH variable is changed, so the other tests can still use just 'oc'. +# Arguments: oc_version - X.Y part of the version of OSE (e.g. 3.9) +function ct_os_set_path_oc() { + local oc_version + local oc_path + + oc_version=$(ct_os_get_latest_ver "$1") + + if oc version | grep -q "oc ${oc_version%.*}." ; then + echo "Binary oc found already available in version ${oc_version}: $(command -v oc) Doing noting." + return 0 + fi + + # first check whether we already have oc available in /usr/local + local installed_oc_path="/usr/local/oc-${oc_version%.*}/bin" + + if [ -x "${installed_oc_path}/oc" ] ; then + oc_path="${installed_oc_path}" + echo "Binary oc found in ${installed_oc_path}" >&2 + else + # oc not available in /usr/local, try to download it from github (community release) + oc_path="/tmp/oc-${oc_version}-bin" + ct_os_download_upstream_oc "${oc_version}" "${oc_path}" + fi + if [ -z "${oc_path}" ] ; then + echo "ERROR: oc not found installed, nor downloaded" >&1 + return 1 + fi + export PATH="${oc_path}:${PATH}" + if ! oc version | grep -q "oc ${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_get_latest_ver VERSION_PART_X +# -------------------- +# Returns full version (vX.Y.Z) from part of the version (X.Y) +# Arguments: vxy - X.Y part of the version +# Returns vX.Y.Z variant of the version +function ct_os_get_latest_ver(){ + local vxy="v$1" + for vz in {3..0} ; do + curl -sif "https://github.com/openshift/origin/releases/tag/${vxy}.${vz}" >/dev/null && echo "${vxy}.${vz}" && return 0 + done + echo "ERROR: version ${vxy} not found in https://github.com/openshift/origin/tags" >&2 + return 1 +} + +# ct_os_download_upstream_oc OC_VERSION OUTPUT_DIR +# -------------------- +# Downloads a particular version of openshift-origin-client-tools from +# github into specified output directory +# Arguments: oc_version - version of OSE (e.g. v3.7.2) +# Arguments: output_dir - output directory +function ct_os_download_upstream_oc() { + local oc_version=$1 + local output_dir=$2 + + # check whether we already have the binary in place + [ -x "${output_dir}/oc" ] && return 0 + + mkdir -p "${output_dir}" + # using html output instead of https://api.github.com/repos/openshift/origin/releases/tags/${oc_version}, + # because API is limited for number of queries if not authenticated + tarball=$(curl -si "https://github.com/openshift/origin/releases/tag/${oc_version}" | grep -o -e "openshift-origin-client-tools-${oc_version}-[a-f0-9]*-linux-64bit.tar.gz" | head -n 1) + + # download, unpack the binaries and then put them into output directory + echo "Downloading https://github.com/openshift/origin/releases/download/${oc_version}/${tarball} into ${output_dir}/" >&2 + curl -sL https://github.com/openshift/origin/releases/download/"${oc_version}"/"${tarball}" | tar -C "${output_dir}" -xz + mv -f "${output_dir}"/"${tarball%.tar.gz}"/* "${output_dir}/" + + rmdir "${output_dir}"/"${tarball%.tar.gz}" +} + + +# ct_os_test_s2i_app_func IMAGE APP CONTEXT_DIR CHECK_CMD [OC_ARGS] +# -------------------- +# Runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. Then check the container by arbitrary +# function given as argument (such an argument may include string, +# that will be replaced with actual IP). +# Arguments: image - prefix or whole ID of the pod to run the cmd in (compulsory) +# Arguments: app - url or local path to git repo with the application sources (compulsory) +# Arguments: context_dir - sub-directory inside the repository with the application sources (compulsory) +# Arguments: check_command - CMD line that checks whether the container works (compulsory; '' will be replaced with actual IP) +# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` +# command, typically environment variables (optional) +function ct_os_test_s2i_app_func() { + local image_name=${1} + local app=${2} + local context_dir=${3} + local check_command=${4} + local oc_args=${5:-} + local image_name_no_namespace=${image_name##*/} + local service_name="${image_name_no_namespace%%:*}-testing" + local namespace + + if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then + echo "ERROR: ct_os_test_s2i_app_func() requires at least 4 arguments that cannot be emtpy." >&2 + return 1 + fi + + # shellcheck disable=SC2119 + 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_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_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_tagged}" --insecure=true --reference-policy=local + 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}" + fi + fi + + local app_param="${app}" + if [ -d "${app}" ] ; then + # for local directory, we need to copy the content, otherwise too smart os command + # pulls the git remote repository instead + app_param=$(ct_obtain_input "${app}") + fi + + # shellcheck disable=SC2086 + ct_os_deploy_s2i_image "${image_tagged}" "${app_param}" \ + --context-dir="${context_dir}" \ + --name "${service_name}" \ + ${oc_args} + + if [ -d "${app}" ] ; then + # in order to avoid weird race seen sometimes, let's wait shortly + # before starting the build explicitly + sleep 5 + oc start-build "${service_name}" --from-dir="${app_param}" + fi + + ct_os_wait_pod_ready "${service_name}" 300 + + local ip + local check_command_exp + local image_id + + # get image ID from the deployment config + image_id=$(oc get "deploymentconfig.apps.openshift.io/${service_name}" -o custom-columns=IMAGE:.spec.template.spec.containers[*].image | tail -n 1) + + ip=$(ct_os_get_service_ip "${service_name}") + # shellcheck disable=SC2001 + check_command_exp=$(echo "$check_command" | sed -e "s//$ip/g" -e "s||${image_id}|g") + + echo " Checking APP using $check_command_exp ..." + local result=0 + eval "$check_command_exp" || result=1 + + ct_os_service_image_info "${service_name}" + + if [ $result -eq 0 ] ; then + echo " Check passed." + else + echo " Check failed." + fi + + # shellcheck disable=SC2119 + ct_os_delete_project + return $result +} + +# ct_os_test_s2i_app IMAGE APP CONTEXT_DIR EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ] +# -------------------- +# Runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. Then check the http response. +# Arguments: image - prefix or whole ID of the pod to run the cmd in (compulsory) +# Arguments: app - url or local path to git repo with the application sources (compulsory) +# Arguments: context_dir - sub-directory inside the repository with the application sources (compulsory) +# Arguments: expected_output - PCRE regular expression that must match the response body (compulsory) +# Arguments: port - which port to use (optional; default: 8080) +# Arguments: protocol - which protocol to use (optional; default: http) +# Arguments: response_code - what http response code to expect (optional; default: 200) +# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` +# command, typically environment variables (optional) +function ct_os_test_s2i_app() { + local image_name=${1} + local app=${2} + local context_dir=${3} + local expected_output=${4} + local port=${5:-8080} + local protocol=${6:-http} + local response_code=${7:-200} + local oc_args=${8:-} + + if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then + echo "ERROR: ct_os_test_s2i_app() requires at least 4 arguments that cannot be emtpy." >&2 + return 1 + fi + + ct_os_test_s2i_app_func "${image_name}" \ + "${app}" \ + "${context_dir}" \ + "ct_os_test_response_internal '${protocol}://:${port}' '${response_code}' '${expected_output}'" \ + "${oc_args}" +} + +# ct_os_test_template_app_func IMAGE APP IMAGE_IN_TEMPLATE CHECK_CMD [OC_ARGS] +# -------------------- +# Runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. Then check the container by arbitrary +# function given as argument (such an argument may include string, +# that will be replaced with actual IP). +# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory) +# Arguments: template - url or local path to a template to use (compulsory) +# Arguments: name_in_template - image name used in the template +# Arguments: check_command - CMD line that checks whether the container works (compulsory; '' will be replaced with actual IP) +# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` +# command, typically environment variables (optional) +# Arguments: other_images - some templates need other image to be pushed into the OpenShift registry, +# specify them in this parameter as "|", where "" is a full image name +# (including registry if needed) and "" is a tag under which the image should be available +# in the OpenShift registry. +function ct_os_test_template_app_func() { + local image_name=${1} + local template=${2} + local name_in_template=${3} + local check_command=${4} + local oc_args=${5:-} + local other_images=${6:-} + + if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then + echo "ERROR: ct_os_test_template_app_func() requires at least 4 arguments that cannot be emtpy." >&2 + return 1 + fi + + local service_name="${name_in_template}-testing" + local image_tagged="${name_in_template}:${VERSION}" + local namespace + + # shellcheck disable=SC2119 + ct_os_new_project + + namespace=${CT_NAMESPACE:-"$(oc project -q)"} + # 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}" + else + if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then + echo "Importing image ${image_name} as ${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_tagged}" --insecure=true --reference-policy=local + 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}" + 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 + for i_t in ${other_images} ; do + echo "${i_t}" + IFS='|' read -ra image_tag_a <<< "${i_t}" + docker pull "${image_tag_a[0]}" + 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 + + # get the template file from remote or local location; if not found, it is + # considered an internal template name, like 'mysql', so use the name + # explicitly + 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}" \ + -p NAMESPACE="${namespace}" \ + ${oc_args} + + ct_os_wait_pod_ready "${service_name}" 300 + + local ip + local check_command_exp + local image_id + + # get image ID from the deployment config + image_id=$(oc get "deploymentconfig.apps.openshift.io/${service_name}" -o custom-columns=IMAGE:.spec.template.spec.containers[*].image | tail -n 1) + + ip=$(ct_os_get_service_ip "${service_name}") + # shellcheck disable=SC2001 + check_command_exp=$(echo "$check_command" | sed -e "s//$ip/g" -e "s||${image_id}|g") + + echo " Checking APP using $check_command_exp ..." + local result=0 + eval "$check_command_exp" || result=1 + + ct_os_service_image_info "${service_name}" + + if [ $result -eq 0 ] ; then + echo " Check passed." + else + echo " Check failed." + fi + + # shellcheck disable=SC2119 + ct_os_delete_project + return $result +} + +# params: +# ct_os_test_template_app IMAGE APP IMAGE_IN_TEMPLATE EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ] +# -------------------- +# Runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. Then check the http response. +# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory) +# Arguments: template - url or local path to a template to use (compulsory) +# Arguments: name_in_template - image name used in the template +# Arguments: expected_output - PCRE regular expression that must match the response body (compulsory) +# Arguments: port - which port to use (optional; default: 8080) +# Arguments: protocol - which protocol to use (optional; default: http) +# Arguments: response_code - what http response code to expect (optional; default: 200) +# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` +# command, typically environment variables (optional) +# Arguments: other_images - some templates need other image to be pushed into the OpenShift registry, +# specify them in this parameter as "|", where "" is a full image name +# (including registry if needed) and "" is a tag under which the image should be available +# in the OpenShift registry. +function ct_os_test_template_app() { + local image_name=${1} + local template=${2} + local name_in_template=${3} + local expected_output=${4} + local port=${5:-8080} + local protocol=${6:-http} + local response_code=${7:-200} + local oc_args=${8:-} + local other_images=${9:-} + + if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then + echo "ERROR: ct_os_test_template_app() requires at least 4 arguments that cannot be emtpy." >&2 + return 1 + fi + + ct_os_test_template_app_func "${image_name}" \ + "${template}" \ + "${name_in_template}" \ + "ct_os_test_response_internal '${protocol}://:${port}' '${response_code}' '${expected_output}'" \ + "${oc_args}" \ + "${other_images}" +} + +# ct_os_test_image_update IMAGE_NAME OLD_IMAGE ISTAG CHECK_FUNCTION OC_ARGS +# -------------------- +# Runs an image update test with [image] uploaded to [is] imagestream +# and checks the services using an arbitrary function provided in [check_function]. +# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory) +# Arguments: old_image - valid name of the image from the registry +# Arguments: istag - imagestream to upload the images into (compulsory) +# Arguments: check_function - command to be run to check functionality of created services (compulsory) +# Arguments: oc_args - arguments to use during oc new-app (compulsory) +ct_os_test_image_update() { + local image_name=$1; shift + local old_image=$1; shift + local istag=$1; shift + local check_function=$1; shift + local service_name=${image_name##*/} + local ip="" check_command_exp="" + + echo "Running image update test for: $image_name" + # shellcheck disable=SC2119 + ct_os_new_project + + # Get current image from repository and create an imagestream + docker pull "$old_image:latest" 2>/dev/null + ct_os_upload_image "$old_image" "$istag" + + # Setup example application with curent image + oc new-app "$@" --name "$service_name" + ct_os_wait_pod_ready "$service_name" 60 + + # Check application output + ip=$(ct_os_get_service_ip "$service_name") + check_command_exp=${check_function///$ip} + 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_wait_pod_ready "${service_name}-2" 60 + + # Check application output + ip=$(ct_os_get_service_ip "$service_name") + check_command_exp=${check_function///$ip} + ct_assert_cmd_success "$check_command_exp" + + # shellcheck disable=SC2119 + ct_os_delete_project +} + +# ct_os_deploy_cmd_image IMAGE_NAME +# -------------------- +# Runs a special command pod, a pod that does nothing, but includes utilities for testing. +# A typical usage is a mysql pod that includes mysql commandline, that we need for testing. +# Running commands inside this command pod is done via ct_os_cmd_image_run function. +# The pod is not run again if already running. +# Arguments: image_name - image to be used as a command pod +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 ${image_name}" + oc create -f - <" + local sleep_time=3 + local attempt=1 + local result=1 + local status + local response_code + local response_file + local util_image_name='ubi7/ubi' + + response_file=$(mktemp /tmp/ct_test_response_XXXXXX) + ct_os_deploy_cmd_image "${util_image_name}" + + while [ "${attempt}" -le "${max_attempts}" ]; do + ct_os_cmd_image_run "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_os_get_image_from_pod +# ------------------------ +# Print image identifier from an existing pod to stdout +# Argument: pod_prefix - prefix or full name of the pod to get image from +ct_os_get_image_from_pod() { + local pod_prefix=$1 ; shift + local pod_name + pod_name=$(ct_os_get_pod_name "$pod_prefix") + oc get "po/${pod_name}" -o yaml | sed -ne 's/^\s*image:\s*\(.*\)\s*$/\1/ p' | head -1 +} + +# ct_os_check_cmd_internal +# ---------------- +# Runs a specified command, checks exit code and compares the output with expected regexp. +# That all is done inside an image in the cluster, so the function is used +# typically in clusters that are not accessible outside. +# The check is repeated until timeout. +# Argument: util_image_name - name of the image in the cluster that is used for running the cmd +# Argument: service_name - kubernetes' service name to work with (IP address is taken from this one) +# Argument: check_command - command that is run within the util_image_name container +# Argument: expected_content_match - regexp that must be in the output (use .* to ignore check) +# Argument: timeout - number of seconds to wait till the check succeeds +function ct_os_check_cmd_internal() { + local util_image_name=$1 ; shift + local service_name=$1 ; shift + local check_command=$1 ; shift + local expected_content_match=${1:-.*} ; shift + local timeout=${1:-60} ; shift || : + + : " Service ${service_name} check ..." + + local output + local ret + local ip + local check_command_exp + + ip=$(ct_os_get_service_ip "${service_name}") + # shellcheck disable=SC2001 + check_command_exp=$(echo "$check_command" | sed -e "s//$ip/g") + + ct_os_deploy_cmd_image "${util_image_name}" + SECONDS=0 + + echo -n "Waiting for ${service_name} service becoming ready ..." + while true ; do + output=$(ct_os_cmd_image_run "$check_command_exp") + ret=$? + echo "${output}" | grep -qe "${expected_content_match}" || ret=1 + if [ ${ret} -eq 0 ] ; then + echo " PASS" + return 0 + fi + echo -n "." + [ ${SECONDS} -gt "${timeout}" ] && break + sleep 3 + done + echo " FAIL" + return 1 +} + +# ct_os_test_image_stream_template IMAGE_STREAM_FILE TEMPLATE_FILE SERVICE NAME [TEMPLATE_PARAMS] +# ------------------------ +# Creates an image stream and deploys a specified template. Then checks that a pod runs. +# Argument: image_stream_file - local or remote file with the image stream definition +# Argument: template_file - local file name with a template +# Argument: service_name - how the pod will be named (prefix) +# Argument: template_params (optional) - parameters for the template, like image stream version +function ct_os_test_image_stream_template() { + local image_stream_file=${1} + local template_file=${2} + local service_name=${3} + local template_params=${4:-} + local local_image_stream_file + local local_template_file + + if [ $# -lt 3 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ]; then + echo "ERROR: ct_os_test_image_stream() requires at least 3 arguments that cannot be empty." >&2 + return 1 + fi + + echo "Running image stream test for stream ${image_stream_file} and template ${template_file}" + # shellcheck disable=SC2119 + ct_os_new_project + + local_image_stream_file=$(ct_obtain_input "${image_stream_file}") + local_template_file=$(ct_obtain_input "${template_file}") + oc create -f "${local_image_stream_file}" + + # shellcheck disable=SC2086 + if ! ct_os_deploy_template_image "${local_template_file}" -p NAMESPACE="${CT_NAMESPACE:-$(oc project -q)}" ${template_params} ; then + echo "ERROR: ${template_file} could not be loaded" + return 1 + # Deliberately not runnig ct_os_delete_project here because user either + # might want to investigate or the cleanup is done with the cleanup trap. + # Most functions depend on the set -e anyway at this point. + fi + ct_os_wait_pod_ready "${service_name}" 120 + + # shellcheck disable=SC2119 + ct_os_delete_project +} + +# ct_os_wait_stream_ready IMAGE_STREAM_FILE NAMESPACE [ TIMEOUT ] +# ------------------------ +# Waits max timeout seconds till a [stream] is available in the [namespace]. +# Arguments: image_stream - stream name (usuallly :) +# Arguments: namespace - namespace name +# Arguments: timeout - how many seconds to wait +function ct_os_wait_stream_ready() { + local image_stream=${1} + local namespace=${2} + local timeout=${3:-60} + # It takes some time for the first time before the image is pulled in + SECONDS=0 + echo -n "Waiting for ${namespace}/${image_stream} to become available ..." + while ! oc get -n "${namespace}" istag "${image_stream}" &>/dev/null; do + if [ "$SECONDS" -gt "${timeout}" ] ; then + echo "FAIL: ${namespace}/${image_stream} not available after ${timeout}s:" + echo "oc get -n ${namespace} istag ${image_stream}" + oc get -n "${namespace}" istag "${image_stream}" + return 1 + fi + sleep 3 + echo -n . + done + echo " DONE" +} + +# ct_os_test_image_stream_s2i IMAGE_STREAM_FILE IMAGE_NAME APP CONTEXT_DIR EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ] +# -------------------- +# Check the imagestream with an s2i app check. First it imports the given image stream, then +# it runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. Then check the http response. +# Argument: image_stream_file - local or remote file with the image stream definition +# Argument: image_name - container image we test (or name of the existing image stream in : format) +# Argument: app - url or local path to git repo with the application sources (compulsory) +# Argument: context_dir - sub-directory inside the repository with the application sources (compulsory) +# Argument: expected_output - PCRE regular expression that must match the response body (compulsory) +# Argument: port - which port to use (optional; default: 8080) +# Argument: protocol - which protocol to use (optional; default: http) +# Argument: response_code - what http response code to expect (optional; default: 200) +# Argument: oc_args - all other arguments are used as additional parameters for the `oc new-app` +# command, typically environment variables (optional) +function ct_os_test_image_stream_s2i() { + local image_stream_file=${1} + local image_name=${2} + local app=${3} + local context_dir=${4} + local expected_output=${5} + local port=${6:-8080} + local protocol=${7:-http} + local response_code=${8:-200} + local oc_args=${9:-} + local result + local local_image_stream_file + + echo "Running image stream test for stream ${image_stream_file} and application ${app} with context ${context_dir}" + + # shellcheck disable=SC2119 + ct_os_new_project + + local_image_stream_file=$(ct_obtain_input "${image_stream_file}") + oc create -f "${local_image_stream_file}" + + # ct_os_test_s2i_app creates a new project, but we already need + # it before for the image stream import, so tell it to skip this time + CT_SKIP_NEW_PROJECT=true \ + ct_os_test_s2i_app "${IMAGE_NAME}" "${app}" "${context_dir}" "${expected_output}" \ + "${port}" "${protocol}" "${response_code}" "${oc_args}" + result=$? + + # shellcheck disable=SC2119 + ct_os_delete_project + + return $result +} + +# ct_os_test_image_stream_quickstart IMAGE_STREAM_FILE TEMPLATE IMAGE_NAME NAME_IN_TEMPLATE EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, OTHER_IMAGES ] +# -------------------- +# Check the imagestream with an s2i app check. First it imports the given image stream, then +# it runs [image] and [app] in the openshift and optionally specifies env_params +# as environment variables to the image. Then check the http response. +# Argument: image_stream_file - local or remote file with the image stream definition +# Argument: template_file - local file name with a template +# Argument: image_name - container image we test (or name of the existing image stream in : format) +# Argument: name_in_template - image name used in the template +# Argument: expected_output - PCRE regular expression that must match the response body (compulsory) +# Argument: port - which port to use (optional; default: 8080) +# Argument: protocol - which protocol to use (optional; default: http) +# Argument: response_code - what http response code to expect (optional; default: 200) +# Argument: oc_args - all other arguments are used as additional parameters for the `oc new-app` +# command, typically environment variables (optional) +# Argument: other_images - some templates need other image to be pushed into the OpenShift registry, +# specify them in this parameter as "|", where "" is a full image name +# (including registry if needed) and "" is a tag under which the image should be available +# in the OpenShift registry. +function ct_os_test_image_stream_quickstart() { + local image_stream_file=${1} + local template_file=${2} + local image_name=${3} + local name_in_template=${4} + local expected_output=${5} + local port=${6:-8080} + local protocol=${7:-http} + local response_code=${8:-200} + local oc_args=${9:-} + local other_images=${10:-} + local result + local local_image_stream_file + local local_template_file + + echo "Running image stream test for stream ${image_stream_file} and quickstart template ${template_file}" + echo "Image name is ${IMAGE_NAME}" + # shellcheck disable=SC2119 + ct_os_new_project + + local_image_stream_file=$(ct_obtain_input "${image_stream_file}") + local_template_file=$(ct_obtain_input "${template_file}") + # ct_os_test_template_app creates a new project, but we already need + # it before for the image stream import, so tell it to skip this time + namespace=${CT_NAMESPACE:-"$(oc project -q)"} + + # Add namespace into openshift arguments + if [[ $oc_args != *"NAMESPACE"* ]]; then + oc_args="${oc_args} -p NAMESPACE=${namespace}" + fi + oc create -f "${local_image_stream_file}" + + # In case we are testing on OpenShift 4 export variable for mirror image + # which means, that image is going to be mirrored from an internal registry into OpenShift 4 + if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ]; then + export CT_TAG_IMAGE=true + fi + # ct_os_test_template_app creates a new project, but we already need + # it before for the image stream import, so tell it to skip this time + + CT_SKIP_NEW_PROJECT=true \ + ct_os_test_template_app "${image_name}" \ + "${local_template_file}" \ + "${name_in_template}" \ + "${expected_output}" \ + "${port}" "${protocol}" "${response_code}" "${oc_args}" "${other_images}" + + result=$? + + # shellcheck disable=SC2119 + ct_os_delete_project + + return $result +} + +# ct_os_service_image_info SERVICE_NAME +# -------------------- +# Shows information about the image used by a specified service. +# Argument: service_name - Service name (uesd for deployment config) +function ct_os_service_image_info() { + local service_name=$1 + local image_id + local namespace + + # get image ID from the deployment config + image_id=$(oc get "deploymentconfig.apps.openshift.io/${service_name}" -o custom-columns=IMAGE:.spec.template.spec.containers[*].image | tail -n 1) + namespace=${CT_NAMESPACE:-"$(oc project -q)"} + + echo " Information about the image we work with:" + oc get deploymentconfig.apps.openshift.io/"${service_name}" -o yaml | grep lastTriggeredImage + # for s2i builds, the resulting image is actually in the current namespace, + # so if the specified namespace does not succeed, try the current namespace + oc get isimage -n "${namespace}" "${image_id##*/}" -o yaml || oc get isimage "${image_id##*/}" -o yaml +} +# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/test/test-lib-redis.sh b/test/test-lib-redis.sh new file mode 100644 index 0000000..698abe8 --- /dev/null +++ b/test/test-lib-redis.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Functions for tests for the Redis 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 +source ${THISDIR}/test-lib-remote-openshift.sh + +function test_redis_integration() { + local image_name=$1 + local service_name=redis + ct_os_test_template_app_func "${image_name}" \ + "https://raw.githubusercontent.com/openshift/origin/master/examples/db-templates/redis-ephemeral-template.json" \ + "${service_name}" \ + "ct_os_check_cmd_internal '' '${service_name}-testing' 'timeout 15 redis-cli -h -a testp ping' 'PONG'" \ + "-p REDIS_VERSION=${VERSION} \ + -p DATABASE_SERVICE_NAME="${service_name}-testing" \ + -p REDIS_PASSWORD=testp" +} + +# Check the imagestream +function test_redis_imagestream() { + case ${OS} in + rhel7|centos7) ;; + *) echo "Imagestream testing not supported for $OS environment." ; return 0 ;; + esac + + ct_os_test_image_stream_template "${THISDIR}/../imagestreams/redis-${OS%[0-9]*}.json" "${THISDIR}/../examples/redis-ephemeral-template.json" redis "-p REDIS_VERSION=${VERSION}" +} + +# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/test/test-lib-remote-openshift.sh b/test/test-lib-remote-openshift.sh new file mode 100644 index 0000000..fd9a684 --- /dev/null +++ b/test/test-lib-remote-openshift.sh @@ -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-/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 new file mode 100644 index 0000000..b219ffd --- /dev/null +++ b/test/test-lib.sh @@ -0,0 +1,918 @@ +# 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=_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 <"$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: +# /-cert-selfsigned.pem -- public PEM cert +# /-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 <"$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 or local 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 [ -d "$app_url" ] ; then + echo "Copying local folder: $app_url -> $app_dir." + cp -Lr $app_url $app_dir + else + # 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 + 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: From 91419800c9c6f600b6f78f2834d2823cf5fe911c Mon Sep 17 00:00:00 2001 From: phracek Date: Thu, 25 Nov 2021 12:00:57 +0000 Subject: [PATCH 15/19] Merge pull request #93 from phracek/enable_redis_6_for_rhel7 Enable testing redis 6 for RHEL7 and RHEL8 --- Dockerfile | 5 +- root/usr/bin/run-redis | 4 +- root/usr/libexec/container-setup | 12 +- .../share/container-scripts/redis/common.sh | 2 +- test/examples/redis-ephemeral-template.json | 8 +- test/examples/redis-persistent-template.json | 8 +- test/imagestreams/redis-centos.json | 2 +- test/imagestreams/redis-rhel-aarch64.json | 2 +- test/imagestreams/redis-rhel.json | 20 +- test/redis-ephemeral-template.json | 8 +- test/run-openshift-remote-cluster | 4 +- test/test-lib-openshift.sh | 75 +++--- test/test-lib-remote-openshift.sh | 4 + test/test-lib.sh | 214 +++++++++++++++--- 14 files changed, 283 insertions(+), 85 deletions(-) diff --git a/Dockerfile b/Dockerfile index 659d2b5..cb3499b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ FROM registry.fedoraproject.org/f34/s2i-core:latest # * $REDIS_PASSWORD - Database password ENV NAME=redis \ - RELEASE=\"2" \ + RELEASE=1 \ VERSION=6 ENV REDIS_VERSION=$VERSION \ @@ -58,7 +58,8 @@ RUN getent group redis &> /dev/null || groupadd -r redis &> /dev/null && \ # Get prefix path and path to scripts rather than hard-code them in scripts ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/redis \ - REDIS_PREFIX=/usr + REDIS_PREFIX=/usr \ + REDIS_CONF=/etc/redis/redis.conf COPY root / diff --git a/root/usr/bin/run-redis b/root/usr/bin/run-redis index d739867..835a7fb 100755 --- a/root/usr/bin/run-redis +++ b/root/usr/bin/run-redis @@ -9,7 +9,7 @@ set -eu # Process the Redis configuration files log_info 'Processing Redis configuration files ...' if [[ -v REDIS_PASSWORD ]]; then - envsubst < ${CONTAINER_SCRIPTS_PATH}/password.conf.template >> /etc/redis/redis.conf + envsubst < ${CONTAINER_SCRIPTS_PATH}/password.conf.template >> "${REDIS_CONF}" else log_info 'WARNING: setting REDIS_PASSWORD is recommended' fi @@ -24,4 +24,4 @@ fi unset_env_vars log_volume_info "${REDIS_DATADIR}" log_info 'Running final exec -- Only Redis logs after this point' -exec ${REDIS_PREFIX}/bin/redis-server /etc/redis/redis.conf --daemonize no "$@" 2>&1 +exec ${REDIS_PREFIX}/bin/redis-server "${REDIS_CONF}" --daemonize no "$@" 2>&1 diff --git a/root/usr/libexec/container-setup b/root/usr/libexec/container-setup index d918e9c..4b452f5 100755 --- a/root/usr/libexec/container-setup +++ b/root/usr/libexec/container-setup @@ -5,13 +5,13 @@ set -eu # setup config file if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then - mv /etc/opt/rh/rh-redis6/redis.conf /etc/redis/redis.conf - ln -s /etc/redis/redis.conf /etc/opt/rh/rh-redis6/redis.conf + mv /etc/opt/rh/rh-redis6/redis.conf "${REDIS_CONF}" + ln -s "${REDIS_CONF}" /etc/opt/rh/rh-redis6/redis.conf fi # setup directory for data -chown -R redis:0 "${HOME}" /etc/redis/redis.conf -restorecon -R "${HOME}" /etc/redis/redis.conf +chown -R redis:0 "${HOME}" "${REDIS_CONF}" +restorecon -R "${HOME}" "${REDIS_CONF}" # create a symlink for SCL datadir, so there is some reasonable content there if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then @@ -24,8 +24,8 @@ fi # When only specifying user, group is 0, that's why /var/lib/redis must have # owner redis.0; that allows to avoid a+rwx for this dir chmod 0770 "${HOME}" "${REDIS_DATADIR}" -chmod 0660 /etc/redis/redis.conf +chmod 0660 "${REDIS_CONF}" # adjust config with changes we do every-time clear_config -envsubst < ${CONTAINER_SCRIPTS_PATH}/base.conf.template >> /etc/redis/redis.conf +envsubst < ${CONTAINER_SCRIPTS_PATH}/base.conf.template >> "${REDIS_CONF}" diff --git a/root/usr/share/container-scripts/redis/common.sh b/root/usr/share/container-scripts/redis/common.sh index da365c3..d131515 100644 --- a/root/usr/share/container-scripts/redis/common.sh +++ b/root/usr/share/container-scripts/redis/common.sh @@ -22,5 +22,5 @@ function clear_config() { -e "s/^logfile/#logfile/" \ -e "s/^dir /#dir /" \ -e "/^protected-mode/s/yes/no/" \ - -i /etc/redis/redis.conf + -i "${REDIS_CONF}" } diff --git a/test/examples/redis-ephemeral-template.json b/test/examples/redis-ephemeral-template.json index 387ce62..f00f8e9 100644 --- a/test/examples/redis-ephemeral-template.json +++ b/test/examples/redis-ephemeral-template.json @@ -1,6 +1,6 @@ { "kind": "Template", - "apiVersion": "v1", + "apiVersion": "template.openshift.io/v1", "metadata": { "name": "redis-ephemeral", "annotations": { @@ -63,7 +63,7 @@ }, { "kind": "DeploymentConfig", - "apiVersion": "v1", + "apiVersion": "apps.openshift.io/v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", "annotations": { @@ -208,8 +208,8 @@ { "name": "REDIS_VERSION", "displayName": "Version of Redis Image", - "description": "Version of Redis image to be used (5-el7, 5-el8, or latest).", - "value": "5-el8", + "description": "Version of Redis image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", + "value": "6-el8", "required": true } ] diff --git a/test/examples/redis-persistent-template.json b/test/examples/redis-persistent-template.json index 253e0f8..f4815cf 100644 --- a/test/examples/redis-persistent-template.json +++ b/test/examples/redis-persistent-template.json @@ -1,6 +1,6 @@ { "kind": "Template", - "apiVersion": "v1", + "apiVersion": "template.openshift.io/v1", "metadata": { "name": "redis-persistent", "annotations": { @@ -80,7 +80,7 @@ }, { "kind": "DeploymentConfig", - "apiVersion": "v1", + "apiVersion": "apps.openshift.io/v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", "annotations": { @@ -232,8 +232,8 @@ { "name": "REDIS_VERSION", "displayName": "Version of Redis Image", - "description": "Version of Redis image to be used (5-el7, 5-el8, or latest).", - "value": "5-el8", + "description": "Version of Redis image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", + "value": "6-el8", "required": true } ] diff --git a/test/imagestreams/redis-centos.json b/test/imagestreams/redis-centos.json index d22b7a1..27875a0 100644 --- a/test/imagestreams/redis-centos.json +++ b/test/imagestreams/redis-centos.json @@ -1,5 +1,5 @@ { - "apiVersion": "v1", + "apiVersion": "image.openshift.io/v1", "kind": "ImageStream", "metadata": { "annotations": { diff --git a/test/imagestreams/redis-rhel-aarch64.json b/test/imagestreams/redis-rhel-aarch64.json index ad6f208..309351b 100644 --- a/test/imagestreams/redis-rhel-aarch64.json +++ b/test/imagestreams/redis-rhel-aarch64.json @@ -1,5 +1,5 @@ { - "apiVersion": "v1", + "apiVersion": "image.openshift.io/v1", "kind": "ImageStream", "metadata": { "annotations": { diff --git a/test/imagestreams/redis-rhel.json b/test/imagestreams/redis-rhel.json index c74e444..c9906b2 100644 --- a/test/imagestreams/redis-rhel.json +++ b/test/imagestreams/redis-rhel.json @@ -1,5 +1,5 @@ { - "apiVersion": "v1", + "apiVersion": "image.openshift.io/v1", "kind": "ImageStream", "metadata": { "annotations": { @@ -44,6 +44,24 @@ }, "name": "6-el8" }, + { + "annotations": { + "description": "Provides a Redis 6 database on RHEL 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.", + "iconClass": "icon-redis", + "openshift.io/display-name": "Redis 6 (RHEL 7)", + "openshift.io/provider-display-name": "Red Hat, Inc.", + "tags": "redis", + "version": "6" + }, + "from": { + "kind": "DockerImage", + "name": "registry.redhat.io/rhscl/redis-6-rhel7:latest" + }, + "referencePolicy": { + "type": "Local" + }, + "name": "6-el7" + }, { "annotations": { "description": "Provides a Redis 5 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", diff --git a/test/redis-ephemeral-template.json b/test/redis-ephemeral-template.json index 387ce62..f00f8e9 100644 --- a/test/redis-ephemeral-template.json +++ b/test/redis-ephemeral-template.json @@ -1,6 +1,6 @@ { "kind": "Template", - "apiVersion": "v1", + "apiVersion": "template.openshift.io/v1", "metadata": { "name": "redis-ephemeral", "annotations": { @@ -63,7 +63,7 @@ }, { "kind": "DeploymentConfig", - "apiVersion": "v1", + "apiVersion": "apps.openshift.io/v1", "metadata": { "name": "${DATABASE_SERVICE_NAME}", "annotations": { @@ -208,8 +208,8 @@ { "name": "REDIS_VERSION", "displayName": "Version of Redis Image", - "description": "Version of Redis image to be used (5-el7, 5-el8, or latest).", - "value": "5-el8", + "description": "Version of Redis image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", + "value": "6-el8", "required": true } ] diff --git a/test/run-openshift-remote-cluster b/test/run-openshift-remote-cluster index 2f717db..2199f48 100755 --- a/test/run-openshift-remote-cluster +++ b/test/run-openshift-remote-cluster @@ -16,6 +16,8 @@ set -eo nounset trap ct_os_cleanup EXIT SIGINT +ct_os_set_ocp4 + ct_os_check_compulsory_vars oc status || false "It looks like oc is not properly logged in." @@ -23,8 +25,6 @@ oc status || false "It looks like oc is not properly logged in." # For testing on OpenShift 4 we use external registry export CT_EXTERNAL_REGISTRY=true -ct_os_set_ocp4 - # Check the template test_redis_integration "${IMAGE_NAME}" diff --git a/test/test-lib-openshift.sh b/test/test-lib-openshift.sh index fa15eae..624a391 100644 --- a/test/test-lib-openshift.sh +++ b/test/test-lib-openshift.sh @@ -342,6 +342,10 @@ function _ct_os_get_uniq_project_name() { # to authenticate to image registries. # shellcheck disable=SC2120 function ct_os_new_project() { + if [ "${CVP:-0}" -eq "1" ]; then + echo "Testing in CVP environment. No need to create OpenShift project. This is done by CVP pipeline" + return + fi if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then echo "Creating project skipped." return @@ -364,7 +368,7 @@ function ct_os_new_project() { # Arguments: project - project name, uses the current project if omitted # shellcheck disable=SC2120 function ct_os_delete_project() { - if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then + if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] || [ "${CVP:-0}" -eq "1" ]; then echo "Deleting project skipped, cleaning objects only." # when not having enough privileges (remote cluster), it might fail and # it is not a big problem, so ignore failure in this case @@ -665,20 +669,24 @@ function ct_os_test_s2i_app_func() { 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_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_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_tagged}" --insecure=true --reference-policy=local - ct_os_wait_stream_ready "${image_tagged}" "${namespace}" + if [ "${CVP:-0}" -eq "0" ]; then + if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then + ct_os_import_image_ocp4 "${image_name}" "${image_tagged}" else - echo "Uploading image ${image_name} as ${image_tagged}" - ct_os_upload_image "${image_name}" "${image_tagged}" + # 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_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_tagged}" --insecure=true --reference-policy=local + 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}" + fi fi + else + echo "Testing image ${image_name} in CVP pipeline." fi local app_param="${app}" @@ -803,21 +811,27 @@ function ct_os_test_template_app_func() { ct_os_new_project namespace=${CT_NAMESPACE:-"$(oc project -q)"} - # 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}" - else - if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then - echo "Importing image ${image_name} as ${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_tagged}" --insecure=true --reference-policy=local - ct_os_wait_stream_ready "${image_tagged}" "${namespace}" + # 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}" else - echo "Uploading image ${image_name} as ${image_tagged}" - ct_os_upload_image "${image_name}" "${image_tagged}" + if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then + echo "Importing image ${image_name} as ${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_tagged}" --insecure=true --reference-policy=local + 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}" + fi fi + else + echo "Import is already done by CVP pipeline." fi + # Other images are not uploaded by CVP pipeline. We need to do it. 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 @@ -825,7 +839,14 @@ function ct_os_test_template_app_func() { for i_t in ${other_images} ; do echo "${i_t}" IFS='|' read -ra image_tag_a <<< "${i_t}" - docker pull "${image_tag_a[0]}" + if [[ "$(docker images -q "$image_name" 2>/dev/null)" == "" ]]; then + echo "ERROR: Image $image_name is not pulled yet." + docker images + echo "Add to the beginning of scripts run-openshift-remote-cluster and run-openshift row" + echo "'ct_pull_image $image_name true'." + exit 1 + fi + if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ] ; then ct_os_import_image_ocp4 "${image_tag_a[0]}" "${image_tag_a[1]}" else @@ -1043,7 +1064,7 @@ ct_os_test_response_internal() { local status local response_code local response_file - local util_image_name='ubi7/ubi' + local util_image_name='registry.access.redhat.com/ubi7/ubi' response_file=$(mktemp /tmp/ct_test_response_XXXXXX) ct_os_deploy_cmd_image "${util_image_name}" diff --git a/test/test-lib-remote-openshift.sh b/test/test-lib-remote-openshift.sh index fd9a684..bda03f6 100644 --- a/test/test-lib-remote-openshift.sh +++ b/test/test-lib-remote-openshift.sh @@ -47,6 +47,10 @@ function ct_os_set_path_oc_4() { # # function ct_os_set_ocp4() { + if [ "${CVP:-0}" -eq "1" ]; then + echo "Testing in CVP environment. No need to login to OpenShift cluster. This is already done by CVP pipeline." + return + fi local login OS_OC_CLIENT_VERSION=${OS_OC_CLIENT_VERSION:-4.4} ct_os_set_path_oc_4 "${OS_OC_CLIENT_VERSION}" diff --git a/test/test-lib.sh b/test/test-lib.sh index b219ffd..42f22ba 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -2,11 +2,11 @@ # # Test a container image. # -# Always use sourced from a specific container testfile +# Always use sourced from a specific container testfile # # reguires definition of CID_FILE_DIR # CID_FILE_DIR=$(mktemp --suffix=_test_cidfiles -d) -# reguires definition of TEST_LIST +# reguires definition of TEST_LIST # TEST_LIST="\ # ctest_container_creation # ctest_doc_content" @@ -52,6 +52,50 @@ function ct_enable_cleanup() { trap ct_cleanup EXIT SIGINT } +# ct_pull_image +# ------------- +# Function pull an image before tests execution +# Argument: image_name - string containing the public name of the image to pull +# Argument: exit - in case "true" is defined and pull failed, then script has to exit with 1 and no tests are executed +# Argument: loops - how many times to pull image in case of failure +# Function returns either 0 in case of pull was successful +# Or the test suite exit with 1 in case of pull error +function ct_pull_image() { + local image_name="$1"; shift + local exit=${1:-"false"}; shift + local loops=${1:-10}; shift + local loop=0 + + # Let's try to pull image. + echo "-> Pulling image $image_name ..." + # Sometimes in Fedora case it fails with HTTP 50X + # Check if the image is available locally and try to pull it if it is not + if [[ "$(docker images -q "$image_name" 2>/dev/null)" != "" ]]; then + echo "The image $image_name is already pulled." + return 0 + fi + + # Try pulling the image to see if it is accessible + # WORKAROUND: Since Fedora registry sometimes fails randomly, let's try it more times + while ! docker pull "$image_name"; do + ((loop++)) || : + echo "Pulling image $image_name failed." + if [ "$loop" -gt "$loops" ]; then + echo "Pulling of image $image_name failed $loops times in a row. Giving up." + echo "!!! ERROR with pulling image $image_name !!!!" + # shellcheck disable=SC2268 + if [[ x"$exit" == x"false" ]]; then + return 1 + else + exit 1 + fi + fi + echo "Let's wait $((loop*5)) seconds and try again." + sleep "$((loop*5))" + done +} + + # ct_check_envs_set env_filter check_envs loop_envs [env_format] # -------------------- # Compares values from one list of environment variable definitions against such list, @@ -185,7 +229,7 @@ function ct_assert_container_creation_fails() { 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 + # shellcheck disable=SC2086,SC2153 docker run --cidfile="$cid_file" -d ${CONTAINER_ARGS:-} "$IMAGE_NAME" "$@" ct_wait_for_cid "$cid_file" || return 1 : "Created container $(cat "$cid_file")" @@ -310,6 +354,7 @@ function ct_npm_works() { 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 + cat "${tmpdir}/jquery" return 1 fi @@ -582,13 +627,13 @@ ct_get_public_image_name() { local registry registry=$(ct_registry_from_os "$os") - if [ "x$os" == "xrhel7" ]; then + if [ "$os" == "rhel7" ]; then public_image_name=$registry/rhscl/$base_image_name-${version//./}-rhel7 - elif [ "x$os" == "xrhel8" ]; then + elif [ "$os" == "rhel8" ]; then public_image_name=$registry/rhel8/$base_image_name-${version//./} - elif [ "x$os" == "xcentos7" ]; then + elif [ "$os" == "centos7" ]; then public_image_name=$registry/centos7/$base_image_name-${version//./}-centos7 - elif [ "x$os" == "xcentos8" ]; then + elif [ "$os" == "centos8" ]; then public_image_name=$registry/centos8/$base_image_name-${version//./}-centos8 fi @@ -670,7 +715,7 @@ ct_s2i_build_as_df() local df_name= local tmpdir= local incremental=false - local mount_options="" + local mount_options=() # Run the entire thing inside a subshell so that we do not leak shell options outside of the function ( @@ -687,14 +732,11 @@ ct_s2i_build_as_df() # 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." + if ! user_id=$(ct_get_uid_from_image "$user" "$src_image"); then 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) @@ -757,14 +799,93 @@ EOF 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) + read -ra 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" . + docker build ${mount_options[@]+"${mount_options[@]}"} -f "$df_name" --no-cache=true -t "$dst_image" . ) } +# ct_s2i_multistage_build APP_PATH SRC_IMAGE DST_IMAGE SEC_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 process +# Argument: SEC_IMAGE - image to be used as the base for the result of the build process +# 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 environment variable definitions. +ct_s2i_multistage_build() { + + local app_path=$1; shift + local src_image=$1; shift + local sec_image=$1; shift + local dst_image=$1; shift + local s2i_args=$*; + local local_app="app-src" + local user_id= + 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 + + 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 + if ! user_id=$(ct_get_uid_from_image "$user" "$src_image"); then + echo "Terminating s2i build." + return 1 + fi + + # Use /tmp to not pollute cwd + tmpdir=$(mktemp -d) + df_name=$(mktemp -p "$tmpdir" Dockerfile.XXXX) + cd "$tmpdir" + + # If the path exists on the local host, copy it into the directory for the build + # Otherwise handle it as a link to a git repository + if [ -e "${app_path/file:\/\//}/." ] ; then + mkdir -p "$local_app" + # Strip file:// from APP_PATH and copy its contents into current context + cp -r "${app_path/file:\/\//}/." "$local_app" + + else + ct_clone_git_repository "$app_path" "$local_app" + fi + + cat <"$df_name" +# First stage builds the application +FROM $src_image as builder +# Add application sources to a directory that the assemble script expects them +# and set permissions so that the container runs without root access +USER 0 +ADD app-src /tmp/src +RUN chown -R 1001:0 /tmp/src +$(echo "$s2i_args" | grep -o -e '\(-e\|--env\)[[:space:]=]\S*=\S*' | sed -e 's/-e /ENV /' -e 's/--env[ =]/ENV /') +# 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") +USER $user_id +# Install the dependencies +RUN /usr/libexec/s2i/assemble +# Second stage copies the application to the minimal image +FROM $sec_image +# Copy the application source and build artifacts from the builder image to this one +COPY --from=builder \$HOME \$HOME +# Set the default command for the resulting image +CMD /usr/libexec/s2i/run +EOF + + # Check if -v parameter is present in s2i_args and add it into docker build command + read -ra mount_options <<< "$(echo "$s2i_args" | grep -o -e '\(-v\)[[:space:]]\.*\S*' || true)" + + docker build ${mount_options[@]+"${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. @@ -773,7 +894,7 @@ 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 + if ! ct_pull_image "$public_image_name" &>/dev/null; then echo "$public_image_name could not be downloaded via 'docker'" return 1 fi @@ -823,6 +944,50 @@ ct_show_resources() lscpu } +# ct_clone_git_repository +# ----------------------------- +# Argument: app_url - git URI pointing to a repository, supports "@" to indicate a different branch +# Argument: app_dir (optional) - name of the directory to clone the repository into +ct_clone_git_repository() +{ + local app_url=$1; shift + local app_dir=$1 + + # 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}." + return 1 + fi +} + +# ct_get_uid_from_image +# ----------------------------- +# Argument: user - user to get uid for inside the image +# Argument: src_image - image to use for user information +ct_get_uid_from_image() +{ + local user=$1; shift + local src_image=$1 + local user_id= + + # 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." + return 1 + else + echo "${user_id:-$user}" + fi +} + # ct_test_app_dockerfile # ----------------------------- # Argument: dockerfile - path to a Dockerfile that will be used for building an image @@ -867,20 +1032,9 @@ ct_test_app_dockerfile() { if [ -d "$app_url" ] ; then echo "Copying local folder: $app_url -> $app_dir." - cp -Lr $app_url $app_dir + cp -Lr "$app_url" "$app_dir" else - # 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}." + if ! ct_clone_git_repository "$app_url" "$app_dir" ; then echo "Terminating the Dockerfile build." return 1 fi From 98bed69ec34b6ac519336a2026d0acced52bfc85 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Fri, 17 Dec 2021 08:44:28 +0100 Subject: [PATCH 16/19] Link to source for README.md --- README.md | 86 +------------------------------------------------------ 1 file changed, 1 insertion(+), 85 deletions(-) mode change 100644 => 120000 README.md diff --git a/README.md b/README.md deleted file mode 100644 index e9446d4..0000000 --- a/README.md +++ /dev/null @@ -1,85 +0,0 @@ -Redis 6 in-memory data structure store container image -==================== - -This container image includes Redis 6 in-memory data structure store for OpenShift and general usage. -Users can choose between RHEL, CentOS and Fedora based 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/), -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). - -Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments - -Description ------------ - -Redis 6 available as container, is an advanced key-value store. -It is often referred to as a data structure server since keys can contain strings, hashes, lists, -sets and sorted sets. You can run atomic operations on these types, like appending to a string; -incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; -or getting the member with highest ranking in a sorted set. In order to achieve its outstanding -performance, Redis works with an in-memory dataset. Depending on your use case, you can persist -it either by dumping the dataset to disk every once in a while, or by appending each command to a log. - - -Usage ------ - -For this, we will assume that you are using the `rhel8/redis-6` image. -If you want to set only the mandatory environment variables and not store -the database in a host directory, execute the following command: - -``` -$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-6 -``` - -This will create a container named `redis_database`. Port 6379 will be exposed and mapped -to the host. - -If you want your database to be persistent across container executions, also add a -`-v /host/db/path:/var/lib/redis/data:Z` argument. This will be the Redis data directory. - -For protecting Redis data by a password, pass `REDIS_PASSWORD` environment variable -to the container like this: - -``` -$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-6 -``` - -**Warning: since Redis is pretty fast an outside user can try up to -160k passwords per second against a good box. This means that you should -use a very strong password otherwise it will be very easy to break.** - - -Environment variables and volumes ----------------------------------- - -**`REDIS_PASSWORD`** - Password for the server access - - -You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. - -**`/var/lib/redis/data`** - Redis data directory - - -**Notice: When mouting a directory from the host into the container, ensure that the mounted -directory has the appropriate permissions and that the owner and group of the directory -matches the user UID or name which is running inside the container.** - - -Troubleshooting ---------------- -Redis logs into standard output, so the log is available in the container log. The log can be examined by running: - - podman logs - - -See also --------- -Dockerfile and other sources for this container image are available on -https://github.com/sclorg/redis-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. diff --git a/README.md b/README.md new file mode 120000 index 0000000..0b7f519 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +root/usr/share/container-scripts/redis/README.md \ No newline at end of file From 3594051935eddd9a2375c5b9c530268fcaffba0c Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Fri, 17 Dec 2021 08:45:10 +0100 Subject: [PATCH 17/19] Remove pre-generated man file from sources --- root/help.1 | 107 ---------------------------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 root/help.1 diff --git a/root/help.1 b/root/help.1 deleted file mode 100644 index 3174112..0000000 --- a/root/help.1 +++ /dev/null @@ -1,107 +0,0 @@ -.TH Redis 6 in\-memory data structure store container image -.PP -This container image includes Redis 6 in\-memory data structure store for OpenShift and general usage. -Users can choose between RHEL, CentOS and Fedora based images. -The RHEL images are available in the Red Hat Container Catalog -\[la]https://access.redhat.com/containers/\[ra], -the CentOS images are available on Docker Hub -\[la]https://hub.docker.com/r/centos/\[ra], -and the Fedora images are available in Fedora Registry -\[la]https://registry.fedoraproject.org/\[ra]\&. -The resulting image can be run using podman -\[la]https://github.com/containers/libpod\[ra]\&. - -.PP -Note: while the examples in this README are calling \fB\fCpodman\fR, you can replace any such calls by \fB\fCdocker\fR with the same arguments - -.SH Description -.PP -Redis 6 available as container, is an advanced key\-value store. -It is often referred to as a data structure server since keys can contain strings, hashes, lists, -sets and sorted sets. You can run atomic operations on these types, like appending to a string; -incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; -or getting the member with highest ranking in a sorted set. In order to achieve its outstanding -performance, Redis works with an in\-memory dataset. Depending on your use case, you can persist -it either by dumping the dataset to disk every once in a while, or by appending each command to a log. - -.SH Usage -.PP -For this, we will assume that you are using the \fB\fCrhel8/redis\-6\fR image. -If you want to set only the mandatory environment variables and not store -the database in a host directory, execute the following command: - -.PP -.RS - -.nf -$ podman run \-d \-\-name redis\_database \-p 6379:6379 rhel8/redis\-6 - -.fi -.RE - -.PP -This will create a container named \fB\fCredis\_database\fR\&. Port 6379 will be exposed and mapped -to the host. - -.PP -If you want your database to be persistent across container executions, also add a -\fB\fC\-v /host/db/path:/var/lib/redis/data:Z\fR argument. This will be the Redis data directory. - -.PP -For protecting Redis data by a password, pass \fB\fCREDIS\_PASSWORD\fR environment variable -to the container like this: - -.PP -.RS - -.nf -$ podman run \-d \-\-name redis\_database \-e REDIS\_PASSWORD=strongpassword rhel8/redis\-6 - -.fi -.RE - -.PP -\fBWarning: since Redis is pretty fast an outside user can try up to -160k passwords per second against a good box. This means that you should -use a very strong password otherwise it will be very easy to break.\fP - -.SH Environment variables and volumes -.PP -\fB\fB\fCREDIS\_PASSWORD\fR\fP -.br - Password for the server access - -.PP -You can also set the following mount points by passing the \fB\fC\-v /host:/container:Z\fR flag to podman. - -.PP -\fB\fB\fC/var/lib/redis/data\fR\fP -.br - Redis data directory - -.PP -\fBNotice: When mouting a directory from the host into the container, ensure that the mounted -directory has the appropriate permissions and that the owner and group of the directory -matches the user UID or name which is running inside the container.\fP - -.SH Troubleshooting -.PP -Redis logs into standard output, so the log is available in the container log. The log can be examined by running: - -.PP -.RS - -.nf -podman logs - -.fi -.RE - -.SH See also -.PP -Dockerfile and other sources for this container image are available on - -\[la]https://github.com/sclorg/redis-container\[ra]\&. -In that repository you also can find another versions of Python environment Dockerfiles. -Dockerfile for CentOS is called \fB\fCDockerfile\fR, Dockerfile for RHEL7 is called \fB\fCDockerfile.rhel7\fR, -for RHEL8 it's \fB\fCDockerfile.rhel8\fR and the Fedora Dockerfile is called Dockerfile.fedora. From 14c99c0649839a12986575bd93a50468cc7abf3d Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Fri, 17 Dec 2021 08:45:44 +0100 Subject: [PATCH 18/19] Update to F35 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cb3499b..d0941ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fedoraproject.org/f34/s2i-core:latest +FROM registry.fedoraproject.org/f35/s2i-core:latest # Redis image based on Software Collections packages # From ae482a1665e528eeaea9ba459191e8f689c6df63 Mon Sep 17 00:00:00 2001 From: Honza Horak Date: Tue, 8 Jul 2025 17:40:16 +0200 Subject: [PATCH 19/19] container sources not used for building fedora containers anymore --- .gitignore | 0 6 | 1 - Dockerfile | 80 - Dockerfile.fedora | 1 - README.md | 1 - bot-cfg.yml | 20 - dead.package | 1 + help.md | 1 - root/usr/bin/container-entrypoint | 2 - root/usr/bin/run-redis | 27 - root/usr/bin/usage | 4 - root/usr/libexec/container-setup | 31 - .../share/container-scripts/redis/README.md | 85 - .../redis/base.conf.template | 1 - .../share/container-scripts/redis/common.sh | 26 - .../share/container-scripts/redis/helpers.sh | 24 - .../redis/password.conf.template | 3 - .../container-scripts/redis/post-init.sh | 6 - .../share/container-scripts/redis/scl_enable | 3 - .../redis/validate-variables.sh | 15 - sources | 0 test/examples/redis-ephemeral-template.json | 216 --- test/examples/redis-persistent-template.json | 240 --- test/imagestreams/redis-centos.json | 85 - test/imagestreams/redis-rhel-aarch64.json | 67 - test/imagestreams/redis-rhel.json | 121 -- test/redis-ephemeral-template.json | 216 --- test/run | 359 ----- test/run-openshift | 1 - test/run-openshift-local-cluster | 119 -- test/run-openshift-remote-cluster | 37 - test/test-lib-openshift.sh | 1364 ----------------- test/test-lib-redis.sh | 37 - test/test-lib-remote-openshift.sh | 116 -- test/test-lib.sh | 1072 ------------- 35 files changed, 1 insertion(+), 4381 deletions(-) delete mode 100644 .gitignore delete mode 120000 6 delete mode 100644 Dockerfile delete mode 120000 Dockerfile.fedora delete mode 120000 README.md delete mode 100644 bot-cfg.yml create mode 100644 dead.package delete mode 120000 help.md delete mode 100755 root/usr/bin/container-entrypoint delete mode 100755 root/usr/bin/run-redis delete mode 100755 root/usr/bin/usage delete mode 100755 root/usr/libexec/container-setup delete mode 100644 root/usr/share/container-scripts/redis/README.md delete mode 100644 root/usr/share/container-scripts/redis/base.conf.template delete mode 100644 root/usr/share/container-scripts/redis/common.sh delete mode 100644 root/usr/share/container-scripts/redis/helpers.sh delete mode 100644 root/usr/share/container-scripts/redis/password.conf.template delete mode 100644 root/usr/share/container-scripts/redis/post-init.sh delete mode 100644 root/usr/share/container-scripts/redis/scl_enable delete mode 100644 root/usr/share/container-scripts/redis/validate-variables.sh delete mode 100644 sources delete mode 100644 test/examples/redis-ephemeral-template.json delete mode 100644 test/examples/redis-persistent-template.json delete mode 100644 test/imagestreams/redis-centos.json delete mode 100644 test/imagestreams/redis-rhel-aarch64.json delete mode 100644 test/imagestreams/redis-rhel.json delete mode 100644 test/redis-ephemeral-template.json delete mode 100755 test/run delete mode 120000 test/run-openshift delete mode 100755 test/run-openshift-local-cluster delete mode 100755 test/run-openshift-remote-cluster delete mode 100644 test/test-lib-openshift.sh delete mode 100644 test/test-lib-redis.sh delete mode 100644 test/test-lib-remote-openshift.sh delete mode 100644 test/test-lib.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/6 b/6 deleted file mode 120000 index 945c9b4..0000000 --- a/6 +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index d0941ce..0000000 --- a/Dockerfile +++ /dev/null @@ -1,80 +0,0 @@ -FROM registry.fedoraproject.org/f35/s2i-core:latest - -# Redis image based on Software Collections packages -# -# Volumes: -# * /var/lib/redis/data - Datastore for Redis -# Environment: -# * $REDIS_PASSWORD - Database password - -ENV NAME=redis \ - RELEASE=1 \ - VERSION=6 - -ENV REDIS_VERSION=$VERSION \ - HOME=/var/lib/redis - -ENV SUMMARY="Redis in-memory data structure store, used as database, cache and message broker" \ - DESCRIPTION="Redis $REDIS_VERSION available as container, is an advanced key-value store. \ -It is often referred to as a data structure server since keys can contain strings, hashes, lists, \ -sets and sorted sets. You can run atomic operations on these types, like appending to a string; \ -incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; \ -or getting the member with highest ranking in a sorted set. In order to achieve its outstanding \ -performance, Redis works with an in-memory dataset. Depending on your use case, you can persist \ -it either by dumping the dataset to disk every once in a while, or by appending each command to a log." - -LABEL summary="$SUMMARY" \ - description="$DESCRIPTION" \ - io.k8s.description="$SUMMARY" \ - io.k8s.display-name="Redis 6" \ - io.openshift.expose-services="6379:redis" \ - io.openshift.tags="database,redis,redis6" \ - com.redhat.component="$NAME" \ - name="$FGC/$NAME" \ - version="$VERSION" \ - usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ - maintainer="SoftwareCollections.org " - -EXPOSE 6379 - -# Create user for redis that has known UID -# We need to do this before installing the RPMs which would create user with random UID -# The UID is the one used by the default user from the parent layer (1001), -# and since the user exists already, do not create a new one, but only rename -# the existing -# This image must forever use UID 1001 for redis user so our volumes are -# safe in the future. This should *never* change, the last test is there -# to make sure of that. -RUN getent group redis &> /dev/null || groupadd -r redis &> /dev/null && \ - usermod -l redis -aG redis -c 'Redis Server' default &> /dev/null && \ -# Install gettext for envsubst command - dnf install -y yum-utils gettext policycoreutils && \ - INSTALL_PKGS="redis" && \ - dnf install -y --setopt=tsflags=nodocs --nogpgcheck $INSTALL_PKGS && \ - rpm -V $INSTALL_PKGS && \ - dnf clean all && \ - mkdir -p /var/lib/redis/data && chown -R redis.0 /var/lib/redis && \ - [[ "$(id redis)" == "uid=1001(redis)"* ]] - -# Get prefix path and path to scripts rather than hard-code them in scripts -ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/redis \ - REDIS_PREFIX=/usr \ - REDIS_CONF=/etc/redis/redis.conf - -COPY root / - -# this is needed due to issues with squash -# when this directory gets rm'd by the container-setup -# script. -RUN /usr/libexec/container-setup - -VOLUME ["/var/lib/redis/data"] - -# Using a numeric value because of a comment in [1]: -# If your S2I image does not include a USER declaration with a numeric user, -# your builds will fail by default. -# [1] https://docs.openshift.com/container-platform/4.4/openshift_images/create-images.html#images-create-guide-openshift_create-images -USER 1001 - -ENTRYPOINT ["container-entrypoint"] -CMD ["run-redis"] diff --git a/Dockerfile.fedora b/Dockerfile.fedora deleted file mode 120000 index 1d1fe94..0000000 --- a/Dockerfile.fedora +++ /dev/null @@ -1 +0,0 @@ -Dockerfile \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 120000 index 0b7f519..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -root/usr/share/container-scripts/redis/README.md \ No newline at end of file diff --git a/bot-cfg.yml b/bot-cfg.yml deleted file mode 100644 index 3a3b98b..0000000 --- a/bot-cfg.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -version: "1" - -betka: - # is betka enabled for this repository - # optional - defaults to true - enabled: true - notifications: - email_addresses: ["pkubat@redhat.com", "phracek@redhat.com", "hhorak@redhat.com"] - - # Specify if master branch in upstream repository is synced - master_checker: true - # Should pull requests be synced? - pr_checker: false - # Path to directory with dockerfile withing upstream repository - upstream_git_path: "6" - # Github comment message to enforce sync of a pull request - pr_comment_message: "[test]" - # Specify URL to an image used for dist-git source generation. Like - image_url: "quay.io/rhscl/cwt-generator" diff --git a/dead.package b/dead.package new file mode 100644 index 0000000..7290702 --- /dev/null +++ b/dead.package @@ -0,0 +1 @@ +container sources not used for building fedora containers anymore diff --git a/help.md b/help.md deleted file mode 120000 index 42061c0..0000000 --- a/help.md +++ /dev/null @@ -1 +0,0 @@ -README.md \ No newline at end of file diff --git a/root/usr/bin/container-entrypoint b/root/usr/bin/container-entrypoint deleted file mode 100755 index 9d8ad4d..0000000 --- a/root/usr/bin/container-entrypoint +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec "$@" diff --git a/root/usr/bin/run-redis b/root/usr/bin/run-redis deleted file mode 100755 index 835a7fb..0000000 --- a/root/usr/bin/run-redis +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -export_vars=$(cgroup-limits); export $export_vars -source ${CONTAINER_SCRIPTS_PATH}/common.sh -set -eu - -[ -f ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh ] && source ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh - -# Process the Redis configuration files -log_info 'Processing Redis configuration files ...' -if [[ -v REDIS_PASSWORD ]]; then - envsubst < ${CONTAINER_SCRIPTS_PATH}/password.conf.template >> "${REDIS_CONF}" -else - log_info 'WARNING: setting REDIS_PASSWORD is recommended' -fi - -# Source post-init source if exists -if [ -f ${CONTAINER_SCRIPTS_PATH}/post-init.sh ]; then - log_info 'Sourcing post-init.sh ...' - source ${CONTAINER_SCRIPTS_PATH}/post-init.sh -fi - -# Restart the Redis server with public IP bindings -unset_env_vars -log_volume_info "${REDIS_DATADIR}" -log_info 'Running final exec -- Only Redis logs after this point' -exec ${REDIS_PREFIX}/bin/redis-server "${REDIS_CONF}" --daemonize no "$@" 2>&1 diff --git a/root/usr/bin/usage b/root/usr/bin/usage deleted file mode 100755 index d204ed2..0000000 --- a/root/usr/bin/usage +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -cat /usr/share/container-scripts/redis/README.md - diff --git a/root/usr/libexec/container-setup b/root/usr/libexec/container-setup deleted file mode 100755 index 4b452f5..0000000 --- a/root/usr/libexec/container-setup +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -source ${CONTAINER_SCRIPTS_PATH}/common.sh -set -eu - -# setup config file -if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then - mv /etc/opt/rh/rh-redis6/redis.conf "${REDIS_CONF}" - ln -s "${REDIS_CONF}" /etc/opt/rh/rh-redis6/redis.conf -fi - -# setup directory for data -chown -R redis:0 "${HOME}" "${REDIS_CONF}" -restorecon -R "${HOME}" "${REDIS_CONF}" - -# create a symlink for SCL datadir, so there is some reasonable content there -if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then - rmdir /var/opt/rh/rh-redis6/lib/redis/ - ln -s /var/lib/redis /var/opt/rh/rh-redis6/lib/redis -fi - -# Loosen permission bits for group to avoid problems running container with -# arbitrary UID -# When only specifying user, group is 0, that's why /var/lib/redis must have -# owner redis.0; that allows to avoid a+rwx for this dir -chmod 0770 "${HOME}" "${REDIS_DATADIR}" -chmod 0660 "${REDIS_CONF}" - -# adjust config with changes we do every-time -clear_config -envsubst < ${CONTAINER_SCRIPTS_PATH}/base.conf.template >> "${REDIS_CONF}" diff --git a/root/usr/share/container-scripts/redis/README.md b/root/usr/share/container-scripts/redis/README.md deleted file mode 100644 index 97db572..0000000 --- a/root/usr/share/container-scripts/redis/README.md +++ /dev/null @@ -1,85 +0,0 @@ -Redis 6 in-memory data structure store container image -====================================================== - -This container image includes Redis 6 in-memory data structure store for OpenShift and general usage. -Users can choose between RHEL, CentOS and Fedora based images. -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). - -Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments - -Description ------------ - -Redis 6 available as container, is an advanced key-value store. -It is often referred to as a data structure server since keys can contain strings, hashes, lists, -sets and sorted sets. You can run atomic operations on these types, like appending to a string; -incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; -or getting the member with highest ranking in a sorted set. In order to achieve its outstanding -performance, Redis works with an in-memory dataset. Depending on your use case, you can persist -it either by dumping the dataset to disk every once in a while, or by appending each command to a log. - - -Usage ------ - -For this, we will assume that you are using the `rhel8/redis-6` image. -If you want to set only the mandatory environment variables and not store -the database in a host directory, execute the following command: - -``` -$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-6 -``` - -This will create a container named `redis_database`. Port 6379 will be exposed and mapped -to the host. - -If you want your database to be persistent across container executions, also add a -`-v /host/db/path:/var/lib/redis/data:Z` argument. This will be the Redis data directory. - -For protecting Redis data by a password, pass `REDIS_PASSWORD` environment variable -to the container like this: - -``` -$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-6 -``` - -**Warning: since Redis is pretty fast an outside user can try up to -150k passwords per second against a good box. This means that you should -use a very strong password otherwise it will be very easy to break.** - - -Environment variables and volumes ----------------------------------- - -**`REDIS_PASSWORD`** - Password for the server access - - -You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. - -**`/var/lib/redis/data`** - Redis data directory - - -**Notice: When mouting a directory from the host into the container, ensure that the mounted -directory has the appropriate permissions and that the owner and group of the directory -matches the user UID or name which is running inside the container.** - - -Troubleshooting ---------------- -Redis logs into standard output, so the log is available in the container log. The log can be examined by running: - - podman logs - - -See also --------- -Dockerfile and other sources for this container image are available on -https://github.com/sclorg/redis-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. diff --git a/root/usr/share/container-scripts/redis/base.conf.template b/root/usr/share/container-scripts/redis/base.conf.template deleted file mode 100644 index 9d3f01b..0000000 --- a/root/usr/share/container-scripts/redis/base.conf.template +++ /dev/null @@ -1 +0,0 @@ -dir ${REDIS_DATADIR} diff --git a/root/usr/share/container-scripts/redis/common.sh b/root/usr/share/container-scripts/redis/common.sh deleted file mode 100644 index d131515..0000000 --- a/root/usr/share/container-scripts/redis/common.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -source ${CONTAINER_SCRIPTS_PATH}/helpers.sh - -# Data directory where Redis database files live. The data subdirectory is here -# because .bashrc lives in /var/lib/redis/ and we don't want a -# volume to override it. -export REDIS_DATADIR=/var/lib/redis/data - -# Be paranoid and stricter than we should be. -redis_password_regex='^[a-zA-Z0-9_~!@#$%^&*()-=<>,.?;:|]+$' - -# Make sure env variables don't propagate to redis process. -function unset_env_vars() { - log_info 'Cleaning up environment variable REDIS_PASSWORD ...' - unset REDIS_PASSWORD -} - -# Comment out settings that we'll set in container specifically -function clear_config() { - sed -e "s/^bind/#bind/" \ - -e "s/^logfile/#logfile/" \ - -e "s/^dir /#dir /" \ - -e "/^protected-mode/s/yes/no/" \ - -i "${REDIS_CONF}" -} diff --git a/root/usr/share/container-scripts/redis/helpers.sh b/root/usr/share/container-scripts/redis/helpers.sh deleted file mode 100644 index 4e832fc..0000000 --- a/root/usr/share/container-scripts/redis/helpers.sh +++ /dev/null @@ -1,24 +0,0 @@ -function log_info { - echo "---> `date +%T` $@" -} - -function log_and_run { - log_info "Running $@" - "$@" -} - -function log_volume_info { - CONTAINER_DEBUG=${CONTAINER_DEBUG:-} - if [[ "${CONTAINER_DEBUG,,}" != "true" ]]; then - return - fi - - log_info "Volume info for $@:" - set +e - log_and_run mount - while [ $# -gt 0 ]; do - log_and_run ls -alZ $1 - shift - done - set -e -} diff --git a/root/usr/share/container-scripts/redis/password.conf.template b/root/usr/share/container-scripts/redis/password.conf.template deleted file mode 100644 index bd2eef3..0000000 --- a/root/usr/share/container-scripts/redis/password.conf.template +++ /dev/null @@ -1,3 +0,0 @@ -# password for the server -requirepass "${REDIS_PASSWORD}" - diff --git a/root/usr/share/container-scripts/redis/post-init.sh b/root/usr/share/container-scripts/redis/post-init.sh deleted file mode 100644 index 5ee2c96..0000000 --- a/root/usr/share/container-scripts/redis/post-init.sh +++ /dev/null @@ -1,6 +0,0 @@ -# This file serves for extending the container image, typically by changing -# the configuration, loading some data etc. - -# Feel free to add content to this file or rewrite it at all. -# You may also start redis server locally to load some data for example, -# but do not forget to stop it after it, so it can be restarted after it. diff --git a/root/usr/share/container-scripts/redis/scl_enable b/root/usr/share/container-scripts/redis/scl_enable deleted file mode 100644 index 5a25432..0000000 --- a/root/usr/share/container-scripts/redis/scl_enable +++ /dev/null @@ -1,3 +0,0 @@ -# This will make scl collection binaries work out of box. -unset BASH_ENV PROMPT_COMMAND ENV -source scl_source enable ${ENABLED_COLLECTIONS} diff --git a/root/usr/share/container-scripts/redis/validate-variables.sh b/root/usr/share/container-scripts/redis/validate-variables.sh deleted file mode 100644 index 57138a4..0000000 --- a/root/usr/share/container-scripts/redis/validate-variables.sh +++ /dev/null @@ -1,15 +0,0 @@ -function usage() { - [ $# == 1 ] && echo "error: $1" - echo "You can specify the following environment variables:" - echo " REDIS_PASSWORD (regex: '$redis_password_regex')" - exit 1 -} - -function validate_variables() { - # Check basic sanity of specified variables - if [[ -v REDIS_PASSWORD ]]; then - [[ "$REDIS_PASSWORD" =~ $redis_password_regex ]] || usage "Invalid password" - fi -} - -validate_variables diff --git a/sources b/sources deleted file mode 100644 index e69de29..0000000 diff --git a/test/examples/redis-ephemeral-template.json b/test/examples/redis-ephemeral-template.json deleted file mode 100644 index f00f8e9..0000000 --- a/test/examples/redis-ephemeral-template.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "template.openshift.io/v1", - "metadata": { - "name": "redis-ephemeral", - "annotations": { - "openshift.io/display-name": "Redis (Ephemeral)", - "description": "Redis in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", - "iconClass": "icon-redis", - "tags": "database,redis", - "openshift.io/long-description": "This template provides a standalone Redis server. The data is not stored on persistent storage, so any restart of the service will result in all data being lost.", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "openshift.io/documentation-url": "https://github.com/sclorg/redis-container/tree/master/5", - "openshift.io/support-url": "https://access.redhat.com" - } - }, - "message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Password: ${REDIS_PASSWORD}\n Connection URL: redis://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.", - "labels": { - "template": "redis-ephemeral-template" - }, - "objects": [ - { - "kind": "Secret", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-password": "{.data['database-password']}" - } - }, - "stringData" : { - "database-password" : "${REDIS_PASSWORD}" - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" - } - }, - "spec": { - "ports": [ - { - "name": "redis", - "protocol": "TCP", - "port": 6379, - "targetPort": 6379, - "nodePort": 0 - } - ], - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "DeploymentConfig", - "apiVersion": "apps.openshift.io/v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.alpha.openshift.io/wait-for-ready": "true" - } - }, - "spec": { - "strategy": { - "type": "Recreate" - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "redis" - ], - "from": { - "kind": "ImageStreamTag", - "name": "redis:${REDIS_VERSION}", - "namespace": "${NAMESPACE}" - }, - "lastTriggeredImage": "" - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "template": { - "metadata": { - "labels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "spec": { - "containers": [ - { - "name": "redis", - "image": " ", - "ports": [ - { - "containerPort": 6379, - "protocol": "TCP" - } - ], - "readinessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 5, - "exec": { - "command": [ "/bin/sh", "-i", "-c", "test \"$(redis-cli -h 127.0.0.1 -a $REDIS_PASSWORD ping)\" == \"PONG\""] - } - }, - "livenessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 30, - "tcpSocket": { - "port": 6379 - } - }, - "env": [ - { - "name": "REDIS_PASSWORD", - "valueFrom": { - "secretKeyRef" : { - "name" : "${DATABASE_SERVICE_NAME}", - "key" : "database-password" - } - } - } - ], - "resources": { - "limits": { - "memory": "${MEMORY_LIMIT}" - } - }, - "volumeMounts": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "mountPath": "/var/lib/redis/data" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "IfNotPresent", - "capabilities": {}, - "securityContext": { - "capabilities": {}, - "privileged": false - } - } - ], - "volumes": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "emptyDir": { - "medium": "" - } - } - ], - "restartPolicy": "Always", - "dnsPolicy": "ClusterFirst" - } - } - }, - "status": {} - } - ], - "parameters": [ - { - "name": "MEMORY_LIMIT", - "displayName": "Memory Limit", - "description": "Maximum amount of memory the container can use.", - "value": "512Mi", - "required": true - }, - { - "name": "NAMESPACE", - "displayName": "Namespace", - "description": "The OpenShift Namespace where the ImageStream resides.", - "value": "openshift" - }, - { - "name": "DATABASE_SERVICE_NAME", - "displayName": "Database Service Name", - "description": "The name of the OpenShift Service exposed for the database.", - "value": "redis", - "required": true - }, - { - "name": "REDIS_PASSWORD", - "displayName": "Redis Connection Password", - "description": "Password for the Redis connection user.", - "generate": "expression", - "from": "[a-zA-Z0-9]{16}", - "required": true - }, - { - "name": "REDIS_VERSION", - "displayName": "Version of Redis Image", - "description": "Version of Redis image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", - "value": "6-el8", - "required": true - } - ] -} diff --git a/test/examples/redis-persistent-template.json b/test/examples/redis-persistent-template.json deleted file mode 100644 index f4815cf..0000000 --- a/test/examples/redis-persistent-template.json +++ /dev/null @@ -1,240 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "template.openshift.io/v1", - "metadata": { - "name": "redis-persistent", - "annotations": { - "openshift.io/display-name": "Redis", - "description": "Redis in-memory data structure store, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", - "iconClass": "icon-redis", - "tags": "database,redis", - "openshift.io/long-description": "This template provides a standalone Redis server. The data is stored on persistent storage.", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "openshift.io/documentation-url": "https://github.com/sclorg/redis-container/tree/master/5", - "openshift.io/support-url": "https://access.redhat.com" - } - }, - "message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Password: ${REDIS_PASSWORD}\n Connection URL: redis://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.", - "labels": { - "template": "redis-persistent-template" - }, - "objects": [ - { - "kind": "Secret", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-password": "{.data['database-password']}" - } - }, - "stringData" : { - "database-password" : "${REDIS_PASSWORD}" - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" - } - }, - "spec": { - "ports": [ - { - "name": "redis", - "protocol": "TCP", - "port": 6379, - "targetPort": 6379, - "nodePort": 0 - } - ], - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "PersistentVolumeClaim", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "spec": { - "accessModes": [ - "ReadWriteOnce" - ], - "resources": { - "requests": { - "storage": "${VOLUME_CAPACITY}" - } - } - } - }, - { - "kind": "DeploymentConfig", - "apiVersion": "apps.openshift.io/v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.alpha.openshift.io/wait-for-ready": "true" - } - }, - "spec": { - "strategy": { - "type": "Recreate" - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "redis" - ], - "from": { - "kind": "ImageStreamTag", - "name": "redis:${REDIS_VERSION}", - "namespace": "${NAMESPACE}" - }, - "lastTriggeredImage": "" - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "template": { - "metadata": { - "labels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "spec": { - "containers": [ - { - "name": "redis", - "image": " ", - "ports": [ - { - "containerPort": 6379, - "protocol": "TCP" - } - ], - "readinessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 5, - "exec": { - "command": [ "/bin/sh", "-i", "-c", "test \"$(redis-cli -h 127.0.0.1 -a $REDIS_PASSWORD ping)\" == \"PONG\""] - } - }, - "livenessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 30, - "tcpSocket": { - "port": 6379 - } - }, - "env": [ - { - "name": "REDIS_PASSWORD", - "valueFrom": { - "secretKeyRef" : { - "name" : "${DATABASE_SERVICE_NAME}", - "key" : "database-password" - } - } - } - ], - "resources": { - "limits": { - "memory": "${MEMORY_LIMIT}" - } - }, - "volumeMounts": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "mountPath": "/var/lib/redis/data" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "IfNotPresent", - "capabilities": {}, - "securityContext": { - "capabilities": {}, - "privileged": false - } - } - ], - "volumes": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "persistentVolumeClaim": { - "claimName": "${DATABASE_SERVICE_NAME}" - } - } - ], - "restartPolicy": "Always", - "dnsPolicy": "ClusterFirst" - } - } - }, - "status": {} - } - ], - "parameters": [ - { - "name": "MEMORY_LIMIT", - "displayName": "Memory Limit", - "description": "Maximum amount of memory the container can use.", - "value": "512Mi", - "required": true - }, - { - "name": "NAMESPACE", - "displayName": "Namespace", - "description": "The OpenShift Namespace where the ImageStream resides.", - "value": "openshift" - }, - { - "name": "DATABASE_SERVICE_NAME", - "displayName": "Database Service Name", - "description": "The name of the OpenShift Service exposed for the database.", - "value": "redis", - "required": true - }, - { - "name": "REDIS_PASSWORD", - "displayName": "Redis Connection Password", - "description": "Password for the Redis connection user.", - "generate": "expression", - "from": "[a-zA-Z0-9]{16}", - "required": true - }, - { - "name": "VOLUME_CAPACITY", - "displayName": "Volume Capacity", - "description": "Volume space available for data, e.g. 512Mi, 2Gi.", - "value": "1Gi", - "required": true - }, - { - "name": "REDIS_VERSION", - "displayName": "Version of Redis Image", - "description": "Version of Redis image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", - "value": "6-el8", - "required": true - } - ] -} diff --git a/test/imagestreams/redis-centos.json b/test/imagestreams/redis-centos.json deleted file mode 100644 index 27875a0..0000000 --- a/test/imagestreams/redis-centos.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "apiVersion": "image.openshift.io/v1", - "kind": "ImageStream", - "metadata": { - "annotations": { - "openshift.io/display-name": "Redis" - }, - "name": "redis" - }, - "spec": { - "tags": [ - { - "annotations": { - "description": "Provides a Redis database on CentOS. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Redis available on OpenShift, including major version updates.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis (Latest)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis" - }, - "from": { - "kind": "ImageStreamTag", - "name": "5-el8" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "latest" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on CentOS 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5 (CentOS 8)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "docker.io/centos/redis-5-centos8:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5-el8" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on CentOS 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5 (CentOS 7)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "quay.io/centos7/redis-5-centos7:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5-el7" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on CentOS 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis,hidden", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "quay.io/centos7/redis-5-centos7:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5" - } - ] - } -} diff --git a/test/imagestreams/redis-rhel-aarch64.json b/test/imagestreams/redis-rhel-aarch64.json deleted file mode 100644 index 309351b..0000000 --- a/test/imagestreams/redis-rhel-aarch64.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "apiVersion": "image.openshift.io/v1", - "kind": "ImageStream", - "metadata": { - "annotations": { - "openshift.io/display-name": "Redis" - }, - "name": "redis" - }, - "spec": { - "tags": [ - { - "annotations": { - "description": "Provides a Redis database on RHEL. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Redis available on OpenShift, including major version updates.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis (Latest)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis" - }, - "from": { - "kind": "ImageStreamTag", - "name": "6-el8" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "latest" - }, - { - "annotations": { - "description": "Provides a Redis 6 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 6 (RHEL 8)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "6" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhel8/redis-6:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "6-el8" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5 (RHEL 8)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhel8/redis-5:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5-el8" - } - ] - } -} diff --git a/test/imagestreams/redis-rhel.json b/test/imagestreams/redis-rhel.json deleted file mode 100644 index c9906b2..0000000 --- a/test/imagestreams/redis-rhel.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "apiVersion": "image.openshift.io/v1", - "kind": "ImageStream", - "metadata": { - "annotations": { - "openshift.io/display-name": "Redis" - }, - "name": "redis" - }, - "spec": { - "tags": [ - { - "annotations": { - "description": "Provides a Redis database on RHEL. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version of Redis available on OpenShift, including major version updates.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis (Latest)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis" - }, - "from": { - "kind": "ImageStreamTag", - "name": "6-el8" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "latest" - }, - { - "annotations": { - "description": "Provides a Redis 6 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 6 (RHEL 8)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "6" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhel8/redis-6:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "6-el8" - }, - { - "annotations": { - "description": "Provides a Redis 6 database on RHEL 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/6/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 6 (RHEL 7)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "6" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhscl/redis-6-rhel7:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "6-el7" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on RHEL 8. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5 (RHEL 8)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhel8/redis-5:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5-el8" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on RHEL 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5 (RHEL 7)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhscl/redis-5-rhel7:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5-el7" - }, - { - "annotations": { - "description": "Provides a Redis 5 database on RHEL 7. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/redis-container/tree/master/5/README.md.", - "iconClass": "icon-redis", - "openshift.io/display-name": "Redis 5", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "tags": "redis,hidden", - "version": "5" - }, - "from": { - "kind": "DockerImage", - "name": "registry.redhat.io/rhscl/redis-5-rhel7:latest" - }, - "referencePolicy": { - "type": "Local" - }, - "name": "5" - } - ] - } -} diff --git a/test/redis-ephemeral-template.json b/test/redis-ephemeral-template.json deleted file mode 100644 index f00f8e9..0000000 --- a/test/redis-ephemeral-template.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "template.openshift.io/v1", - "metadata": { - "name": "redis-ephemeral", - "annotations": { - "openshift.io/display-name": "Redis (Ephemeral)", - "description": "Redis in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", - "iconClass": "icon-redis", - "tags": "database,redis", - "openshift.io/long-description": "This template provides a standalone Redis server. The data is not stored on persistent storage, so any restart of the service will result in all data being lost.", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "openshift.io/documentation-url": "https://github.com/sclorg/redis-container/tree/master/5", - "openshift.io/support-url": "https://access.redhat.com" - } - }, - "message": "The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.\n\n Password: ${REDIS_PASSWORD}\n Connection URL: redis://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/redis-container/blob/master/5.", - "labels": { - "template": "redis-ephemeral-template" - }, - "objects": [ - { - "kind": "Secret", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-password": "{.data['database-password']}" - } - }, - "stringData" : { - "database-password" : "${REDIS_PASSWORD}" - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-uri": "redis://{.spec.clusterIP}:{.spec.ports[?(.name==\"redis\")].port}" - } - }, - "spec": { - "ports": [ - { - "name": "redis", - "protocol": "TCP", - "port": 6379, - "targetPort": 6379, - "nodePort": 0 - } - ], - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "DeploymentConfig", - "apiVersion": "apps.openshift.io/v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.alpha.openshift.io/wait-for-ready": "true" - } - }, - "spec": { - "strategy": { - "type": "Recreate" - }, - "triggers": [ - { - "type": "ImageChange", - "imageChangeParams": { - "automatic": true, - "containerNames": [ - "redis" - ], - "from": { - "kind": "ImageStreamTag", - "name": "redis:${REDIS_VERSION}", - "namespace": "${NAMESPACE}" - }, - "lastTriggeredImage": "" - } - }, - { - "type": "ConfigChange" - } - ], - "replicas": 1, - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "template": { - "metadata": { - "labels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "spec": { - "containers": [ - { - "name": "redis", - "image": " ", - "ports": [ - { - "containerPort": 6379, - "protocol": "TCP" - } - ], - "readinessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 5, - "exec": { - "command": [ "/bin/sh", "-i", "-c", "test \"$(redis-cli -h 127.0.0.1 -a $REDIS_PASSWORD ping)\" == \"PONG\""] - } - }, - "livenessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 30, - "tcpSocket": { - "port": 6379 - } - }, - "env": [ - { - "name": "REDIS_PASSWORD", - "valueFrom": { - "secretKeyRef" : { - "name" : "${DATABASE_SERVICE_NAME}", - "key" : "database-password" - } - } - } - ], - "resources": { - "limits": { - "memory": "${MEMORY_LIMIT}" - } - }, - "volumeMounts": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "mountPath": "/var/lib/redis/data" - } - ], - "terminationMessagePath": "/dev/termination-log", - "imagePullPolicy": "IfNotPresent", - "capabilities": {}, - "securityContext": { - "capabilities": {}, - "privileged": false - } - } - ], - "volumes": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "emptyDir": { - "medium": "" - } - } - ], - "restartPolicy": "Always", - "dnsPolicy": "ClusterFirst" - } - } - }, - "status": {} - } - ], - "parameters": [ - { - "name": "MEMORY_LIMIT", - "displayName": "Memory Limit", - "description": "Maximum amount of memory the container can use.", - "value": "512Mi", - "required": true - }, - { - "name": "NAMESPACE", - "displayName": "Namespace", - "description": "The OpenShift Namespace where the ImageStream resides.", - "value": "openshift" - }, - { - "name": "DATABASE_SERVICE_NAME", - "displayName": "Database Service Name", - "description": "The name of the OpenShift Service exposed for the database.", - "value": "redis", - "required": true - }, - { - "name": "REDIS_PASSWORD", - "displayName": "Redis Connection Password", - "description": "Password for the Redis connection user.", - "generate": "expression", - "from": "[a-zA-Z0-9]{16}", - "required": true - }, - { - "name": "REDIS_VERSION", - "displayName": "Version of Redis Image", - "description": "Version of Redis image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", - "value": "6-el8", - "required": true - } - ] -} diff --git a/test/run b/test/run deleted file mode 100755 index 567ed16..0000000 --- a/test/run +++ /dev/null @@ -1,359 +0,0 @@ -#!/bin/bash -# -# Test the Redis image. -# -# IMAGE_NAME specifies the name of the candidate image used for testing. -# The image has to be available before this script is executed. -# - -set -o errexit -set -o nounset -shopt -s nullglob - -[ "${DEBUG:-0}" -eq 1 ] && set -x - -test -n "${IMAGE_NAME-}" || { echo 'make sure $IMAGE_NAME is defined' && false ;} -test -n "${VERSION-}" || { echo 'make sure $VERSION is defined' && false; } -test -n "${OS-}" || { echo 'make sure $OS is defined' && false; } - -test_short_summary='' -TESTSUITE_RESULT=0 - -TEST_LIST="\ -run_container_creation_tests -run_tests_no_root -run_tests_no_pass -run_tests_no_pass_altuid -run_tests_no_root_altuid -run_change_password_test -run_doc_test -" - -CIDFILE_DIR=$(mktemp --suffix=redis_test_cidfiles -d) - -function cleanup() { - local cidfile - for cidfile in $CIDFILE_DIR/* ; do - local CONTAINER - CONTAINER=$(cat $cidfile) - - echo "Stopping and removing container $CONTAINER..." - docker stop $CONTAINER >/dev/null - local exit_status - exit_status=$(docker inspect -f '{{.State.ExitCode}}' $CONTAINER) - if [ "$exit_status" != "0" ]; then - echo "Inspecting container $CONTAINER" - docker inspect $CONTAINER - echo "Dumping logs for $CONTAINER" - docker logs $CONTAINER - fi - docker rm -v $CONTAINER >/dev/null - rm $cidfile - echo "Done." - done - rmdir $CIDFILE_DIR - - 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 -} -trap cleanup EXIT SIGINT - -check_result() { - local result="$1" - if [[ "$result" != "0" ]]; then - TESTCASE_RESULT=1 - fi - return $result -} - -function get_cid() { - local id="$1" ; shift || return 1 - echo $(cat "$CIDFILE_DIR/$id") -} - -function get_container_ip() { - local id="$1" ; shift - docker inspect --format='{{.NetworkSettings.IPAddress}}' $(get_cid "$id") -} - -function connection_works() { - local container_ip="$1"; shift - local password="$1"; shift - if [ "$(redis_cmd "$container_ip" "$password" ping)" == "PONG" ] ; then - return 0 - fi - return 1 -} - -function redis_cmd() { - local container_ip="$1"; shift - local password="$1"; shift - # if empty password is given, then no password will be specified - docker run --rm "$IMAGE_NAME" redis-cli -h "$container_ip" ${password:+-a "$password"} "$@" -} - -function test_connection() { - local name=$1 ; shift - local password=$1 ; shift - local ip - ip=$(get_container_ip $name) - echo " Testing Redis connection to $ip (password='${password:-}')..." - local max_attempts=10 - local sleep_time=2 - local i - for i in $(seq $max_attempts); do - echo " Trying to connect..." - if connection_works "$ip" "$password" ; then - echo " Success!" - echo - return 0 - fi - sleep $sleep_time - done - echo " Giving up: Failed to connect. Logs:" - docker logs $(get_cid $name) - return 1 -} - -function test_redis() { - local container_ip="$1" - local password="$2" - - echo " Testing Redis (password='${password:-}')" - redis_cmd "$container_ip" "$password" set a 1 >/dev/null - check_result $? - redis_cmd "$container_ip" "$password" set b 2 >/dev/null - check_result $? - test "$(redis_cmd "$container_ip" "$password" get b)" == '2' - echo " Success!" - echo -} - -function create_container() { - local name=$1 ; shift - cidfile="$CIDFILE_DIR/$name" - # create container with a cidfile in a directory for cleanup - local container_id - [ "${DEBUG:-0}" -eq 1 ] && echo "DEBUG: docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d \"$@\" $IMAGE_NAME ${CONTAINER_ARGS:-}" >&2 - container_id="$(docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d "$@" $IMAGE_NAME ${CONTAINER_ARGS:-})" - [ "${DEBUG:-0}" -eq 1 ] && echo "Created container $container_id" - [ x"$container_id" == "x" ] && return 1 || return 0 -} - -function run_change_password_test() { - local tmpdir=$(mktemp -d) - mkdir "${tmpdir}/data" && chmod -R a+rwx "${tmpdir}" - - # Create Redis container with persistent volume and set the initial password - create_container "testpass1" -e REDIS_PASSWORD=foo \ - -v ${tmpdir}:/var/lib/redis/data:Z - check_result $? - test_connection testpass1 foo - check_result $? - docker stop $(get_cid testpass1) >/dev/null - - # Create second container with changed password - create_container "testpass2" -e REDIS_PASSWORD=bar \ - -v ${tmpdir}:/var/lib/redis/data:Z - check_result $? - test_connection testpass2 bar - check_result $? - # The old password should not work anymore - container_ip="$(get_container_ip testpass2)" - connection_works "$container_ip" foo - check_result $? -} - -function assert_login_access() { - local container_ip=$1; shift - local PASS=$1 ; shift - local success=$1 ; shift - - if connection_works "$container_ip" "$PASS" ; then - if $success ; then - echo " Connection ($PASS) access granted as expected" - return 0 - fi - else - if ! $success ; then - echo " Connection ($PASS) access denied as expected" - return 0 - fi - fi - echo " Connection ($PASS) login assertion failed" - return 1 -} - -function assert_local_access() { - local id="$1" ; shift - docker exec $(get_cid "$id") bash -c 'redis-cli ping' -} - -# Make sure the invocation of docker run fails. -function assert_container_creation_fails() { - - # Time the docker run command. It should fail. If it doesn't fail, - # redis will keep running so we kill it with SIGKILL to make sure - # timeout returns a non-zero value. - local ret=0 - timeout -s 9 --preserve-status 60s docker run --rm "$@" $IMAGE_NAME >/dev/null || ret=$? - - # Timeout will exit with a high number. - if [ $ret -gt 10 ]; then - return 1 - fi -} - -function try_image_invalid_combinations() { - assert_container_creation_fails -e REDIS_PASSWORD="pass with space" "$@" - check_result $? -} - -function run_container_creation_tests() { - local ret - echo " Testing image entrypoint usage" - try_image_invalid_combinations - ret=$? - if [ $ret -eq 0 ]; then - echo " Success!" - else - echo " Failed!" - fi - echo - return $ret -} - -test_scl_usage() { - local name="$1" - local run_cmd="$2" - local expected="$3" - - echo " Testing the image SCL enable" - local out - out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}") - if ! echo "${out}" | grep -q "${expected}"; then - echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" - return 1 - fi - out=$(docker exec $(get_cid $name) /bin/bash -c "${run_cmd}" 2>&1) - if ! echo "${out}" | grep -q "${expected}"; then - echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" - return 1 - fi - out=$(docker exec $(get_cid $name) /bin/sh -ic "${run_cmd}" 2>&1) - if ! echo "${out}" | grep -q "${expected}"; then - echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'" - return 1 - fi -} - -run_doc_test() { - local tmpdir=$(mktemp -d) - local f - echo " Testing documentation in the container image" - # Extract the help.1 file from the container - docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /help.1" >${tmpdir}/help.1 - # Check whether the help.1 file includes some important information - for term in 6379 "REDIS\_PASSWORD" volume; do - if ! cat ${tmpdir}/help.1 | grep -F -q -e "${term}" ; then - echo "ERROR: File /help.1 does not include '${term}'." - return 1 - fi - done - # Check whether the file uses the correct format - if ! file ${tmpdir}/help.1 | grep -q roff ; then - echo "ERROR: /help.1 is not in troff or groff format" - return 1 - fi - echo " Success!" - echo -} - -function run_tests() { - local name=$1 ; shift - local ret - envs=${PASS:+"-e REDIS_PASSWORD=$PASS"} - PASS=${PASS:-} - create_container $name $envs - ret=$? - check_result $ret - test_connection "$name" "$PASS" - ret=$? - check_result $ret - # Only check version on rhel/centos builds - if [ "$OS" != "fedora" ]; then - echo " Testing scl usage" - test_scl_usage $name 'redis-server --version' "$VERSION" - check_result $? - fi - echo " Testing login accesses" - local container_ip - container_ip=$(get_container_ip $name) - assert_login_access "$container_ip" "$PASS" true - ret=$? - check_result $ret - if [ -n "$PASS" ] ; then - assert_login_access "$container_ip" "${PASS}_foo" false - check_result $? - fi - assert_local_access "$name" - ret=$? - check_result $ret - if [ $ret -ne 0 ]; then - echo " Local access FAILED." - else - echo " Local access SUCCESS." - fi - echo - test_redis "$container_ip" "$PASS" - check_result $? -} - -function run_tests_no_root() { - # Normal tests with password - PASS=pass run_tests no_root -} - -function run_tests_no_pass() { - # Normal tests without password - run_tests no_pass -} - -function run_tests_no_pass_altuid() { - # Test with arbitrary uid for the container without password - DOCKER_ARGS="-u 12345" run_tests no_pass_altuid -} - -function run_tests_no_root_altuid() { - # Test with arbitrary uid for the container with password - DOCKER_ARGS="-u 12345" PASS=pass run_tests no_root_altuid -} - -function run_all_tests() { - for test_case in $TEST_SET; do - echo "Running test $test_case ...." - TESTCASE_RESULT=0 - $test_case - check_result $? - local test_msg - if [ $TESTCASE_RESULT -eq 0 ]; then - test_msg="[PASSED]" - else - test_msg="[FAILED]" - TESTSUITE_RESULT=1 - fi - printf -v test_short_summary "%s %s for '%s' %s\n" "${test_short_summary}" "${test_msg}" "$test_case" - [ -n "${FAIL_QUICKLY:-}" ] && cleanup "${APP_NAME}" && return 1 - done; -} - -TEST_SET=${TESTS:-$TEST_LIST} run_all_tests - -echo "Success!" -cleanup diff --git a/test/run-openshift b/test/run-openshift deleted file mode 120000 index d84575f..0000000 --- a/test/run-openshift +++ /dev/null @@ -1 +0,0 @@ -run-openshift-local-cluster \ No newline at end of file diff --git a/test/run-openshift-local-cluster b/test/run-openshift-local-cluster deleted file mode 100755 index e4dbbc1..0000000 --- a/test/run-openshift-local-cluster +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash -# -# Test the Redis 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-openshift.sh -source ${THISDIR}/test-lib-redis.sh - -set -eo nounset - -trap ct_os_cleanup EXIT SIGINT - -ct_os_check_compulsory_vars - -ct_os_enable_print_logs - -function check_redis_os_service_connection() { - local util_image_name=$1 ; shift - local service_name=$1 ; shift - local pass=$1 ; shift - local timeout=${1:-60} ; shift || : - local pod_ip=$(ct_os_get_service_ip ${service_name}) - - : " Service ${service_name} check ..." - - local cmd="timeout 15 redis-cli -h $pod_ip -a $pass ping" - local expected_value="PONG" - local output - local ret - SECONDS=0 - - echo -n "Waiting for ${service_name} service becoming ready ..." - while true ; do - output=$(docker run --rm ${util_image_name} bash -c "${cmd}" || :) - echo "${output}" | grep -qe "${expected_value}" && ret=0 || ret=1 - if [ ${ret} -eq 0 ] ; then - echo " PASS" - return 0 - fi - echo -n "." - [ ${SECONDS} -gt ${timeout} ] && break - sleep 3 - done - echo " FAIL" - return 1 -} - -function test_redis_pure_image() { - local image_name=$1 - local image_name_no_namespace=${image_name##*/} - local service_name="${image_name_no_namespace%%:*}-testing" - - ct_os_new_project - # Create a specific imagestream tag for the image so that oc cannot use anything else - ct_os_upload_image "${image_name}" "$image_name_no_namespace" - - ct_os_deploy_pure_image "$image_name_no_namespace" \ - --name "${service_name}" \ - --env REDIS_PASSWORD=pass - - ct_os_wait_pod_ready "${service_name}" 60 - check_redis_os_service_connection "${image_name}" "${service_name}" pass - - ct_os_delete_project -} - -function test_redis_template() { - local image_name=${1:-quay.io/centos7/redis-5-centos7} - local image_name_no_namespace=${image_name##*/} - local service_name="${image_name_no_namespace%%:*}-testing" - - ct_os_new_project - ct_os_upload_image "${image_name}" "redis:$VERSION" - - ct_os_deploy_template_image "$THISDIR/redis-ephemeral-template.json" \ - NAMESPACE="$(oc project -q)" \ - REDIS_VERSION="$VERSION" \ - DATABASE_SERVICE_NAME="${service_name}" \ - REDIS_PASSWORD=pass - - ct_os_wait_pod_ready "${service_name}" 60 - check_redis_os_service_connection "${image_name}" "${service_name}" pass - - ct_os_delete_project -} - -ct_os_cluster_up -test_redis_pure_image "${IMAGE_NAME}" -test_redis_template "${IMAGE_NAME}" - -# test with the just built image and an integrated template -test_redis_integration "${IMAGE_NAME}" - -# test with a released image and an integrated template -PUBLIC_IMAGE_NAME=${PUBLIC_IMAGE_NAME:-$(ct_get_public_image_name "${OS}" "${BASE_IMAGE_NAME}" "${VERSION}")} - -# Try pulling the image first to see if it is accessible -if ct_check_image_availability "$PUBLIC_IMAGE_NAME"; then - test_redis_integration "${PUBLIC_IMAGE_NAME}" -else - echo "Warning: ${PUBLIC_IMAGE_NAME} could not be downloaded via 'docker'" - # ignore possible failure of this test for centos images - [ "${OS}" == "rhel7" ] && false "ERROR: Failed to pull image" -fi - -# Check the imagestream -test_redis_imagestream - -OS_TESTSUITE_RESULT=0 - -ct_os_cluster_down - -# vim: set tabstop=2:shiftwidth=2:expandtab: - diff --git a/test/run-openshift-remote-cluster b/test/run-openshift-remote-cluster deleted file mode 100755 index 2199f48..0000000 --- a/test/run-openshift-remote-cluster +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Test the Redis image in OpenShift (remote cluster) -# -# IMAGE_NAME specifies a name of the candidate image used for testing. -# The image has to be available before this script is executed. -# VERSION specifies the major version of the Redis in format of X.Y -# OS specifies RHEL version (e.g. OS=rhel7) -# - -THISDIR=$(dirname ${BASH_SOURCE[0]}) - -source ${THISDIR}/test-lib-redis.sh - -set -eo nounset - -trap ct_os_cleanup EXIT SIGINT - -ct_os_set_ocp4 - -ct_os_check_compulsory_vars - -oc status || false "It looks like oc is not properly logged in." - -# For testing on OpenShift 4 we use external registry -export CT_EXTERNAL_REGISTRY=true - -# Check the template -test_redis_integration "${IMAGE_NAME}" - -# Check the imagestream -test_redis_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 deleted file mode 100644 index 624a391..0000000 --- a/test/test-lib-openshift.sh +++ /dev/null @@ -1,1364 +0,0 @@ -# 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 -OS_TESTSUITE_RESULT=1 -OS_CLUSTER_STARTED_BY_TEST=0 - -function ct_os_cleanup() { - if [ $OS_TESTSUITE_RESULT -eq 0 ] ; then - # shellcheck disable=SC2153 - echo "OpenShift tests for ${IMAGE_NAME} succeeded." - else - # shellcheck disable=SC2153 - echo "OpenShift tests for ${IMAGE_NAME} failed." - fi -} - -# ct_os_check_compulsory_vars -# --------------------------- -# Check the compulsory variables: -# * IMAGE_NAME specifies a name of the candidate image used for testing. -# * VERSION specifies the major version of the MariaDB in format of X.Y -# * OS specifies RHEL version (e.g. OS=rhel7) -function ct_os_check_compulsory_vars() { - # shellcheck disable=SC2016 - test -n "${IMAGE_NAME-}" || ( echo 'make sure $IMAGE_NAME is defined' >&2 ; exit 1) - # shellcheck disable=SC2016 - test -n "${VERSION-}" || ( echo 'make sure $VERSION is defined' >&2 ; exit 1) - # shellcheck disable=SC2016 - test -n "${OS-}" || ( echo 'make sure $OS is defined' >&2 ; exit 1) -} - -# ct_os_get_status -# -------------------- -# Returns status of all objects to make debugging easier. -function ct_os_get_status() { - oc get all - oc status -} - -# ct_os_print_logs -# -------------------- -# Returns status of all objects and logs from all pods. -function ct_os_print_logs() { - ct_os_get_status - while read -r pod_name; do - echo "INFO: printing logs for pod ${pod_name}" - oc logs "${pod_name}" - done < <(oc get pods --no-headers=true -o custom-columns=NAME:.metadata.name) -} - -# ct_os_enable_print_logs -# -------------------- -# Enables automatic printing of pod logs on ERR. -function ct_os_enable_print_logs() { - set -E - trap ct_os_print_logs ERR -} - -# ct_get_public_ip -# -------------------- -# Returns best guess for the IP that the node is accessible from other computers. -# This is a bit funny heuristic, simply goes through all IPv4 addresses that -# hostname -I returns and de-prioritizes IP addresses commonly used for local -# addressing. The rest of addresses are taken as public with higher probability. -function ct_get_public_ip() { - local hostnames - local public_ip='' - local found_ip - hostnames=$(hostname -I) - for guess_exp in '127\.0\.0\.1' '192\.168\.[0-9\.]*' '172\.[0-9\.]*' \ - '10\.[0-9\.]*' '[0-9\.]*' ; do - found_ip=$(echo "${hostnames}" | grep -oe "${guess_exp}") - if [ -n "${found_ip}" ] ; then - # shellcheck disable=SC2001 - hostnames=$(echo "${hostnames}" | sed -e "s/${found_ip}//") - public_ip="${found_ip}" - fi - done - if [ -z "${public_ip}" ] ; then - echo "ERROR: public IP could not be guessed." >&2 - return 1 - fi - echo "${public_ip}" -} - -# ct_os_run_in_pod POD_NAME CMD -# -------------------- -# Runs [cmd] in the pod specified by prefix [pod_prefix]. -# Arguments: pod_name - full name of the pod -# Arguments: cmd - command to be run in the pod -function ct_os_run_in_pod() { - local pod_name="$1" ; shift - - oc exec "$pod_name" -- "$@" -} - -# ct_os_get_service_ip SERVICE_NAME -# -------------------- -# Returns IP of the service specified by [service_name]. -# Arguments: service_name - name of the service -function ct_os_get_service_ip() { - local service_name="${1}" ; shift - oc get "svc/${service_name}" -o yaml | grep clusterIP | \ - cut -d':' -f2 | grep -oe '172\.30\.[0-9\.]*' -} - - -# ct_os_get_all_pods_status -# -------------------- -# Returns status of all pods. -function ct_os_get_all_pods_status() { - oc get pods -o custom-columns=Ready:status.containerStatuses[0].ready,NAME:.metadata.name -} - -# ct_os_get_all_pods_name -# -------------------- -# Returns the full name of all pods. -function ct_os_get_all_pods_name() { - oc get pods --no-headers -o custom-columns=NAME:.metadata.name -} - -# ct_os_get_pod_status POD_PREFIX -# -------------------- -# Returns status of the pod specified by prefix [pod_prefix]. -# Note: Ignores -build and -deploy pods -# Arguments: pod_prefix - prefix or whole ID of the pod -function ct_os_get_pod_status() { - local pod_prefix="${1}" ; shift - ct_os_get_all_pods_status | grep -e "${pod_prefix}" | grep -Ev "(build|deploy)$" \ - | awk '{print $1}' | head -n 1 -} - -# ct_os_get_build_pod_status POD_PREFIX -# -------------------- -# Returns status of the build pod specified by prefix [pod_prefix]. -# Arguments: pod_prefix - prefix or whole ID of the pod -function ct_os_get_build_pod_status() { - local pod_prefix="${1}" ; shift - local query="custom-columns=NAME:.metadata.name,Ready:status.phase" - oc get pods -o "$query" | grep -e "${pod_prefix}" | grep -E "\-build\s" \ - | sort -u | awk '{print $2}' | tail -n 1 -} - -# ct_os_get_buildconfig_pod_name POD_PREFIX -# ---------------------------- -# Returns status of the buildconfig pod specified by prefix [pod_prefix]. -# Argument: pod_prefix - prefix -function ct_os_get_buildconfig_pod_name() { - local pod_prefix="${1}" ; shift - local query="custom-columns=NAME:.metadata.name" - oc get bc -o "$query" | grep -e "${pod_prefix}" | sort -u | tail -n 1 -} - -# ct_os_get_pod_name POD_PREFIX -# -------------------- -# Returns the full name of pods specified by prefix [pod_prefix]. -# Note: Ignores -build and -deploy pods -# Arguments: pod_prefix - prefix or whole ID of the pod -function ct_os_get_pod_name() { - local pod_prefix="${1}" ; shift - ct_os_get_all_pods_name | grep -e "^${pod_prefix}" | grep -Ev "(build|deploy)$" -} - -# ct_os_get_pod_ip POD_NAME -# -------------------- -# Returns the ip of the pod specified by [pod_name]. -# Arguments: pod_name - full name of the pod -function ct_os_get_pod_ip() { - local pod_name="${1}" - oc get pod "$pod_name" --no-headers -o custom-columns=IP:status.podIP -} - -# ct_os_get_sti_build_logs -# ----------------- -# Return logs from sti_build -# Arguments: pod_name -function ct_os_get_sti_build_logs() { - local pod_prefix="${1}" - pod_name=$(ct_os_get_buildconfig_pod_name "${pod_prefix}") - # Print logs but do not failed. Just for traces - if [ x"${pod_name}" != "x" ]; then - oc logs "bc/$pod_name" || return 0 - else - echo "Build config bc/$pod_name does not exist for some reason." - echo "Import probably failed." - fi -} - -# ct_os_check_pod_readiness POD_PREFIX STATUS -# -------------------- -# Checks whether the pod is ready. -# Arguments: pod_prefix - prefix or whole ID of the pod -# Arguments: status - expected status (true, false) -function ct_os_check_pod_readiness() { - local pod_prefix="${1}" ; shift - local status="${1}" ; shift - test "$(ct_os_get_pod_status "${pod_prefix}")" == "${status}" -} - -# ct_os_wait_pod_ready POD_PREFIX TIMEOUT -# -------------------- -# Wait maximum [timeout] for the pod becomming ready. -# Arguments: pod_prefix - prefix or whole ID of the pod -# Arguments: timeout - how many seconds to wait seconds -function ct_os_wait_pod_ready() { - local pod_prefix="${1}" ; shift - local timeout="${1}" ; shift - # If there is a build pod - wait for it to finish first - sleep 3 - if ct_os_get_all_pods_name | grep -E "${pod_prefix}.*-build"; then - SECONDS=0 - echo -n "Waiting for ${pod_prefix} build pod to finish ..." - while ! [ "$(ct_os_get_build_pod_status "${pod_prefix}")" == "Succeeded" ] ; do - echo -n "." - if [ "${SECONDS}" -gt "${timeout}0" ]; then - echo " FAIL" - ct_os_print_logs || : - ct_os_get_sti_build_logs "${pod_prefix}" || : - return 1 - fi - sleep 3 - done - echo " DONE" - fi - SECONDS=0 - echo -n "Waiting for ${pod_prefix} pod becoming ready ..." - while ! ct_os_check_pod_readiness "${pod_prefix}" "true" ; do - echo -n "." - if [ "${SECONDS}" -gt "${timeout}" ]; then - echo " FAIL"; - ct_os_print_logs || : - ct_os_get_sti_build_logs "${pod_prefix}" || : - return 1 - fi - sleep 3 - done - echo " DONE" -} - -# ct_os_wait_rc_ready POD_PREFIX TIMEOUT -# -------------------- -# Wait maximum [timeout] for the rc having desired number of replicas ready. -# Arguments: pod_prefix - prefix of the replication controller -# Arguments: timeout - how many seconds to wait seconds -function ct_os_wait_rc_ready() { - local pod_prefix="${1}" ; shift - local timeout="${1}" ; shift - SECONDS=0 - echo -n "Waiting for ${pod_prefix} having desired numbers of replicas ..." - while ! test "$( (oc get --no-headers statefulsets; oc get --no-headers rc) 2>/dev/null \ - | grep "^${pod_prefix}" | awk '$2==$3 {print "ready"}')" == "ready" ; do - echo -n "." - if [ "${SECONDS}" -gt "${timeout}" ]; then - echo " FAIL"; - ct_os_print_logs || : - ct_os_get_sti_build_logs "${pod_prefix}" || : - return 1 - fi - sleep 3 - done - echo " DONE" -} - -# ct_os_deploy_pure_image IMAGE [ENV_PARAMS, ...] -# -------------------- -# Runs [image] in the openshift and optionally specifies env_params -# as environment variables to the image. -# Arguments: image - prefix or whole ID of the pod to run the cmd in -# Arguments: env_params - environment variables parameters for the images. -function ct_os_deploy_pure_image() { - local image="${1}" ; shift - # ignore error exit code, because oc new-app returns error when image exists - oc new-app "${image}" "$@" || : - # let openshift cluster to sync to avoid some race condition errors - sleep 3 -} - -# ct_os_deploy_s2i_image IMAGE APP [ENV_PARAMS, ... ] -# -------------------- -# Runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. -# Arguments: image - prefix or whole ID of the pod to run the cmd in -# Arguments: app - url or local path to git repo with the application sources. -# Arguments: env_params - environment variables parameters for the images. -function ct_os_deploy_s2i_image() { - local image="${1}" ; shift - local app="${1}" ; shift - # ignore error exit code, because oc new-app returns error when image exists - oc new-app "${image}~${app}" --strategy=source "$@" || : - - # let openshift cluster to sync to avoid some race condition errors - sleep 3 -} - -# ct_os_deploy_template_image TEMPLATE [ENV_PARAMS, ...] -# -------------------- -# Runs template in the openshift and optionally gives env_params to use -# specific values in the template. -# Arguments: template - prefix or whole ID of the pod to run the cmd in -# Arguments: env_params - environment variables parameters for the template. -# Example usage: ct_os_deploy_template_image mariadb-ephemeral-template.yaml \ -# DATABASE_SERVICE_NAME=mysql-57-centos7 \ -# DATABASE_IMAGE=mysql-57-centos7 \ -# MYSQL_USER=testu \ -# MYSQL_PASSWORD=testp \ -# MYSQL_DATABASE=testdb -function ct_os_deploy_template_image() { - local template="${1}" ; shift - oc process -f "${template}" "$@" | oc create -f - - # let openshift cluster to sync to avoid some race condition errors - sleep 3 -} - -# _ct_os_get_uniq_project_name -# -------------------- -# Returns a uniq name of the OpenShift project. -function _ct_os_get_uniq_project_name() { - local r - while true ; do - r=${RANDOM} - mkdir /var/tmp/sclorg-test-${r} &>/dev/null && echo sclorg-test-${r} && break - done -} - -# ct_os_new_project [PROJECT] -# -------------------- -# Creates a new project in the openshfit using 'os' command. -# Arguments: project - project name, uses a new random name if omitted -# Expects 'os' command that is properly logged in to the OpenShift cluster. -# Not using mktemp, because we cannot use uppercase characters. -# The OPENSHIFT_CLUSTER_PULLSECRET_PATH environment variable can be set -# to contain a path to a k8s secret definition which will be used -# to authenticate to image registries. -# shellcheck disable=SC2120 -function ct_os_new_project() { - if [ "${CVP:-0}" -eq "1" ]; then - echo "Testing in CVP environment. No need to create OpenShift project. This is done by CVP pipeline" - return - fi - if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then - echo "Creating project skipped." - return - fi - local project_name="${1:-$(_ct_os_get_uniq_project_name)}" ; shift || : - oc new-project "${project_name}" - # let openshift cluster to sync to avoid some race condition errors - sleep 3 - if test -n "${OPENSHIFT_CLUSTER_PULLSECRET_PATH:-}" -a -e "${OPENSHIFT_CLUSTER_PULLSECRET_PATH:-}"; then - oc create -f "$OPENSHIFT_CLUSTER_PULLSECRET_PATH" - # add registry pullsecret to the serviceaccount if provided - secret_name=$(grep '^\s*name:' "$OPENSHIFT_CLUSTER_PULLSECRET_PATH" | awk '{ print $2 }') - oc secrets link --for=pull default "$secret_name" - fi -} - -# ct_os_delete_project [PROJECT] -# -------------------- -# Deletes the specified project in the openshfit -# Arguments: project - project name, uses the current project if omitted -# shellcheck disable=SC2120 -function ct_os_delete_project() { - if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] || [ "${CVP:-0}" -eq "1" ]; then - echo "Deleting project skipped, cleaning objects only." - # when not having enough privileges (remote cluster), it might fail and - # it is not a big problem, so ignore failure in this case - ct_delete_all_objects || : - return - fi - local project_name="${1:-$(oc project -q)}" ; shift || : - if oc delete project "${project_name}" ; then - echo "Project ${project_name} was deleted properly" - else - echo "Project ${project_name} was not delete properly. But it does not block CI." - fi - -} - -# ct_delete_all_objects -# ----------------- -# Deletes all objects within the project. -# Handy when we have one project and want to run more tests. -function ct_delete_all_objects() { - for x in bc builds dc is isimage istag po pv pvc rc routes secrets svc ; do - oc delete "$x" --all - done - # for some objects it takes longer to be really deleted, so a dummy sleep - # to avoid some races when other test can see not-yet-deleted objects and can fail - sleep 10 -} - -# ct_os_docker_login -# -------------------- -# 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 - # docker login fails with "404 page not found" error sometimes, just try it more times - # shellcheck disable=SC2034 - for i in $(seq 12) ; do - # shellcheck disable=SC2015 - docker login -u developer -p "$(oc whoami -t)" "${REGISRTY_ADDRESS:-172.30.1.1:5000}" && return 0 || : - sleep 5 - done - return 1 -} - -# ct_os_upload_image IMAGE [IMAGESTREAM] -# -------------------- -# Uploads image from local registry to the OpenShift internal registry. -# Arguments: image - image name to upload -# Arguments: imagestream - name and tag to use for the internal registry. -# 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 input_name="${1}" ; shift - local image_name=${input_name##*/} - local imagestream=${1:-$image_name:latest} - local output_name - - output_name="${REGISRTY_ADDRESS:-172.30.1.1:5000}/$(oc project -q)/$imagestream" - - ct_os_docker_login - docker tag "${input_name}" "${output_name}" - docker push "${output_name}" -} - -# ct_os_is_tag_exists IS_NAME TAG -# -------------------- -# Checks whether the specified tag exists for an image stream -# Arguments: is_name - name of the image stream -# Arguments: tag - name of the tag (usually version) -function ct_os_is_tag_exists() { - local is_name=$1 ; shift - local tag=$1 ; shift - oc get is "${is_name}" -n openshift -o=jsonpath='{.spec.tags[*].name}' | grep -qw "${tag}" -} - -# ct_os_template_exists T_NAME -# -------------------- -# Checks whether the specified template exists for an image stream -# Arguments: t_name - template name of the image stream -function ct_os_template_exists() { - local t_name=$1 ; shift - oc get templates -n openshift | grep -q "^${t_name}\s" -} - -# ct_os_install_in_centos -# -------------------- -# Installs os cluster in CentOS -function ct_os_install_in_centos() { - yum install -y centos-release-openshift-origin - yum install -y wget git net-tools bind-utils iptables-services bridge-utils\ - bash-completion origin-clients docker origin-clients -} - - -# ct_os_cluster_up [DIR, IS_PUBLIC, CLUSTER_VERSION] -# -------------------- -# Runs the local OpenShift cluster using 'oc cluster up' and logs in as developer. -# Arguments: dir - directory to keep configuration data in, random if omitted -# Arguments: is_public - sets either private or public hostname for web-UI, -# use "true" for allow remote access to the web-UI, -# "false" is default -# Arguments: cluster_version - version of the OpenShift cluster to use, empty -# means default version of `oc`; example value: 3.7; -# also can be specified outside by OC_CLUSTER_VERSION -function ct_os_cluster_up() { - ct_os_cluster_running && echo "Cluster already running. Nothing is done." && return 0 - ct_os_logged_in && echo "Already logged in to a cluster. Nothing is done." && return 0 - - mkdir -p /var/tmp/openshift - local dir="${1:-$(mktemp -d /var/tmp/openshift/os-data-XXXXXX)}" ; shift || : - local is_public="${1:-'false'}" ; shift || : - local default_cluster_version=${OC_CLUSTER_VERSION:-} - local cluster_version=${1:-${default_cluster_version}} ; shift || : - if ! grep -qe '--insecure-registry.*172\.30\.0\.0' /etc/sysconfig/docker ; then - sed -i "s|OPTIONS='|OPTIONS='--insecure-registry 172.30.0.0/16 |" /etc/sysconfig/docker - fi - - systemctl stop firewalld || : - setenforce 0 - iptables -F - - systemctl restart docker - local cluster_ip="127.0.0.1" - [ "${is_public}" == "true" ] && cluster_ip=$(ct_get_public_ip) - - if [ -n "${cluster_version}" ] ; then - # if $cluster_version is not set, we simply use oc that is available - ct_os_set_path_oc "${cluster_version}" - fi - - mkdir -p "${dir}"/{config,data,pv} - case $(oc version| head -n 1) in - "oc v3.1"?.*) - oc cluster up --base-dir="${dir}/data" --public-hostname="${cluster_ip}" - ;; - "oc v3."*) - oc cluster up --host-data-dir="${dir}/data" --host-config-dir="${dir}/config" \ - --host-pv-dir="${dir}/pv" --use-existing-config --public-hostname="${cluster_ip}" - ;; - *) - echo "ERROR: Unexpected oc version." >&2 - return 1 - ;; - esac - oc version - oc login -u system:admin - oc project default - ct_os_wait_rc_ready docker-registry 180 - ct_os_wait_rc_ready router 30 - oc login -u developer -p developer - OS_CLUSTER_STARTED_BY_TEST=1 - # let openshift cluster to sync to avoid some race condition errors - sleep 3 -} - -# ct_os_cluster_down -# -------------------- -# Shuts down the local OpenShift cluster using 'oc cluster down' -function ct_os_cluster_down() { - if [ ${OS_CLUSTER_STARTED_BY_TEST:-0} -eq 1 ] ; then - echo "Cluster started by the test, shutting down." - oc cluster down - else - echo "Cluster not started by the test, shutting down skipped." - fi -} - -# ct_os_cluster_running -# -------------------- -# Returns 0 if oc cluster is running -function ct_os_cluster_running() { - oc cluster status &>/dev/null -} - -# ct_os_logged_in -# --------------- -# Returns 0 if logged in to a cluster (remote or local) -function ct_os_logged_in() { - oc whoami >/dev/null -} - -# ct_os_set_path_oc OC_VERSION -# -------------------- -# This is a trick that helps using correct version of the `oc`: -# The input is version of the openshift in format v3.6.0 etc. -# If the currently available version of oc is not of this version, -# it first takes a look into /usr/local/oc-/bin directory, -# and if not found there it downloads the community release from github. -# In the end the PATH variable is changed, so the other tests can still use just 'oc'. -# Arguments: oc_version - X.Y part of the version of OSE (e.g. 3.9) -function ct_os_set_path_oc() { - local oc_version - local oc_path - - oc_version=$(ct_os_get_latest_ver "$1") - - if oc version | grep -q "oc ${oc_version%.*}." ; then - echo "Binary oc found already available in version ${oc_version}: $(command -v oc) Doing noting." - return 0 - fi - - # first check whether we already have oc available in /usr/local - local installed_oc_path="/usr/local/oc-${oc_version%.*}/bin" - - if [ -x "${installed_oc_path}/oc" ] ; then - oc_path="${installed_oc_path}" - echo "Binary oc found in ${installed_oc_path}" >&2 - else - # oc not available in /usr/local, try to download it from github (community release) - oc_path="/tmp/oc-${oc_version}-bin" - ct_os_download_upstream_oc "${oc_version}" "${oc_path}" - fi - if [ -z "${oc_path}" ] ; then - echo "ERROR: oc not found installed, nor downloaded" >&1 - return 1 - fi - export PATH="${oc_path}:${PATH}" - if ! oc version | grep -q "oc ${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_get_latest_ver VERSION_PART_X -# -------------------- -# Returns full version (vX.Y.Z) from part of the version (X.Y) -# Arguments: vxy - X.Y part of the version -# Returns vX.Y.Z variant of the version -function ct_os_get_latest_ver(){ - local vxy="v$1" - for vz in {3..0} ; do - curl -sif "https://github.com/openshift/origin/releases/tag/${vxy}.${vz}" >/dev/null && echo "${vxy}.${vz}" && return 0 - done - echo "ERROR: version ${vxy} not found in https://github.com/openshift/origin/tags" >&2 - return 1 -} - -# ct_os_download_upstream_oc OC_VERSION OUTPUT_DIR -# -------------------- -# Downloads a particular version of openshift-origin-client-tools from -# github into specified output directory -# Arguments: oc_version - version of OSE (e.g. v3.7.2) -# Arguments: output_dir - output directory -function ct_os_download_upstream_oc() { - local oc_version=$1 - local output_dir=$2 - - # check whether we already have the binary in place - [ -x "${output_dir}/oc" ] && return 0 - - mkdir -p "${output_dir}" - # using html output instead of https://api.github.com/repos/openshift/origin/releases/tags/${oc_version}, - # because API is limited for number of queries if not authenticated - tarball=$(curl -si "https://github.com/openshift/origin/releases/tag/${oc_version}" | grep -o -e "openshift-origin-client-tools-${oc_version}-[a-f0-9]*-linux-64bit.tar.gz" | head -n 1) - - # download, unpack the binaries and then put them into output directory - echo "Downloading https://github.com/openshift/origin/releases/download/${oc_version}/${tarball} into ${output_dir}/" >&2 - curl -sL https://github.com/openshift/origin/releases/download/"${oc_version}"/"${tarball}" | tar -C "${output_dir}" -xz - mv -f "${output_dir}"/"${tarball%.tar.gz}"/* "${output_dir}/" - - rmdir "${output_dir}"/"${tarball%.tar.gz}" -} - - -# ct_os_test_s2i_app_func IMAGE APP CONTEXT_DIR CHECK_CMD [OC_ARGS] -# -------------------- -# Runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. Then check the container by arbitrary -# function given as argument (such an argument may include string, -# that will be replaced with actual IP). -# Arguments: image - prefix or whole ID of the pod to run the cmd in (compulsory) -# Arguments: app - url or local path to git repo with the application sources (compulsory) -# Arguments: context_dir - sub-directory inside the repository with the application sources (compulsory) -# Arguments: check_command - CMD line that checks whether the container works (compulsory; '' will be replaced with actual IP) -# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` -# command, typically environment variables (optional) -function ct_os_test_s2i_app_func() { - local image_name=${1} - local app=${2} - local context_dir=${3} - local check_command=${4} - local oc_args=${5:-} - local image_name_no_namespace=${image_name##*/} - local service_name="${image_name_no_namespace%%:*}-testing" - local namespace - - if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then - echo "ERROR: ct_os_test_s2i_app_func() requires at least 4 arguments that cannot be emtpy." >&2 - return 1 - fi - - # shellcheck disable=SC2119 - ct_os_new_project - - namespace=${CT_NAMESPACE:-"$(oc project -q)"} - 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}" - 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_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_tagged}" --insecure=true --reference-policy=local - 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}" - fi - fi - else - echo "Testing image ${image_name} in CVP pipeline." - fi - - local app_param="${app}" - if [ -d "${app}" ] ; then - # for local directory, we need to copy the content, otherwise too smart os command - # pulls the git remote repository instead - app_param=$(ct_obtain_input "${app}") - fi - - # shellcheck disable=SC2086 - ct_os_deploy_s2i_image "${image_tagged}" "${app_param}" \ - --context-dir="${context_dir}" \ - --name "${service_name}" \ - ${oc_args} - - if [ -d "${app}" ] ; then - # in order to avoid weird race seen sometimes, let's wait shortly - # before starting the build explicitly - sleep 5 - oc start-build "${service_name}" --from-dir="${app_param}" - fi - - ct_os_wait_pod_ready "${service_name}" 300 - - local ip - local check_command_exp - local image_id - - # get image ID from the deployment config - image_id=$(oc get "deploymentconfig.apps.openshift.io/${service_name}" -o custom-columns=IMAGE:.spec.template.spec.containers[*].image | tail -n 1) - - ip=$(ct_os_get_service_ip "${service_name}") - # shellcheck disable=SC2001 - check_command_exp=$(echo "$check_command" | sed -e "s//$ip/g" -e "s||${image_id}|g") - - echo " Checking APP using $check_command_exp ..." - local result=0 - eval "$check_command_exp" || result=1 - - ct_os_service_image_info "${service_name}" - - if [ $result -eq 0 ] ; then - echo " Check passed." - else - echo " Check failed." - fi - - # shellcheck disable=SC2119 - ct_os_delete_project - return $result -} - -# ct_os_test_s2i_app IMAGE APP CONTEXT_DIR EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ] -# -------------------- -# Runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. Then check the http response. -# Arguments: image - prefix or whole ID of the pod to run the cmd in (compulsory) -# Arguments: app - url or local path to git repo with the application sources (compulsory) -# Arguments: context_dir - sub-directory inside the repository with the application sources (compulsory) -# Arguments: expected_output - PCRE regular expression that must match the response body (compulsory) -# Arguments: port - which port to use (optional; default: 8080) -# Arguments: protocol - which protocol to use (optional; default: http) -# Arguments: response_code - what http response code to expect (optional; default: 200) -# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` -# command, typically environment variables (optional) -function ct_os_test_s2i_app() { - local image_name=${1} - local app=${2} - local context_dir=${3} - local expected_output=${4} - local port=${5:-8080} - local protocol=${6:-http} - local response_code=${7:-200} - local oc_args=${8:-} - - if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then - echo "ERROR: ct_os_test_s2i_app() requires at least 4 arguments that cannot be emtpy." >&2 - return 1 - fi - - ct_os_test_s2i_app_func "${image_name}" \ - "${app}" \ - "${context_dir}" \ - "ct_os_test_response_internal '${protocol}://:${port}' '${response_code}' '${expected_output}'" \ - "${oc_args}" -} - -# ct_os_test_template_app_func IMAGE APP IMAGE_IN_TEMPLATE CHECK_CMD [OC_ARGS] -# -------------------- -# Runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. Then check the container by arbitrary -# function given as argument (such an argument may include string, -# that will be replaced with actual IP). -# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory) -# Arguments: template - url or local path to a template to use (compulsory) -# Arguments: name_in_template - image name used in the template -# Arguments: check_command - CMD line that checks whether the container works (compulsory; '' will be replaced with actual IP) -# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` -# command, typically environment variables (optional) -# Arguments: other_images - some templates need other image to be pushed into the OpenShift registry, -# specify them in this parameter as "|", where "" is a full image name -# (including registry if needed) and "" is a tag under which the image should be available -# in the OpenShift registry. -function ct_os_test_template_app_func() { - local image_name=${1} - local template=${2} - local name_in_template=${3} - local check_command=${4} - local oc_args=${5:-} - local other_images=${6:-} - - if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then - echo "ERROR: ct_os_test_template_app_func() requires at least 4 arguments that cannot be emtpy." >&2 - return 1 - fi - - local service_name="${name_in_template}-testing" - local image_tagged="${name_in_template}:${VERSION}" - local namespace - - # shellcheck disable=SC2119 - ct_os_new_project - - namespace=${CT_NAMESPACE:-"$(oc project -q)"} - # 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}" - else - if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then - echo "Importing image ${image_name} as ${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_tagged}" --insecure=true --reference-policy=local - 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}" - fi - fi - else - echo "Import is already done by CVP pipeline." - fi - # Other images are not uploaded by CVP pipeline. We need to do it. - 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 - for i_t in ${other_images} ; do - echo "${i_t}" - IFS='|' read -ra image_tag_a <<< "${i_t}" - if [[ "$(docker images -q "$image_name" 2>/dev/null)" == "" ]]; then - echo "ERROR: Image $image_name is not pulled yet." - docker images - echo "Add to the beginning of scripts run-openshift-remote-cluster and run-openshift row" - echo "'ct_pull_image $image_name true'." - exit 1 - fi - - 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 - - # get the template file from remote or local location; if not found, it is - # considered an internal template name, like 'mysql', so use the name - # explicitly - 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}" \ - -p NAMESPACE="${namespace}" \ - ${oc_args} - - ct_os_wait_pod_ready "${service_name}" 300 - - local ip - local check_command_exp - local image_id - - # get image ID from the deployment config - image_id=$(oc get "deploymentconfig.apps.openshift.io/${service_name}" -o custom-columns=IMAGE:.spec.template.spec.containers[*].image | tail -n 1) - - ip=$(ct_os_get_service_ip "${service_name}") - # shellcheck disable=SC2001 - check_command_exp=$(echo "$check_command" | sed -e "s//$ip/g" -e "s||${image_id}|g") - - echo " Checking APP using $check_command_exp ..." - local result=0 - eval "$check_command_exp" || result=1 - - ct_os_service_image_info "${service_name}" - - if [ $result -eq 0 ] ; then - echo " Check passed." - else - echo " Check failed." - fi - - # shellcheck disable=SC2119 - ct_os_delete_project - return $result -} - -# params: -# ct_os_test_template_app IMAGE APP IMAGE_IN_TEMPLATE EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ] -# -------------------- -# Runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. Then check the http response. -# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory) -# Arguments: template - url or local path to a template to use (compulsory) -# Arguments: name_in_template - image name used in the template -# Arguments: expected_output - PCRE regular expression that must match the response body (compulsory) -# Arguments: port - which port to use (optional; default: 8080) -# Arguments: protocol - which protocol to use (optional; default: http) -# Arguments: response_code - what http response code to expect (optional; default: 200) -# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app` -# command, typically environment variables (optional) -# Arguments: other_images - some templates need other image to be pushed into the OpenShift registry, -# specify them in this parameter as "|", where "" is a full image name -# (including registry if needed) and "" is a tag under which the image should be available -# in the OpenShift registry. -function ct_os_test_template_app() { - local image_name=${1} - local template=${2} - local name_in_template=${3} - local expected_output=${4} - local port=${5:-8080} - local protocol=${6:-http} - local response_code=${7:-200} - local oc_args=${8:-} - local other_images=${9:-} - - if [ $# -lt 4 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ] || [ -z "${4}" ]; then - echo "ERROR: ct_os_test_template_app() requires at least 4 arguments that cannot be emtpy." >&2 - return 1 - fi - - ct_os_test_template_app_func "${image_name}" \ - "${template}" \ - "${name_in_template}" \ - "ct_os_test_response_internal '${protocol}://:${port}' '${response_code}' '${expected_output}'" \ - "${oc_args}" \ - "${other_images}" -} - -# ct_os_test_image_update IMAGE_NAME OLD_IMAGE ISTAG CHECK_FUNCTION OC_ARGS -# -------------------- -# Runs an image update test with [image] uploaded to [is] imagestream -# and checks the services using an arbitrary function provided in [check_function]. -# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory) -# Arguments: old_image - valid name of the image from the registry -# Arguments: istag - imagestream to upload the images into (compulsory) -# Arguments: check_function - command to be run to check functionality of created services (compulsory) -# Arguments: oc_args - arguments to use during oc new-app (compulsory) -ct_os_test_image_update() { - local image_name=$1; shift - local old_image=$1; shift - local istag=$1; shift - local check_function=$1; shift - local service_name=${image_name##*/} - local ip="" check_command_exp="" - - echo "Running image update test for: $image_name" - # shellcheck disable=SC2119 - ct_os_new_project - - # Get current image from repository and create an imagestream - docker pull "$old_image:latest" 2>/dev/null - ct_os_upload_image "$old_image" "$istag" - - # Setup example application with curent image - oc new-app "$@" --name "$service_name" - ct_os_wait_pod_ready "$service_name" 60 - - # Check application output - ip=$(ct_os_get_service_ip "$service_name") - check_command_exp=${check_function///$ip} - 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_wait_pod_ready "${service_name}-2" 60 - - # Check application output - ip=$(ct_os_get_service_ip "$service_name") - check_command_exp=${check_function///$ip} - ct_assert_cmd_success "$check_command_exp" - - # shellcheck disable=SC2119 - ct_os_delete_project -} - -# ct_os_deploy_cmd_image IMAGE_NAME -# -------------------- -# Runs a special command pod, a pod that does nothing, but includes utilities for testing. -# A typical usage is a mysql pod that includes mysql commandline, that we need for testing. -# Running commands inside this command pod is done via ct_os_cmd_image_run function. -# The pod is not run again if already running. -# Arguments: image_name - image to be used as a command pod -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 ${image_name}" - oc create -f - <" - local sleep_time=3 - local attempt=1 - local result=1 - local status - local response_code - local response_file - local util_image_name='registry.access.redhat.com/ubi7/ubi' - - response_file=$(mktemp /tmp/ct_test_response_XXXXXX) - ct_os_deploy_cmd_image "${util_image_name}" - - while [ "${attempt}" -le "${max_attempts}" ]; do - ct_os_cmd_image_run "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_os_get_image_from_pod -# ------------------------ -# Print image identifier from an existing pod to stdout -# Argument: pod_prefix - prefix or full name of the pod to get image from -ct_os_get_image_from_pod() { - local pod_prefix=$1 ; shift - local pod_name - pod_name=$(ct_os_get_pod_name "$pod_prefix") - oc get "po/${pod_name}" -o yaml | sed -ne 's/^\s*image:\s*\(.*\)\s*$/\1/ p' | head -1 -} - -# ct_os_check_cmd_internal -# ---------------- -# Runs a specified command, checks exit code and compares the output with expected regexp. -# That all is done inside an image in the cluster, so the function is used -# typically in clusters that are not accessible outside. -# The check is repeated until timeout. -# Argument: util_image_name - name of the image in the cluster that is used for running the cmd -# Argument: service_name - kubernetes' service name to work with (IP address is taken from this one) -# Argument: check_command - command that is run within the util_image_name container -# Argument: expected_content_match - regexp that must be in the output (use .* to ignore check) -# Argument: timeout - number of seconds to wait till the check succeeds -function ct_os_check_cmd_internal() { - local util_image_name=$1 ; shift - local service_name=$1 ; shift - local check_command=$1 ; shift - local expected_content_match=${1:-.*} ; shift - local timeout=${1:-60} ; shift || : - - : " Service ${service_name} check ..." - - local output - local ret - local ip - local check_command_exp - - ip=$(ct_os_get_service_ip "${service_name}") - # shellcheck disable=SC2001 - check_command_exp=$(echo "$check_command" | sed -e "s//$ip/g") - - ct_os_deploy_cmd_image "${util_image_name}" - SECONDS=0 - - echo -n "Waiting for ${service_name} service becoming ready ..." - while true ; do - output=$(ct_os_cmd_image_run "$check_command_exp") - ret=$? - echo "${output}" | grep -qe "${expected_content_match}" || ret=1 - if [ ${ret} -eq 0 ] ; then - echo " PASS" - return 0 - fi - echo -n "." - [ ${SECONDS} -gt "${timeout}" ] && break - sleep 3 - done - echo " FAIL" - return 1 -} - -# ct_os_test_image_stream_template IMAGE_STREAM_FILE TEMPLATE_FILE SERVICE NAME [TEMPLATE_PARAMS] -# ------------------------ -# Creates an image stream and deploys a specified template. Then checks that a pod runs. -# Argument: image_stream_file - local or remote file with the image stream definition -# Argument: template_file - local file name with a template -# Argument: service_name - how the pod will be named (prefix) -# Argument: template_params (optional) - parameters for the template, like image stream version -function ct_os_test_image_stream_template() { - local image_stream_file=${1} - local template_file=${2} - local service_name=${3} - local template_params=${4:-} - local local_image_stream_file - local local_template_file - - if [ $# -lt 3 ] || [ -z "${1}" ] || [ -z "${2}" ] || [ -z "${3}" ]; then - echo "ERROR: ct_os_test_image_stream() requires at least 3 arguments that cannot be empty." >&2 - return 1 - fi - - echo "Running image stream test for stream ${image_stream_file} and template ${template_file}" - # shellcheck disable=SC2119 - ct_os_new_project - - local_image_stream_file=$(ct_obtain_input "${image_stream_file}") - local_template_file=$(ct_obtain_input "${template_file}") - oc create -f "${local_image_stream_file}" - - # shellcheck disable=SC2086 - if ! ct_os_deploy_template_image "${local_template_file}" -p NAMESPACE="${CT_NAMESPACE:-$(oc project -q)}" ${template_params} ; then - echo "ERROR: ${template_file} could not be loaded" - return 1 - # Deliberately not runnig ct_os_delete_project here because user either - # might want to investigate or the cleanup is done with the cleanup trap. - # Most functions depend on the set -e anyway at this point. - fi - ct_os_wait_pod_ready "${service_name}" 120 - - # shellcheck disable=SC2119 - ct_os_delete_project -} - -# ct_os_wait_stream_ready IMAGE_STREAM_FILE NAMESPACE [ TIMEOUT ] -# ------------------------ -# Waits max timeout seconds till a [stream] is available in the [namespace]. -# Arguments: image_stream - stream name (usuallly :) -# Arguments: namespace - namespace name -# Arguments: timeout - how many seconds to wait -function ct_os_wait_stream_ready() { - local image_stream=${1} - local namespace=${2} - local timeout=${3:-60} - # It takes some time for the first time before the image is pulled in - SECONDS=0 - echo -n "Waiting for ${namespace}/${image_stream} to become available ..." - while ! oc get -n "${namespace}" istag "${image_stream}" &>/dev/null; do - if [ "$SECONDS" -gt "${timeout}" ] ; then - echo "FAIL: ${namespace}/${image_stream} not available after ${timeout}s:" - echo "oc get -n ${namespace} istag ${image_stream}" - oc get -n "${namespace}" istag "${image_stream}" - return 1 - fi - sleep 3 - echo -n . - done - echo " DONE" -} - -# ct_os_test_image_stream_s2i IMAGE_STREAM_FILE IMAGE_NAME APP CONTEXT_DIR EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ] -# -------------------- -# Check the imagestream with an s2i app check. First it imports the given image stream, then -# it runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. Then check the http response. -# Argument: image_stream_file - local or remote file with the image stream definition -# Argument: image_name - container image we test (or name of the existing image stream in : format) -# Argument: app - url or local path to git repo with the application sources (compulsory) -# Argument: context_dir - sub-directory inside the repository with the application sources (compulsory) -# Argument: expected_output - PCRE regular expression that must match the response body (compulsory) -# Argument: port - which port to use (optional; default: 8080) -# Argument: protocol - which protocol to use (optional; default: http) -# Argument: response_code - what http response code to expect (optional; default: 200) -# Argument: oc_args - all other arguments are used as additional parameters for the `oc new-app` -# command, typically environment variables (optional) -function ct_os_test_image_stream_s2i() { - local image_stream_file=${1} - local image_name=${2} - local app=${3} - local context_dir=${4} - local expected_output=${5} - local port=${6:-8080} - local protocol=${7:-http} - local response_code=${8:-200} - local oc_args=${9:-} - local result - local local_image_stream_file - - echo "Running image stream test for stream ${image_stream_file} and application ${app} with context ${context_dir}" - - # shellcheck disable=SC2119 - ct_os_new_project - - local_image_stream_file=$(ct_obtain_input "${image_stream_file}") - oc create -f "${local_image_stream_file}" - - # ct_os_test_s2i_app creates a new project, but we already need - # it before for the image stream import, so tell it to skip this time - CT_SKIP_NEW_PROJECT=true \ - ct_os_test_s2i_app "${IMAGE_NAME}" "${app}" "${context_dir}" "${expected_output}" \ - "${port}" "${protocol}" "${response_code}" "${oc_args}" - result=$? - - # shellcheck disable=SC2119 - ct_os_delete_project - - return $result -} - -# ct_os_test_image_stream_quickstart IMAGE_STREAM_FILE TEMPLATE IMAGE_NAME NAME_IN_TEMPLATE EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, OTHER_IMAGES ] -# -------------------- -# Check the imagestream with an s2i app check. First it imports the given image stream, then -# it runs [image] and [app] in the openshift and optionally specifies env_params -# as environment variables to the image. Then check the http response. -# Argument: image_stream_file - local or remote file with the image stream definition -# Argument: template_file - local file name with a template -# Argument: image_name - container image we test (or name of the existing image stream in : format) -# Argument: name_in_template - image name used in the template -# Argument: expected_output - PCRE regular expression that must match the response body (compulsory) -# Argument: port - which port to use (optional; default: 8080) -# Argument: protocol - which protocol to use (optional; default: http) -# Argument: response_code - what http response code to expect (optional; default: 200) -# Argument: oc_args - all other arguments are used as additional parameters for the `oc new-app` -# command, typically environment variables (optional) -# Argument: other_images - some templates need other image to be pushed into the OpenShift registry, -# specify them in this parameter as "|", where "" is a full image name -# (including registry if needed) and "" is a tag under which the image should be available -# in the OpenShift registry. -function ct_os_test_image_stream_quickstart() { - local image_stream_file=${1} - local template_file=${2} - local image_name=${3} - local name_in_template=${4} - local expected_output=${5} - local port=${6:-8080} - local protocol=${7:-http} - local response_code=${8:-200} - local oc_args=${9:-} - local other_images=${10:-} - local result - local local_image_stream_file - local local_template_file - - echo "Running image stream test for stream ${image_stream_file} and quickstart template ${template_file}" - echo "Image name is ${IMAGE_NAME}" - # shellcheck disable=SC2119 - ct_os_new_project - - local_image_stream_file=$(ct_obtain_input "${image_stream_file}") - local_template_file=$(ct_obtain_input "${template_file}") - # ct_os_test_template_app creates a new project, but we already need - # it before for the image stream import, so tell it to skip this time - namespace=${CT_NAMESPACE:-"$(oc project -q)"} - - # Add namespace into openshift arguments - if [[ $oc_args != *"NAMESPACE"* ]]; then - oc_args="${oc_args} -p NAMESPACE=${namespace}" - fi - oc create -f "${local_image_stream_file}" - - # In case we are testing on OpenShift 4 export variable for mirror image - # which means, that image is going to be mirrored from an internal registry into OpenShift 4 - if [ "${CT_EXTERNAL_REGISTRY:-false}" == 'true' ]; then - export CT_TAG_IMAGE=true - fi - # ct_os_test_template_app creates a new project, but we already need - # it before for the image stream import, so tell it to skip this time - - CT_SKIP_NEW_PROJECT=true \ - ct_os_test_template_app "${image_name}" \ - "${local_template_file}" \ - "${name_in_template}" \ - "${expected_output}" \ - "${port}" "${protocol}" "${response_code}" "${oc_args}" "${other_images}" - - result=$? - - # shellcheck disable=SC2119 - ct_os_delete_project - - return $result -} - -# ct_os_service_image_info SERVICE_NAME -# -------------------- -# Shows information about the image used by a specified service. -# Argument: service_name - Service name (uesd for deployment config) -function ct_os_service_image_info() { - local service_name=$1 - local image_id - local namespace - - # get image ID from the deployment config - image_id=$(oc get "deploymentconfig.apps.openshift.io/${service_name}" -o custom-columns=IMAGE:.spec.template.spec.containers[*].image | tail -n 1) - namespace=${CT_NAMESPACE:-"$(oc project -q)"} - - echo " Information about the image we work with:" - oc get deploymentconfig.apps.openshift.io/"${service_name}" -o yaml | grep lastTriggeredImage - # for s2i builds, the resulting image is actually in the current namespace, - # so if the specified namespace does not succeed, try the current namespace - oc get isimage -n "${namespace}" "${image_id##*/}" -o yaml || oc get isimage "${image_id##*/}" -o yaml -} -# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/test/test-lib-redis.sh b/test/test-lib-redis.sh deleted file mode 100644 index 698abe8..0000000 --- a/test/test-lib-redis.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Functions for tests for the Redis 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 -source ${THISDIR}/test-lib-remote-openshift.sh - -function test_redis_integration() { - local image_name=$1 - local service_name=redis - ct_os_test_template_app_func "${image_name}" \ - "https://raw.githubusercontent.com/openshift/origin/master/examples/db-templates/redis-ephemeral-template.json" \ - "${service_name}" \ - "ct_os_check_cmd_internal '' '${service_name}-testing' 'timeout 15 redis-cli -h -a testp ping' 'PONG'" \ - "-p REDIS_VERSION=${VERSION} \ - -p DATABASE_SERVICE_NAME="${service_name}-testing" \ - -p REDIS_PASSWORD=testp" -} - -# Check the imagestream -function test_redis_imagestream() { - case ${OS} in - rhel7|centos7) ;; - *) echo "Imagestream testing not supported for $OS environment." ; return 0 ;; - esac - - ct_os_test_image_stream_template "${THISDIR}/../imagestreams/redis-${OS%[0-9]*}.json" "${THISDIR}/../examples/redis-ephemeral-template.json" redis "-p REDIS_VERSION=${VERSION}" -} - -# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/test/test-lib-remote-openshift.sh b/test/test-lib-remote-openshift.sh deleted file mode 100644 index bda03f6..0000000 --- a/test/test-lib-remote-openshift.sh +++ /dev/null @@ -1,116 +0,0 @@ -# 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-/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() { - if [ "${CVP:-0}" -eq "1" ]; then - echo "Testing in CVP environment. No need to login to OpenShift cluster. This is already done by CVP pipeline." - return - fi - 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 deleted file mode 100644 index 42f22ba..0000000 --- a/test/test-lib.sh +++ /dev/null @@ -1,1072 +0,0 @@ -# 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=_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_pull_image -# ------------- -# Function pull an image before tests execution -# Argument: image_name - string containing the public name of the image to pull -# Argument: exit - in case "true" is defined and pull failed, then script has to exit with 1 and no tests are executed -# Argument: loops - how many times to pull image in case of failure -# Function returns either 0 in case of pull was successful -# Or the test suite exit with 1 in case of pull error -function ct_pull_image() { - local image_name="$1"; shift - local exit=${1:-"false"}; shift - local loops=${1:-10}; shift - local loop=0 - - # Let's try to pull image. - echo "-> Pulling image $image_name ..." - # Sometimes in Fedora case it fails with HTTP 50X - # Check if the image is available locally and try to pull it if it is not - if [[ "$(docker images -q "$image_name" 2>/dev/null)" != "" ]]; then - echo "The image $image_name is already pulled." - return 0 - fi - - # Try pulling the image to see if it is accessible - # WORKAROUND: Since Fedora registry sometimes fails randomly, let's try it more times - while ! docker pull "$image_name"; do - ((loop++)) || : - echo "Pulling image $image_name failed." - if [ "$loop" -gt "$loops" ]; then - echo "Pulling of image $image_name failed $loops times in a row. Giving up." - echo "!!! ERROR with pulling image $image_name !!!!" - # shellcheck disable=SC2268 - if [[ x"$exit" == x"false" ]]; then - return 1 - else - exit 1 - fi - fi - echo "Let's wait $((loop*5)) seconds and try again." - sleep "$((loop*5))" - done -} - - -# 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,SC2153 - 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 - cat "${tmpdir}/jquery" - 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 <"$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: -# /-cert-selfsigned.pem -- public PEM cert -# /-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 [ "$os" == "rhel7" ]; then - 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" == "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 - 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 - if ! user_id=$(ct_get_uid_from_image "$user" "$src_image"); then - echo "Terminating s2i build." - return 1 - 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 <"$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 - read -ra mount_options <<< "$(echo "$s2i_args" | grep -o -e '\(-v\)[[:space:]]\.*\S*' || true)" - - # Run the build and tag the result - docker build ${mount_options[@]+"${mount_options[@]}"} -f "$df_name" --no-cache=true -t "$dst_image" . - ) -} - -# ct_s2i_multistage_build APP_PATH SRC_IMAGE DST_IMAGE SEC_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 process -# Argument: SEC_IMAGE - image to be used as the base for the result of the build process -# 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 environment variable definitions. -ct_s2i_multistage_build() { - - local app_path=$1; shift - local src_image=$1; shift - local sec_image=$1; shift - local dst_image=$1; shift - local s2i_args=$*; - local local_app="app-src" - local user_id= - 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 - - 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 - if ! user_id=$(ct_get_uid_from_image "$user" "$src_image"); then - echo "Terminating s2i build." - return 1 - fi - - # Use /tmp to not pollute cwd - tmpdir=$(mktemp -d) - df_name=$(mktemp -p "$tmpdir" Dockerfile.XXXX) - cd "$tmpdir" - - # If the path exists on the local host, copy it into the directory for the build - # Otherwise handle it as a link to a git repository - if [ -e "${app_path/file:\/\//}/." ] ; then - mkdir -p "$local_app" - # Strip file:// from APP_PATH and copy its contents into current context - cp -r "${app_path/file:\/\//}/." "$local_app" - - else - ct_clone_git_repository "$app_path" "$local_app" - fi - - cat <"$df_name" -# First stage builds the application -FROM $src_image as builder -# Add application sources to a directory that the assemble script expects them -# and set permissions so that the container runs without root access -USER 0 -ADD app-src /tmp/src -RUN chown -R 1001:0 /tmp/src -$(echo "$s2i_args" | grep -o -e '\(-e\|--env\)[[:space:]=]\S*=\S*' | sed -e 's/-e /ENV /' -e 's/--env[ =]/ENV /') -# 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") -USER $user_id -# Install the dependencies -RUN /usr/libexec/s2i/assemble -# Second stage copies the application to the minimal image -FROM $sec_image -# Copy the application source and build artifacts from the builder image to this one -COPY --from=builder \$HOME \$HOME -# Set the default command for the resulting image -CMD /usr/libexec/s2i/run -EOF - - # Check if -v parameter is present in s2i_args and add it into docker build command - read -ra mount_options <<< "$(echo "$s2i_args" | grep -o -e '\(-v\)[[:space:]]\.*\S*' || true)" - - docker build ${mount_options[@]+"${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 ! ct_pull_image "$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_clone_git_repository -# ----------------------------- -# Argument: app_url - git URI pointing to a repository, supports "@" to indicate a different branch -# Argument: app_dir (optional) - name of the directory to clone the repository into -ct_clone_git_repository() -{ - local app_url=$1; shift - local app_dir=$1 - - # 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}." - return 1 - fi -} - -# ct_get_uid_from_image -# ----------------------------- -# Argument: user - user to get uid for inside the image -# Argument: src_image - image to use for user information -ct_get_uid_from_image() -{ - local user=$1; shift - local src_image=$1 - local user_id= - - # 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." - return 1 - else - echo "${user_id:-$user}" - fi -} - -# 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 or local 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 [ -d "$app_url" ] ; then - echo "Copying local folder: $app_url -> $app_dir." - cp -Lr "$app_url" "$app_dir" - else - if ! ct_clone_git_repository "$app_url" "$app_dir" ; then - echo "Terminating the Dockerfile build." - return 1 - fi - 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: