From a70052ab01198f85b36ef3498aab98399c7de812 Mon Sep 17 00:00:00 2001 From: Nikola Davidova Date: Tue, 9 Jun 2026 16:35:51 +0200 Subject: [PATCH] Onboard valkey-container to distgen-generated sources Add distgen templates (src/Dockerfile, src/Dockerfile.fedora), multispec configuration, manifest with COPY_RULES for root/ files, and override the generate target in the Makefile. Remove unsupported version 7. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/container-tests.yml | 4 +- 7/.exclude-c10s | 0 7/.exclude-fedora | 0 7/.exclude-rhel10 | 0 7/Dockerfile.c10s | 89 - 7/README.md | 88 - 7/test/check_imagestreams.py | 105 -- .../examples/valkey-ephemeral-template.json | 199 --- .../examples/valkey-persistent-template.json | 223 --- 7/test/imagestreams/imagestreams.yaml | 15 - 7/test/imagestreams/valkey-centos.json | 50 - 7/test/run | 308 ---- 7/test/run-openshift-pytest | 13 - 7/test/run-openshift-remote-cluster | 39 - 7/test/show_all_imagestreams.py | 58 - 7/test/test-lib-openshift.sh | 1206 ------------- 7/test/test-lib-remote-openshift.sh | 136 -- 7/test/test-lib-valkey.sh | 48 - 7/test/test-lib.sh | 1509 ----------------- 7/test/test-openshift.yaml | 77 - 7/test/test_valkey_imagestream.py | 48 - 7/test/test_valkey_imagestream_template.py | 45 - 7/test/test_valkey_latest_imagestreams.py | 26 - 7/test/test_valkey_template.py | 53 - 7/test/valkey-persistent-template.json | 223 --- 8/Dockerfile.c10s | 4 +- 8/Dockerfile.fedora | 4 +- 8/Dockerfile.rhel10 | 5 +- 8/Dockerfile.rhel9 | 4 +- 8/examples | 1 + 8/imagestreams | 1 + 8/test | 2 +- Makefile | 7 +- README.md | 1 - manifest.yml | 76 + specs/multispec.yml | 61 + 7/Dockerfile.rhel10 => src/Dockerfile | 16 +- {7 => src}/Dockerfile.fedora | 28 +- {7 => src}/root/usr/bin/container-entrypoint | 0 {7 => src}/root/usr/bin/run-valkey | 0 {7 => src}/root/usr/bin/usage | 0 {7 => src}/root/usr/libexec/container-setup | 0 .../share/container-scripts/valkey/README.md | 12 +- .../valkey/base.conf.template | 0 .../share/container-scripts/valkey/common.sh | 0 .../share/container-scripts/valkey/helpers.sh | 0 .../valkey/password.conf.template | 0 .../container-scripts/valkey/post-init.sh | 0 .../valkey/validate-variables.sh | 0 49 files changed, 184 insertions(+), 4600 deletions(-) delete mode 100644 7/.exclude-c10s delete mode 100644 7/.exclude-fedora delete mode 100644 7/.exclude-rhel10 delete mode 100644 7/Dockerfile.c10s delete mode 100644 7/README.md delete mode 100755 7/test/check_imagestreams.py delete mode 100644 7/test/examples/valkey-ephemeral-template.json delete mode 100644 7/test/examples/valkey-persistent-template.json delete mode 100644 7/test/imagestreams/imagestreams.yaml delete mode 100644 7/test/imagestreams/valkey-centos.json delete mode 100755 7/test/run delete mode 100755 7/test/run-openshift-pytest delete mode 100755 7/test/run-openshift-remote-cluster delete mode 100755 7/test/show_all_imagestreams.py delete mode 100644 7/test/test-lib-openshift.sh delete mode 100644 7/test/test-lib-remote-openshift.sh delete mode 100644 7/test/test-lib-valkey.sh delete mode 100644 7/test/test-lib.sh delete mode 100644 7/test/test-openshift.yaml delete mode 100644 7/test/test_valkey_imagestream.py delete mode 100644 7/test/test_valkey_imagestream_template.py delete mode 100644 7/test/test_valkey_latest_imagestreams.py delete mode 100644 7/test/test_valkey_template.py delete mode 100644 7/test/valkey-persistent-template.json create mode 120000 8/examples create mode 120000 8/imagestreams create mode 100644 manifest.yml create mode 100644 specs/multispec.yml rename 7/Dockerfile.rhel10 => src/Dockerfile (88%) rename {7 => src}/Dockerfile.fedora (82%) rename {7 => src}/root/usr/bin/container-entrypoint (100%) rename {7 => src}/root/usr/bin/run-valkey (100%) rename {7 => src}/root/usr/bin/usage (100%) rename {7 => src}/root/usr/libexec/container-setup (100%) rename {7 => src}/root/usr/share/container-scripts/valkey/README.md (88%) rename {7 => src}/root/usr/share/container-scripts/valkey/base.conf.template (100%) rename {7 => src}/root/usr/share/container-scripts/valkey/common.sh (100%) rename {7 => src}/root/usr/share/container-scripts/valkey/helpers.sh (100%) rename {7 => src}/root/usr/share/container-scripts/valkey/password.conf.template (100%) rename {7 => src}/root/usr/share/container-scripts/valkey/post-init.sh (100%) rename {7 => src}/root/usr/share/container-scripts/valkey/validate-variables.sh (100%) diff --git a/.github/workflows/container-tests.yml b/.github/workflows/container-tests.yml index da67d6b..52282f4 100644 --- a/.github/workflows/container-tests.yml +++ b/.github/workflows/container-tests.yml @@ -10,6 +10,6 @@ jobs: uses: "sclorg/ci-actions/.github/workflows/container-tests.yml@main" with: enabled-tests: '["container","container-pytest","openshift-pytest"]' - versions: '[ "7", "8" ]' - openshift-versions: '[ "7", "8" ]' + versions: '[ "8" ]' + openshift-versions: '[ "8" ]' secrets: inherit diff --git a/7/.exclude-c10s b/7/.exclude-c10s deleted file mode 100644 index e69de29..0000000 diff --git a/7/.exclude-fedora b/7/.exclude-fedora deleted file mode 100644 index e69de29..0000000 diff --git a/7/.exclude-rhel10 b/7/.exclude-rhel10 deleted file mode 100644 index e69de29..0000000 diff --git a/7/Dockerfile.c10s b/7/Dockerfile.c10s deleted file mode 100644 index f591ef3..0000000 --- a/7/Dockerfile.c10s +++ /dev/null @@ -1,89 +0,0 @@ -FROM quay.io/sclorg/s2i-core-c10s:c10s - -# Valkey image based on Software Collections packages -# -# Volumes: -# * /var/lib/valkey/data - Datastore for Valkey -# Environment: -# * $VALKEY_PASSWORD - Database password - -ENV VALKEY_VERSION=7 \ - HOME=/var/lib/valkey \ - NAME=valkey - -ENV SUMMARY="Valkey in-memory data structure store, used as database, cache and message broker" \ - DESCRIPTION="Valkey $VALKEY_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, Valkey 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="$DESCRIPTION" \ - io.k8s.display-name="Valkey $VALKEY_VERSION" \ - io.openshift.expose-services="6379:valkey" \ - io.openshift.tags="database,valkey,valkey,valkey-$VALKEY_VERSION" \ - com.redhat.component="valkey-$VALKEY_VERSION-container" \ - name="sclorg/valkey-$VALKEY_VERSION-c10s" \ - version="$VALKEY_VERSION" \ - com.redhat.license_terms="https://www.redhat.com/en/about/red-hat-end-user-license-agreements#rhel" \ - usage="podman run -d --name valkey_database -p 6379:6379 quay.io/sclorg/valkey-$VALKEY_VERSION-c10s" \ - maintainer="SoftwareCollections.org " - -EXPOSE 6379 - -# Get prefix path and path to scripts rather than hard-code them in scripts -ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/valkey \ - VALKEY_PREFIX=/usr \ - VALKEY_CONF=/etc/valkey/valkey.conf \ - VALKEY_SOCK=/run/valkey/valkey.sock \ - VALKEY_LIB=/var/lib/valkey \ - VALKEY_RUN=/run/valkey - - -# Create user for Valkey 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 Valkey 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 valkey &> /dev/null || groupadd -r valkey &> /dev/null && \ - usermod -l valkey -aG valkey -c 'Valkey Server' default &> /dev/null && \ -# Install gettext for envsubst command - INSTALL_PKGS="policycoreutils gettext bind valkey" && \ - dnf install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ - rpm -V $INSTALL_PKGS && \ - dnf -y clean all --enablerepo='*' && \ - valkey-server --version | grep -qe "^Server v=$VALKEY_VERSION\." && echo "Found VERSION $VALKEY_VERSION" && \ - mkdir -p $VALKEY_LIB/data && chown -R valkey:0 $VALKEY_LIB && \ - mkdir -p $VALKEY_RUN && chown -R valkey:0 $VALKEY_RUN && \ - chmod -R ug+rwX $VALKEY_RUN && \ - [[ "$(id valkey)" == "uid=1001(valkey)"* ]] - -# Get prefix path and path to scripts rather than hard-code them in scripts -ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/valkey \ - VALKEY_PREFIX=/usr \ - VALKEY_CONF=/etc/valkey/valkey.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/valkey/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-valkey"] diff --git a/7/README.md b/7/README.md deleted file mode 100644 index a36b886..0000000 --- a/7/README.md +++ /dev/null @@ -1,88 +0,0 @@ -Valkey 7 in-memory data structure store container image -====================================================== - -This container image includes Valkey 7 in-memory data structure store for OpenShift and general usage. -Users can choose between RHEL, CentOS Stream, and Fedora based images. -The RHEL images are available in the [Red Hat Container Catalog](https://access.redhat.com/containers/), -the CentOS Stream images are available on [Quay.io](https://quay.io/organization/sclorg), -and the Fedora images are available in [Fedora Registry](https://quay.io/organization/fedora). -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 ------------ - -Valkey 7 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, Valkey 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/valkey-7` 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 valkey_database -p 6379:6379 rhel8/valkey-7 -``` - -This will create a container named `valkey_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/valkey/data:Z` argument. This will be the Valkey data directory. - -For protecting Valkey data by a password, pass `VALKEY_PASSWORD` environment variable -to the container like this: - -``` -$ podman run -d --name valkey_database -e VALKEY_PASSWORD=strongpassword rhel8/valkey-7 -``` - -**Warning: since Valkey 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 ----------------------------------- - -**`VALKEY_PASSWORD`** - Password for the server access - -**`BIND_ADDRESS`** - IP address for the server to listen on - - -You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. - -**`/var/lib/valkey/data`** - Valkey 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 ---------------- -Valkey 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/valkey-container. -In that repository you also can find another versions of Python environment Dockerfiles. -Dockerfile for CentOS Stream 10 it's `Dockerfile.c10s`, -Dockerfile for RHEL10 it's `Dockerfile.rhel10`,and the Fedora Dockerfile is called Dockerfile.fedora. diff --git a/7/test/check_imagestreams.py b/7/test/check_imagestreams.py deleted file mode 100755 index 6ddaef3..0000000 --- a/7/test/check_imagestreams.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/env python3 - -# MIT License -# -# Copyright (c) 2018-2019 Red Hat, Inc. - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import json -import logging -import os - -from pathlib import Path -from typing import Dict, List, Any - -IMAGESTREAMS_DIR: str = "imagestreams" - - -class ImageStreamChecker(object): - version: str = "" - - def __init__(self, version: str): - self.version = version - self.results: Dict[Any, Any] = {} - - def load_json_file(self, filename: Path) -> Any: - with open(str(filename)) as f: - data = json.load(f) - isinstance(data, Dict) - return data - - def check_version(self, json_dict: Dict[Any, Any]) -> List[str]: - res = [] - for tags in json_dict["spec"]["tags"]: - print( - f"check_version: Compare tags['name']:'{tags['name']}' against version:'{self.version}'" - ) - # The name can be"" or "-elX" or "-ubiX" - if tags["name"] == self.version or tags["name"].startswith( - self.version + "-" - ): - res.append(tags) - return res - - def check_latest_tag(self, json_dict: Dict[Any, Any]) -> bool: - latest_tag_correct: bool = False - for tags in json_dict["spec"]["tags"]: - if tags["name"] != "latest": - continue - print( - f"check_latest_tag: Compare tags['name']:'{tags['name']}' against version:'{self.version}'" - ) - # The latest can link to either "" or "-elX" or "-ubiX" - if tags["from"]["name"] == self.version or tags["from"]["name"].startswith( - self.version + "-" - ): - latest_tag_correct = True - print(f"Latest tag found.") - return latest_tag_correct - - def check_imagestreams(self) -> int: - p = Path(".") - json_files = p.glob(f"{IMAGESTREAMS_DIR}/*.json") - if not json_files: - print(f"No json files present in {IMAGESTREAMS_DIR}.") - return 0 - for f in json_files: - print(f"Checking file {str(f)}.") - json_dict = self.load_json_file(f) - if not (self.check_version(json_dict) and self.check_latest_tag(json_dict)): - print( - f"The latest version is not present in {str(f)} or in latest tag." - ) - self.results[f] = False - if self.results: - return 1 - print("Imagestreams contains the latest version.") - return 0 - - -if __name__ == "__main__": - if len(sys.argv) != 2: - logging.fatal("%s: %s", sys.argv[0], "VERSION as an argument was not provided") - sys.exit(1) - - print(f"Version to check is {sys.argv[1]}.") - isc = ImageStreamChecker(version=sys.argv[1]) - sys.exit(isc.check_imagestreams()) diff --git a/7/test/examples/valkey-ephemeral-template.json b/7/test/examples/valkey-ephemeral-template.json deleted file mode 100644 index 6f7f16c..0000000 --- a/7/test/examples/valkey-ephemeral-template.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "template.openshift.io/v1", - "metadata": { - "name": "valkey-ephemeral", - "annotations": { - "openshift.io/display-name": "Valkey (Ephemeral)", - "description": "Valkey in-memory data structure store, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/valkey-container/blob/master/5.\n\nWARNING: Any data stored will be lost upon pod destruction. Only use this template for testing", - "iconClass": "icon-valkey", - "tags": "database,valkey", - "openshift.io/long-description": "This template provides a standalone Valkey 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/valkey-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: ${VALKEY_PASSWORD}\n Connection URL: valkey://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/valkey-container/blob/master/5.", - "labels": { - "template": "valkey-ephemeral-template" - }, - "objects": [ - { - "kind": "Secret", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-password": "{.data['database-password']}" - } - }, - "stringData" : { - "database-password" : "${VALKEY_PASSWORD}" - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-uri": "valkey://{.spec.clusterIP}:{.spec.ports[?(.name==\"valkey\")].port}" - } - }, - "spec": { - "ports": [ - { - "name": "valkey", - "protocol": "TCP", - "port": 6379, - "targetPort": 6379, - "nodePort": 0 - } - ], - "selector": { - "name": "${DATABASE_SERVICE_NAME}" - }, - "type": "ClusterIP", - "sessionAffinity": "None" - }, - "status": { - "loadBalancer": {} - } - }, - { - "kind": "Deployment", - "apiVersion": "apps/v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.alpha.openshift.io/wait-for-ready": "true", - "image.openshift.io/triggers": "[{\"from\":{\"kind\":\"ImageStreamTag\",\"name\":\"valkey:${VALKEY_VERSION}\"},\"fieldPath\": \"spec.template.spec.containers[0].image\"}]" - } - }, - "spec": { - "strategy": { - "type": "Recreate" - }, - "replicas": 1, - "selector": { - "matchLabels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "template": { - "metadata": { - "labels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "spec": { - "containers": [ - { - "name": "valkey", - "image": " ", - "ports": [ - { - "containerPort": 6379, - "protocol": "TCP" - } - ], - "readinessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 5, - "exec": { - "command": [ "/bin/sh", "-i", "-c", "test \"$(valkey-cli -h 127.0.0.1 -a $VALKEY_PASSWORD ping)\" == \"PONG\""] - } - }, - "livenessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 30, - "tcpSocket": { - "port": 6379 - } - }, - "env": [ - { - "name": "VALKEY_PASSWORD", - "valueFrom": { - "secretKeyRef" : { - "name" : "${DATABASE_SERVICE_NAME}", - "key" : "database-password" - } - } - } - ], - "resources": { - "limits": { - "memory": "${MEMORY_LIMIT}" - } - }, - "volumeMounts": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "mountPath": "/var/lib/valkey/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": "valkey", - "required": true - }, - { - "name": "VALKEY_PASSWORD", - "displayName": "Valkey Connection Password", - "description": "Password for the Valkey connection user.", - "generate": "expression", - "from": "[a-zA-Z0-9]{16}", - "required": true - }, - { - "name": "VALKEY_VERSION", - "displayName": "Version of Valkey Image", - "description": "Version of Valkey image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", - "value": "6-el8", - "required": true - } - ] -} diff --git a/7/test/examples/valkey-persistent-template.json b/7/test/examples/valkey-persistent-template.json deleted file mode 100644 index a51b5d9..0000000 --- a/7/test/examples/valkey-persistent-template.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "kind": "Template", - "apiVersion": "template.openshift.io/v1", - "metadata": { - "name": "valkey-persistent", - "annotations": { - "openshift.io/display-name": "Valkey", - "description": "Valkey in-memory data structure store, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/valkey-container/blob/master/5.\n\nNOTE: You must have persistent volumes available in your cluster to use this template.", - "iconClass": "icon-valkey", - "tags": "database,valkey", - "openshift.io/long-description": "This template provides a standalone Valkey server. The data is stored on persistent storage.", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "openshift.io/documentation-url": "https://github.com/sclorg/valkey-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: ${VALKEY_PASSWORD}\n Connection URL: valkey://${DATABASE_SERVICE_NAME}:6379/\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/valkey-container/blob/master/5.", - "labels": { - "template": "valkey-persistent-template" - }, - "objects": [ - { - "kind": "Secret", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-password": "{.data['database-password']}" - } - }, - "stringData" : { - "database-password" : "${VALKEY_PASSWORD}" - } - }, - { - "kind": "Service", - "apiVersion": "v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.openshift.io/expose-uri": "valkey://{.spec.clusterIP}:{.spec.ports[?(.name==\"valkey\")].port}" - } - }, - "spec": { - "ports": [ - { - "name": "valkey", - "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": "Deployment", - "apiVersion": "apps/v1", - "metadata": { - "name": "${DATABASE_SERVICE_NAME}", - "annotations": { - "template.alpha.openshift.io/wait-for-ready": "true", - "image.openshift.io/triggers": "[{\"from\":{\"kind\":\"ImageStreamTag\",\"name\":\"valkey:${VALKEY_VERSION}\"},\"fieldPath\": \"spec.template.spec.containers[0].image\"}]" - } - }, - "spec": { - "strategy": { - "type": "Recreate" - }, - "replicas": 1, - "selector": { - "matchLabels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "template": { - "metadata": { - "labels": { - "name": "${DATABASE_SERVICE_NAME}" - } - }, - "spec": { - "containers": [ - { - "name": "valkey", - "image": " ", - "ports": [ - { - "containerPort": 6379, - "protocol": "TCP" - } - ], - "readinessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 5, - "exec": { - "command": [ "/bin/sh", "-i", "-c", "test \"$(valkey-cli -h 127.0.0.1 -a $VALKEY_PASSWORD ping)\" == \"PONG\""] - } - }, - "livenessProbe": { - "timeoutSeconds": 1, - "initialDelaySeconds": 30, - "tcpSocket": { - "port": 6379 - } - }, - "env": [ - { - "name": "VALKEY_PASSWORD", - "valueFrom": { - "secretKeyRef" : { - "name" : "${DATABASE_SERVICE_NAME}", - "key" : "database-password" - } - } - } - ], - "resources": { - "limits": { - "memory": "${MEMORY_LIMIT}" - } - }, - "volumeMounts": [ - { - "name": "${DATABASE_SERVICE_NAME}-data", - "mountPath": "/var/lib/valkey/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": "valkey", - "required": true - }, - { - "name": "VALKEY_PASSWORD", - "displayName": "Valkey Connection Password", - "description": "Password for the Valkey 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": "VALKEY_VERSION", - "displayName": "Version of Valkey Image", - "description": "Version of Valkey image to be used (5-el7, 5-el8, 6-el7, 6-el8, or latest).", - "value": "6-el8", - "required": true - } - ] -} diff --git a/7/test/imagestreams/imagestreams.yaml b/7/test/imagestreams/imagestreams.yaml deleted file mode 100644 index 9c3504e..0000000 --- a/7/test/imagestreams/imagestreams.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: valkey - pretty_name: Valkey - sample_repo: "" - category: database - description: >- - Provides a Valkey APP_VERSION database on DISTRO_NAME. For more information - about using this database image, including OpenShift considerations, see - https://github.com/sclorg/valkey-container/tree/master/APP_VERSION/README.md. - imagestream_files: - - filename: valkey-centos.json - latest: "7-el10" - distros: - - name: CentOS Stream 10 - app_versions: [7] diff --git a/7/test/imagestreams/valkey-centos.json b/7/test/imagestreams/valkey-centos.json deleted file mode 100644 index d12818c..0000000 --- a/7/test/imagestreams/valkey-centos.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "kind": "ImageStream", - "apiVersion": "image.openshift.io/v1", - "metadata": { - "name": "valkey", - "annotations": { - "openshift.io/display-name": "Valkey" - } - }, - "spec": { - "tags": [ - { - "name": "7-el10", - "annotations": { - "openshift.io/display-name": "Valkey 7 (CentOS Stream 10)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "description": "Provides a Valkey 7 database on CentOS Stream 10. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/valkey-container/tree/master/7/README.md.", - "iconClass": "icon-valkey", - "tags": "database,valkey", - "version": "7" - }, - "from": { - "kind": "DockerImage", - "name": "quay.io/sclorg/valkey-7-c10s:latest" - }, - "referencePolicy": { - "type": "Local" - } - }, - { - "name": "latest", - "annotations": { - "openshift.io/display-name": "Valkey 7 (Latest)", - "openshift.io/provider-display-name": "Red Hat, Inc.", - "description": "Provides a Valkey 7 database on CentOS Stream 10. For more information about using this database image, including OpenShift considerations, see https://github.com/sclorg/valkey-container/tree/master/7/README.md.\n\nWARNING: By selecting this tag, your application will automatically update to use the latest version available on OpenShift, including major version updates.\n", - "iconClass": "icon-valkey", - "tags": "database,valkey", - "version": "7" - }, - "from": { - "kind": "ImageStreamTag", - "name": "7-el10" - }, - "referencePolicy": { - "type": "Local" - } - } - ] - } -} diff --git a/7/test/run b/7/test/run deleted file mode 100755 index 383bc07..0000000 --- a/7/test/run +++ /dev/null @@ -1,308 +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 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_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 -run_bind_address_test -run_no_bind_address_test -" -THISDIR=$(dirname ${BASH_SOURCE[0]}) -source "${THISDIR}"/test-lib.sh - -function connection_works() { - local container_ip="$1"; shift - local password="$1"; shift - if [ "$(valkey_cmd "$container_ip" "$password" ping)" == "PONG" ] ; then - return 0 - fi - return 1 -} - -function valkey_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" valkey-cli -h "$container_ip" ${password:+-a "$password"} "$@" -} - -function test_connection() { - local name=$1 ; shift - local password=$1 ; shift - local ip - ip=$(ct_get_cip $name) - echo " Testing Valkey 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 $(ct_get_cid $name) - return 1 -} - -function test_valkey() { - local container_ip="$1" - local password="$2" - - echo " Testing Valkey (password='${password:-}')" - valkey_cmd "$container_ip" "$password" set a 1 >/dev/null - ct_check_testcase_result $? - valkey_cmd "$container_ip" "$password" set b 2 >/dev/null - ct_check_testcase_result $? - test "$(valkey_cmd "$container_ip" "$password" get b)" == '2' - echo " Success!" - echo -} - -function create_container() { - local name=$1 ; shift - cidfile="$CID_FILE_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 Valkey container with persistent volume and set the initial password - create_container "testpass1" -e VALKEY_PASSWORD=foo \ - -v ${tmpdir}:/var/lib/valkey/data:Z - ct_check_testcase_result $? - test_connection testpass1 foo - ct_check_testcase_result $? - docker stop $(ct_get_cid testpass1) >/dev/null - - # Create second container with changed password - create_container "testpass2" -e VALKEY_PASSWORD=bar \ - -v ${tmpdir}:/var/lib/valkey/data:Z - ct_check_testcase_result $? - test_connection testpass2 bar - ct_check_testcase_result $? - # The old password should not work anymore - container_ip="$(ct_get_cip testpass2)" - ! connection_works "$container_ip" foo - ct_check_testcase_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 $(ct_get_cid "$id") bash -c 'valkey-cli ping' -} - - -function assert_bind_address() { - local name="$1" - local run_cmd="[ -f \${VALKEY_CONF} ] && grep \"^bind 127.0.0.1\" \${VALKEY_CONF}" - - echo "Checking if bind is set to 127.0.0.1 in valkey.conf file." - docker exec $(ct_get_cid "$name") /bin/bash -c "${run_cmd}" -} - -function assert_no_bind_address() { - local name="$1" - local run_cmd="[ -f \${VALKEY_CONF} ] && ! grep \"^bind 127.0.0.1\" \${VALKEY_CONF}" - - echo "Checking if bind is not set in valkey.conf file." - docker exec $(ct_get_cid "$name") /bin/bash -c "${run_cmd}" -} - -# 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, - # valkey 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 VALKEY_PASSWORD="pass with space" "$@" - ct_check_testcase_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 $(ct_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 $(ct_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() { - ct_doc_content_old 6379 "VALKEY.*PASSWORD" volume - return $? -} - -function run_tests() { - local name=$1 ; shift - local ret - envs=${PASS:+"-e VALKEY_PASSWORD=$PASS"} - PASS=${PASS:-} - create_container $name $envs - ret=$? - ct_check_testcase_result $ret - # Only check version on rhel/centos builds - if [ "$OS" != "fedora" ]; then - echo " Testing scl usage" - test_scl_usage "$name" 'valkey-server --version' "$VERSION" - ct_check_testcase_result $? - fi - if [ "$name" == "bind_address_test" ]; then - echo " Testing bind_address test" - assert_bind_address "$name" - ct_check_testcase_result $? - # Quit test suite. valkey container will not response. bind is set to localhost - return - fi - if [ "$name" == "no_bind_address_test" ]; then - echo " Testing no bind_address test" - assert_no_bind_address "$name" - ct_check_testcase_result $? - fi - test_connection "$name" "$PASS" - ret=$? - ct_check_testcase_result $ret - echo " Testing login accesses" - local container_ip - container_ip=$(ct_get_cip $name) - assert_login_access "$container_ip" "$PASS" true - ret=$? - ct_check_testcase_result $ret - if [ -n "$PASS" ] ; then - assert_login_access "$container_ip" "${PASS}_foo" false - ct_check_testcase_result $? - fi - assert_local_access "$name" - ret=$? - ct_check_testcase_result $ret - if [ $ret -ne 0 ]; then - echo " Local access FAILED." - else - echo " Local access SUCCESS." - fi - echo - test_valkey "$container_ip" "$PASS" - ct_check_testcase_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_bind_address_test() { - DOCKER_ARGS="-e BIND_ADDRESS=127.0.0.1" run_tests bind_address_test -} - -function run_no_bind_address_test() { - run_tests no_bind_address_test -} - -ct_init - -TEST_SET=${TESTS:-$TEST_LIST} ct_run_tests_from_testset "valkey_tests" -# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/7/test/run-openshift-pytest b/7/test/run-openshift-pytest deleted file mode 100755 index acfac84..0000000 --- a/7/test/run-openshift-pytest +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# -# 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 MariaDB in format of X.Y -# OS specifies RHEL version (e.g. OS=rhel7) -# - -THISDIR=$(dirname ${BASH_SOURCE[0]}) - -git show -s - -cd "${THISDIR}" && python3.12 -m pytest -s -rA --showlocals -vv test_valkey_*.py diff --git a/7/test/run-openshift-remote-cluster b/7/test/run-openshift-remote-cluster deleted file mode 100755 index 287aa50..0000000 --- a/7/test/run-openshift-remote-cluster +++ /dev/null @@ -1,39 +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=rhel8) -# - -THISDIR=$(dirname ${BASH_SOURCE[0]}) - -source ${THISDIR}/test-lib-valkey.sh - -TEST_LIST="\ -test_valkey_integration -test_valkey_imagestream -test_latest_imagestreams -" - -trap ct_os_cleanup EXIT SIGINT - -ct_os_set_ocp4 || exit $OC_ERR - -ct_os_check_compulsory_vars || exit $OC_ERR - -ct_os_tag_image_for_cvp "valkey" - -ct_os_check_login || exit $OC_ERR - -set -u - -# For testing on OpenShift 4 we use internal registry -export CT_OCP4_TEST=true - -TEST_SUMMARY='' -TEST_SET=${TESTS:-$TEST_LIST} ct_run_tests_from_testset "openshift-remote-cluster" - -# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/7/test/show_all_imagestreams.py b/7/test/show_all_imagestreams.py deleted file mode 100755 index d802276..0000000 --- a/7/test/show_all_imagestreams.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/env python3 - -# MIT License -# -# Copyright (c) 2018-2019 Red Hat, Inc. - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import json -import os -import sys - -from pathlib import Path -from typing import Dict, Any - -IMAGESTREAMS_DIR: str = "imagestreams" - - -class ShowAllImageStreams(object): - def load_json_file(self, filename: Path) -> Any: - with open(str(filename)) as f: - data = json.load(f) - isinstance(data, Dict) - return data - - def show_all_imagestreams(self) -> int: - p = Path(".") - json_files = p.glob(f"{IMAGESTREAMS_DIR}/*.json") - if not json_files: - print(f"No json files present in {IMAGESTREAMS_DIR}.") - return 0 - for f in json_files: - json_dict = self.load_json_file(f) - print(f"Tags in the image stream {f}:") - for tag in json_dict["spec"]["tags"]: - print(f"- {tag['name']} -> {tag['from']['name']}") - return 0 - - -if __name__ == "__main__": - isc = ShowAllImageStreams() - isc.show_all_imagestreams() diff --git a/7/test/test-lib-openshift.sh b/7/test/test-lib-openshift.sh deleted file mode 100644 index d65d3a1..0000000 --- a/7/test/test-lib-openshift.sh +++ /dev/null @@ -1,1206 +0,0 @@ -# shellcheck disable=SC2148 -if [ -z "${sourced_test_lib_openshift:-}" ]; then - sourced_test_lib_openshift=1 -else - return 0 -fi - -# 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 -# TESTSUITE_RESULT=0 -# And the following trap must be set, in the beginning of the test script: -# trap ct_os_cleanup EXIT SIGINT -TESTSUITE_RESULT=0 - -function ct_os_cleanup() { - local exit_code=$? - echo "${TEST_SUMMARY:-}" - if [ "$TESTSUITE_RESULT" -ne 0 ] || [ "$exit_code" -ne 0 ]; then - # shellcheck disable=SC2153 - echo "OpenShift tests for ${IMAGE_NAME} failed." - exit 1 - else - # shellcheck disable=SC2153 - echo "OpenShift tests for ${IMAGE_NAME} succeeded." - exit 0 - 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=rhel8) -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 - oc status --suggest -} - -# 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 - local ocp_docker_address="172\.30\.[0-9\.]*" - if [ "${CVP:-0}" -eq "1" ]; then - # shellcheck disable=SC2034 - ocp_docker_address="172\.27\.[0-9\.]*" - fi - # shellcheck disable=SC2016 - oc get "svc/${service_name}" -o yaml | grep clusterIP | \ - cut -d':' -f2 | grep -oe "$ocp_docker_address" -} - - -# 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}" - oc status --suggest - 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-80-c9s \ -# DATABASE_IMAGE=mysql-80-c9s \ -# 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() { - local objects="bc builds dc is isimage istag po rc routes svc" - if [ "${CVP:-0}" -eq "1" ]; then - echo "Testing in CVP environment. No need to delete isimage and istag in OpenShift project. This is done by CVP pipeline" - objects="bc builds dc po pvc rc routes" - fi - for x in $objects; do - echo "oc gets info about $x" - oc get "$x" - echo "oc deletes $x with --all --force --grace-period=0" - oc delete "$x" --all --force --grace-period=0 - 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_v4 -# -------------------- -# Logs in into docker daemon -# Uses global REGISRTY_ADDRESS environment variable for arbitrary registry address. -# Does not do anything if REGISTRY_ADDRESS is set. -function ct_os_docker_login_v4() { - OCP4_REGISTER=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}') - echo "OCP4 loging address is $OCP4_REGISTER." - if [ -z "${OCP4_REGISTER}" ]; then - echo "!!!OpenShift 4 registry address not found. This is an error. Check OpenShift 4 cluster!!!" - return 1 - fi - - if docker login -u kubeadmin -p "$(oc whoami -t)" "${OCP4_REGISTER}"; then - echo "Login to $OCP4_REGISTER was successfully." - return 0 - fi - return 1 -} - -# ct_os_upload_image IMAGE [IMAGESTREAM] -# -------------------- -# Uploads image from local registry to the OpenShift internal registry. -# 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=${1} - local output_name - local source_name - - source_name="${input_name}" - # Variable OCP4_REGISTER is set in function ct_os_docker_login_v4 - if ! ct_os_docker_login_v4; then - return 1 - fi - output_name="$OCP4_REGISTER/$namespace/$image_name" - - docker tag "${source_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_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_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 - echo "Uploading image ${image_name} as ${image_tagged} into OpenShift internal registry." - ct_os_upload_image "${image_name}" "${image_tagged}" - 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 - echo "Uploading image ${image_name} as ${image_tagged} into OpenShift internal registry." - ct_os_upload_image "${image_name}" "${image_tagged}" - else - echo "Import is already done by CVP pipeline." - fi - # Upload main image is already done by CVP pipeline. No need to do it twice. - if [ "${CVP:-0}" -eq "0" ]; then - # Other images are not uploaded by CVP pipeline. We need to do it. - # 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 - - echo "Uploading image ${image_tag_a[0]} as ${image_tag_a[1]} into OpenShift internal registry." - ct_os_upload_image "${image_tag_a[0]}" "${image_tag_a[1]}" - 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 ip="" check_command_exp="" - local image_name_no_namespace=${image_name##*/} - local service_name="${image_name_no_namespace%%:*}-testing" - - 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 - result=$? - - # shellcheck disable=SC2119 - ct_os_delete_project - return $result -} - -# 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_SKIP_NEW_PROJECT=false - 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_SKIP_NEW_PROJECT=false - 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/7/test/test-lib-remote-openshift.sh b/7/test/test-lib-remote-openshift.sh deleted file mode 100644 index 8b51cbe..0000000 --- a/7/test/test-lib-remote-openshift.sh +++ /dev/null @@ -1,136 +0,0 @@ -# shellcheck disable=SC2148 -if [ -z "${sourced_test_lib_remote_openshift:-}" ]; then - sourced_test_lib_remote_openshift=1 -else - return 0 -fi - -# 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 - -# this should be returned when something related to the openshift cluster -# goes wrong during the test pipeline -# shellcheck disable=SC2034 -readonly OC_ERR=11 - -# Set of functions for testing docker images in OpenShift using 'oc' command - -# A variable containing the overall test result -# TESTSUITE_RESULT=0 -# And the following trap must be set, in the beginning of the test script: -# trap ct_os_cleanup EXIT SIGINT - -# 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. 4.4) -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 "OpenShift Client binary on path ${installed_oc_path} not found" - return 1 - fi - export PATH="${oc_path}:${PATH}" -} - -# 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} - ct_os_set_path_oc_4 "${OS_OC_CLIENT_VERSION}" - - login=$(cat "$KUBEPASSWORD") - oc login -u kubeadmin -p "$login" - oc version - if ! oc version | grep -q "Client Version: ${OS_OC_CLIENT_VERSION}." ; then - echo "ERROR: something went wrong, oc located at ${oc_path}, but oc of version ${OS_OC_CLIENT_VERSION} not found in PATH ($PATH)" >&1 - return 1 - else - echo "PATH set correctly, binary oc found in version ${OS_OC_CLIENT_VERSION}: $(command -v oc)" - fi - # Switch to default project as soon as we are logged to cluster - oc project default - 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_tag_image_for_cvp() { - if [ "${CVP:-0}" -eq "0" ]; then - echo "The function is valid only for CVP pipeline." - return - fi - local tag_image_name="$1" - local tag="" - if [ "${OS}" == "rhel8" ]; then - tag="-el8" - elif [ "${OS}" == "rhel9" ]; then - tag="-el9" - else - echo "Only RHEL images are supported." - return - fi - oc tag "${tag_image_name}:${VERSION}" "${tag_image_name}:${VERSION}${tag}" -} - -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_import_image_ocp4() { - local image_name="${1}"; shift - local imagestream=${1:-$image_name:latest} - - echo "Uploading image ${image_name} as ${imagestream} into OpenShift internal registry." - ct_os_upload_image "${image_name}" "${imagestream}" - -} - -# ct_os_check_login -# --------------- -# function checks if the login to openshift was successful -# if successful returns 0 -# if not, write error message, sets test result to 1 -# and exits with non-zero -# Uses: $TESTSUITE_RESULT - overall result of all tests -function ct_os_check_login() { - oc status || { - echo "-------------------------------------------" - echo "It looks like oc is not properly logged in." - # shellcheck disable=SC2034 - TESTSUITE_RESULT=1 - return 1 - } -} diff --git a/7/test/test-lib-valkey.sh b/7/test/test-lib-valkey.sh deleted file mode 100644 index 0a43628..0000000 --- a/7/test/test-lib-valkey.sh +++ /dev/null @@ -1,48 +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_valkey_integration() { -# local service_name=valkey -# namespace_image="${OS}/valkey-${VERSION}" -# TEMPLATES="valkey-ephemeral-template.json -# valkey-persistent-template.json" -# for template in $TEMPLATES; do -# ct_os_test_template_app_func "${IMAGE_NAME}" \ -# "${THISDIR}/examples/${template}" \ -# "${service_name}" \ -# "ct_os_check_cmd_internal 'registry.redhat.io/${namespace_image}' '${service_name}-testing' 'timeout 15 valkey-cli -h -a testp ping' 'PONG'" \ -# "-p REDIS_VERSION=${VERSION} \ -# -p DATABASE_SERVICE_NAME="${service_name}-testing" \ -# -p REDIS_PASSWORD=testp" -# done -#} - -# Check the imagestream -#function test_valkey_imagestream() { -# TEMPLATES="valkey-ephemeral-template.json -# valkey-persistent-template.json" -# for template in $TEMPLATES; do -# ct_os_test_image_stream_template "${THISDIR}/imagestreams/valkey-${OS%[0-9]*}.json" "${THISDIR}/examples/${template}" valkey "-p REDIS_VERSION=${VERSION}${tag}" -# done -#} - -function test_latest_imagestreams() { - info "Testing the latest version in imagestreams" - # Switch to root directory of a container - pushd "${THISDIR}/../.." >/dev/null - ct_check_latest_imagestreams - popd >/dev/null -} - -# vim: set tabstop=2:shiftwidth=2:expandtab: diff --git a/7/test/test-lib.sh b/7/test/test-lib.sh deleted file mode 100644 index 84377b4..0000000 --- a/7/test/test-lib.sh +++ /dev/null @@ -1,1509 +0,0 @@ -# shellcheck shell=bash -# -# Test a container image. -# -# Always use sourced from a specific container testfile -# - -# Container CI tests -# abbreviated as "ct" - -# run ct_init before starting the actual testsuite - -# shellcheck disable=SC2148 -if [ -z "${sourced_test_lib:-}" ]; then - sourced_test_lib=1 -else - return 0 -fi - -LINE="==============================================" - -# may be redefined in the specific container testfile -EXPECTED_EXIT_CODE=0 - -# define UNSTABLE_TESTS if not already defined, as this variable -# is not mandatory for containers -UNSTABLE_TESTS="${UNSTABLE_TESTS:-""}" - - -# ct_init -# -------------------- -# This function needs to be called before any container test starts -# Sets: $APP_ID_FILE_DIR - path to directory used for storing -# IDs of application images used during tests. -# Sets: $CID_FILE_DIR - path to directory containing cid_files -# Sets: $TEST_SUMMARY - string, where test results are written -# Sets: $TESTSUITE_RESULT - overall result of run testuite -function ct_init() { - APP_ID_FILE_DIR="$(mktemp -d)" - CID_FILE_DIR="$(mktemp -d)" - TEST_SUMMARY="" - TESTSUITE_RESULT=0 - ct_enable_cleanup -} - -# 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 -# Uses: $TESTSUITE_RESULT - overall result of all tests -function ct_cleanup() { - echo "$LINE" - echo "Cleaning of testing containers and images started." - echo "It may take a few seconds." - echo "$LINE" - ct_clean_app_images - ct_clean_containers -} - -# ct_build_image_and_parse_id -# -------------------- -# Return 0 if build was successful, 1 otherwise -# Uses: $1 - path to docckerfile -# Uses: $2 - build params -# Uses: $APP_IMAGE_ID - sets the app image id value to this variable -# this should be replaced by the --iidfile parameter -# when it becames supported by all versions of podman and docker that we support -ct_build_image_and_parse_id() { - local tmpdir - local log_file - local ret_val - local dockerfile - local command - local pid_build - local pid_sleep - local sleep_time - log_file="$(mktemp)" - sleep_time="10m" - [ -n "$1" ] && dockerfile="-f $1" - command="$(echo "docker build --no-cache $dockerfile $2" | tr -d "'")" - # running command in subshell, the subshell in background, storing pid to variable - ( - $command > "$log_file" 2>&1 - ) & pid_build=$! - # creating second subshell with trap function on ALRM signal - # the subshell sleeps for 10m, then kills the first subshell - ( - trap 'exit 0' ALRM; sleep "$sleep_time" && kill $pid_build - ) & pid_sleep=$! - # waiting for build subshell to finish, either with success, or killed from sleep subshell - wait $pid_build - ret_val=$? - # send ALRM signal to the sleep subshell, so it exits even in case the 10mins - # not yet passed. If the kill was successful (the wait subshell received ALRM signal) - # then the build was not finished yet, so the return value is set to 1 - kill -s ALRM $pid_sleep 2>/dev/null || ret_val=1 - - if [ $ret_val -eq 0 ]; then - APP_IMAGE_ID="$(tail -n 1 "$log_file")" - fi - - cat "$log_file" ; rm -r "$log_file" - return "$ret_val" -} - -# ct_container_running -# -------------------- -# Return 0 if given container is in running state -# Uses: $1 - container id to check -function ct_container_running() { - local running - running="$(docker inspect -f '{{.State.Running}}' "$1")" - [ "$running" = "true" ] || return 1 -} - -# ct_container_exists -# -------------------- -# Return 0 if given container exists -# Uses: $1 - container id to check -function ct_container_exists() { - local exists - exists="$(docker ps -q -a -f "id=$1")" - [ -n "$exists" ] || return 1 -} - -# ct_clean_app_images -# -------------------- -# Cleans up application images referenced by APP_ID_FILE_DIR -# Uses: $APP_ID_FILE_DIR - path to directory containing image ID files -function ct_clean_app_images() { - local image - if [[ ! -d "${APP_ID_FILE_DIR:-}" ]]; then - echo "The \$APP_ID_FILE_DIR=$APP_ID_FILE_DIR is not created. App cleaning is to be skipped." - return 0 - fi; - echo "Examining image ID files in \$APP_ID_FILE_DIR=$APP_ID_FILE_DIR" - for file in "${APP_ID_FILE_DIR:?}"/*; do - image="$(cat "$file")" - docker inspect "$image" > /dev/null 2>&1 || continue - containers="$(docker ps -q -a -f ancestor="$image")" - [[ -z "$containers" ]] || docker rm -f "$containers" 2>/dev/null - docker rmi -f "$image" - done - rm -fr "$APP_ID_FILE_DIR" -} - -# ct_clean_containers -# -------------------- -# Cleans up containers referenced by CID_FILE_DIR -# Uses: $CID_FILE_DIR - path to directory containing cid_files -function ct_clean_containers() { - if [[ -z ${CID_FILE_DIR:-} ]]; then - echo "The \$CID_FILE_DIR is not set. Container cleaning is to be skipped." - return - fi; - - echo "Examining CID files in \$CID_FILE_DIR=$CID_FILE_DIR" - for cid_file in "$CID_FILE_DIR"/* ; do - [ -f "$cid_file" ] || continue - local container - container=$(cat "$cid_file") - - ct_container_exists "$container" || continue - - echo "Stopping and removing container $container..." - if ct_container_running "$container"; then - docker stop "$container" - fi - - exit_status=$(docker inspect -f '{{.State.ExitCode}}' "$container") - if [ "$exit_status" != "$EXPECTED_EXIT_CODE" ]; then - echo "Dumping logs for $container" - docker logs "$container" - fi - docker rm -v "$container" - rm -f "$cid_file" - done - - rm -rf "$CID_FILE_DIR" -} - -# ct_show_results -# --------------- -# Prints results of all test cases that are stored into TEST_SUMMARY variable. -# Uses: $IMAGE_NAME - name of the tested container image -# Uses: $TEST_SUMMARY - text info about test-cases -# Uses: $TESTSUITE_RESULT - overall result of all tests -function ct_show_results() { - echo "$LINE" - #shellcheck disable=SC2153 - echo "Tests were run for image ${IMAGE_NAME}" - echo "$LINE" - echo "Test cases results:" - echo - echo "${TEST_SUMMARY:-}" - - if [ -n "${TESTSUITE_RESULT:-}" ] ; then - if [ "$TESTSUITE_RESULT" -eq 0 ] ; then - # shellcheck disable=SC2153 - echo "Tests for ${IMAGE_NAME} succeeded." - else - # shellcheck disable=SC2153 - echo "Tests for ${IMAGE_NAME} failed." - fi - fi -} - -# ct_enable_cleanup -# -------------------- -# Enables automatic container cleanup after tests. -function ct_enable_cleanup() { - trap ct_trap_on_exit EXIT - trap ct_trap_on_sigint SIGINT -} - -# ct_trap_on_exit -# -------------------- -function ct_trap_on_exit() { - local exit_code=$? - [ "$exit_code" -eq 130 ] && return # we do not want to catch SIGINT here - # We should not really care about what the script returns - # as the tests are constructed the way they never exit the shell. - # The check is added just to be sure that we catch some not expected behavior - # if any is added in the future. - echo "Tests finished with EXIT=$exit_code" - [ $exit_code -eq 0 ] && exit_code="${TESTSUITE_RESULT:-0}" - [ -n "${DEBUG:-}" ] || ct_show_resources - ct_cleanup - ct_show_results - exit "$exit_code" -} - -# ct_trap_on_sigint -# -------------------- -function ct_trap_on_sigint() { - echo "Tests were stopped by SIGINT signal" - ct_cleanup - ct_show_results - exit 130 -} - -# 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"; [[ $# -gt 0 ]] && shift - local exit_variable=${1:-"false"}; [[ $# -gt 0 ]] && shift - local loops=${1:-10} - 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_variable" == 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 - # shellcheck disable=SC2295 - 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 - echo "Waiting for container start... $attempt" - 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 - 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" - 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 -E -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 - local cid_file - tmpdir=$(mktemp -d) - : " Testing npm in the container image" - cid_file="$(mktemp --dry-run --tmpdir="${CID_FILE_DIR}")" - 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")" - 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 id_file - 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 - ct_build_image_and_parse_id "$tmpdir/Dockerfile" "$tmpdir" - #shellcheck disable=SC2181 - if [ $? -ne 0 ]; then - echo " ERROR: Failed to find $binary in \$PATH!" >&2 - return 1 - fi - id_file="${APP_ID_FILE_DIR:?}"/"$RANDOM" - echo "$APP_IMAGE_ID" > "$id_file" -} - -# 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_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} - - echo " 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 - echo "Trying to connect ... ${attempt}" - 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" == "rhel8" ]; then - public_image_name=$registry/rhel8/$base_image_name-${version//./} - elif [ "$os" == "rhel9" ]; then - public_image_name=$registry/rhel9/$base_image_name-${version//./} - elif [ "$os" == "c9s" ]; then - public_image_name=$registry/sclorg/$base_image_name-${version//./}-c9s - elif [ "$os" == "c10s" ]; then - public_image_name=$registry/sclorg/$base_image_name-${version//./}-c10s - 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 ..." - # shellcheck disable=SC2294 - 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 ..." - # shellcheck disable=SC2294 - 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. -# This function is wrapper for ct_s2i_build_as_df_build_args in case user do not want to add build args -# This function is used in all https://github.com/sclorg/*-container test cases and we do not -# want to break functionality -# 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="$*"; - - ct_s2i_build_as_df_build_args "$app_path" "$src_image" "$dst_image" "" "$s2i_args" -} - -# ct_s2i_build_as_df_build_args APP_PATH SRC_IMAGE DST_IMAGE BUILD_ARGS [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: BUILD_ARGS - Build arguments to be used in the s2i build -# 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_build_args() -{ - local app_path=$1; shift - local src_image=$1; shift - local dst_image=$1; shift - local build_args=$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=() - local id_file - - # Run the entire thing inside a subshell so that we do not leak shell options outside of the function - ( - # FIXME: removed temporarily, need proper fixing - # 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" || return 1 - # 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 - ct_build_image_and_parse_id "$df_name" "${mount_options[*]+${mount_options[*]}} -t $dst_image . $build_args" - #shellcheck disable=SC2181 - if [ "$?" -ne 0 ]; then - echo " ERROR: Failed to to build $df_name" >&2 - return 1 - fi - id_file="${APP_ID_FILE_DIR:?}"/"$RANDOM" - echo "$APP_IMAGE_ID" > "$id_file" - ) -} - -# 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=() - local id_file - - - # Run the entire thing inside a subshell so that we do not leak shell options outside of the function - ( - # FIXME: removed temporarily, need proper fixing - # 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" || return 1 - - # 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)" - - ct_build_image_and_parse_id "$df_name" "${mount_options[*]+${mount_options[*]}} -t $dst_image ." - #shellcheck disable=SC2181 - if [ "$?" -ne 0 ]; then - echo " ERROR: Failed to to build $df_name" >&2 - return 1 - fi - id_file="${APP_ID_FILE_DIR:?}"/"$RANDOM" - echo "$APP_IMAGE_ID" > "$id_file" - ) -} - -# 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 Stream (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 - - test_lib_dir=$(dirname "$(readlink -f "$0")") - python3 "${test_lib_dir}/show_all_imagestreams.py" - # shellcheck disable=SC2153 - if [ "$latest_version" == "$VERSION" ]; then - 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 - echo "$LINE" - echo "Resources info:" - echo "Memory:" - free -h - echo "Storage:" - df -h || : - echo "CPU" - lscpu - - echo "$LINE" - echo "Image ${IMAGE_NAME} information:" - echo "$LINE" - echo "Uncompressed size of the image: $(ct_get_image_size_uncompresseed "${IMAGE_NAME}")" - echo "Compressed size of the image: $(ct_get_image_size_compresseed "${IMAGE_NAME}")" - echo -} - -# 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: build_args - build args that will be used for building an image -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 build_args=${5:-""} - local port=8080 - local app_image_name=myapp - local ret - local cname=app_dockerfile - local id_file - - 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 || return 1 - 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 ! ct_build_image_and_parse_id "" "-t ${app_image_name} . $build_args"; then - echo "ERROR: The image cannot be built from ${dockerfile} and application ${app_url}." - echo "Terminating the Dockerfile build." - return 1 - fi - id_file="${APP_ID_FILE_DIR:?}"/"$RANDOM" - echo "$APP_IMAGE_ID" > "$id_file" - - 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}")" - if [ -z "$ip" ]; then - echo "ERROR: Cannot get container's IP address." - return 1 - fi - ct_test_response "http://$ip:${port}" 200 "${expected_text}" - ret=$? - - [[ $ret -eq 0 ]] || docker logs "$(ct_get_cid "${cname}")" - - # cleanup - docker kill "$(ct_get_cid "${cname}")" - sleep 2 - docker rmi "${app_image_name}" - popd >/dev/null || return 1 - rm -rf "${tmpdir}" - rm -f "${CID_FILE_DIR}/${cname}" - return $ret -} - -# ct_check_testcase_result -# ----------------------------- -# Check if testcase ended in success or error -# Argument: result - testcase result value -# Uses: $TESTCASE_RESULT - result of the testcase -# Uses: $IMAGE_NAME - name of the image being tested -ct_check_testcase_result() { - local result="$1" - if [[ "$result" != "0" ]]; then - echo "Test for image '${IMAGE_NAME}' FAILED (exit code: ${result})" - TESTCASE_RESULT=1 - fi - return "$result" -} - -# ct_update_test_result -# ----------------------------- -# adds result to the $TEST_SUMMARY variable -# Argument: test_msg -# Argument: app_name -# Argument: test_name -# Argument: time_diff (optional) -# Uses: $TEST_SUMMARY - variable for storing test results -ct_update_test_result() { - local test_msg="$1" - local app_name="$2" - local test_case="$3" - local time_diff="${4:-}" - printf -v TEST_SUMMARY "%s %s for '%s' %s (%s)\n" "${TEST_SUMMARY:-}" "${test_msg}" "${app_name}" "$test_case" "$time_diff" -} - -# ct_run_tests_from_testset -# ----------------------------- -# Runs all tests in $TEST_SET, prints result to -# the $TEST_SUMMARY variable -# Argument: app_name - application name to log -# Uses: $TEST_SET - set of test cases to run -# Uses: $TEST_SUMMARY - variable for storing test results -# Uses: $IMAGE_NAME - name of the image being tested -# Uses: $UNSTABLE_TESTS - set of tests, whose result can be ignored -# Uses: $IGNORE_UNSTABLE_TESTS - flag to ignore unstable tests -ct_run_tests_from_testset() { - local app_name="${1:-appnamenotset}" - local time_beg_pretty - local time_beg - local time_end - local time_diff - local test_msg - local is_unstable - - # Let's store in the log what change do we test - echo - git show -s - echo - - echo "Running tests for image ${IMAGE_NAME}" - - for test_case in $TEST_SET; do - TESTCASE_RESULT=0 - # shellcheck disable=SC2076 - if [[ " ${UNSTABLE_TESTS[*]} " =~ " ${app_name} " ]] || \ - [[ " ${UNSTABLE_TESTS[*]} " =~ " ${test_case} " ]]; then - is_unstable=1 - else - is_unstable=0 - fi - time_beg_pretty=$(ct_timestamp_pretty) - time_beg=$(ct_timestamp_s) - echo "-----------------------------------------------" - echo "Running test $test_case (starting at $time_beg_pretty) ... " - echo "-----------------------------------------------" - $test_case - ct_check_testcase_result $? - time_end=$(ct_timestamp_s) - if [ $TESTCASE_RESULT -eq 0 ]; then - test_msg="[PASSED]" - else - if [ -n "${IGNORE_UNSTABLE_TESTS:-""}" ] && [ $is_unstable -eq 1 ]; then - test_msg="[FAILED][UNSTABLE-IGNORED]" - else - test_msg="[FAILED]" - TESTSUITE_RESULT=1 - fi - fi - # As soon as test is finished - # switch the project from sclorg-test- to default. - if [ "${CT_OCP4_TEST:-false}" == "true" ]; then - oc project default - fi - time_diff=$(ct_timestamp_diff "$time_beg" "$time_end") - ct_update_test_result "${test_msg}" "${app_name}" "$test_case" "$time_diff" - done -} - -# ct_timestamp_s -# -------------- -# Returns timestamp in seconds since unix era -- a large integer -function ct_timestamp_s() { - date '+%s' -} - -# ct_timestamp_pretty -# ----------------- -# Returns timestamp readable to a human, like 2022-05-18 10:52:44+02:00 -function ct_timestamp_pretty() { - date --rfc-3339=seconds -} - -# ct_timestamp_diff -# ----------------- -# Computes a time diff between two timestamps -# Argument: start_date - Beginning (in seconds since unix era -- a large integer) -# Argument: final_date - End (in seconds since unix era -- a large integer) -# Returns: Time difference in format HH:MM:SS -function ct_timestamp_diff() { - local start_date=$1 - local final_date=$2 - date -u -d "0 $final_date seconds - $start_date seconds" +"%H:%M:%S" -} - -# ct_get_certificate_timestamp -# ---------------------------- -# Looks into a running container into a specified file (certificate) and extracts -# a notBefore date. -# Argument: container - ID of a running container -# Argument: path - path to the certificate inside the running container -# Returns: timestamp (seconds since Unix era) for the certificate generation -function ct_get_certificate_timestamp() { - local container=$1 - local path=$2 - date '+%s' --date="$(docker exec "$container" bash -c "cat $path" | openssl x509 -startdate -noout | grep notBefore | sed -e 's/notBefore=//')" -} - -# ct_get_certificate_age_s -# ------------------------ -# Looks into a running container into a specified file and retuns age of the certificate -# Argument: container - ID of a running container -# Argument: path - path inside the running container -# Returns: age of the certificate in seconds -function ct_get_certificate_age_s() { - local container=$1 - local path=$2 - local now - local cert_timestamp - now=$(date '+%s') - cert_timestamp=$(ct_get_certificate_timestamp "$container" "$path") - echo $(( now - cert_timestamp )) -} - -# ct_get_image_age_s -# ------------------ -# Retuns age of a given image in seconds -# Argument: image_name - name of a given image -# Returns: age of the image in seconds -function ct_get_image_age_s() { - local image_name=$1 - local now - local image_created - local image_timestamp - now=$(date '+%s') - # docker inspect returns format