diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..198beb1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,71 @@ +FROM registry.fedoraproject.org/f30/s2i-core:latest + +# Redis image based on Software Collections packages +# +# Volumes: +# * /var/lib/redis/data - Datastore for Redis +# Environment: +# * $REDIS_PASSWORD - Database password + +ENV NAME=redis \ + VERSION=5 \ + ARCH=x86_64 + +ENV REDIS_VERSION=$VERSION \ + HOME=/var/lib/redis + +ENV SUMMARY="Redis in-memory data structure store, used as database, cache and message broker" \ + DESCRIPTION="Redis $REDIS_VERSION available as container, is an advanced key-value store. \ +It is often referred to as a data structure server since keys can contain strings, hashes, lists, \ +sets and sorted sets. You can run atomic operations on these types, like appending to a string; \ +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; \ +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding \ +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist \ +it either by dumping the dataset to disk every once in a while, or by appending each command to a log." + +LABEL summary="$SUMMARY" \ + description="$DESCRIPTION" \ + io.k8s.description="$SUMMARY" \ + io.k8s.display-name="Redis 5" \ + io.openshift.expose-services="6379:redis" \ + io.openshift.tags="database,redis,redis5" \ + com.redhat.component="$NAME" \ + name="$FGC/$NAME" \ + version="$VERSION" \ + usage="docker run -d --name redis_database -p 6379:6379 $FGC/$NAME" \ + maintainer="SoftwareCollections.org " + +EXPOSE 6379 + +# Create user for redis that has known UID +# We need to do this before installing the RPMs which would create user with random UID +RUN getent group redis &> /dev/null || groupadd -r redis &> /dev/null && \ + usermod -l redis -g redis -c 'Redis Server' default &> /dev/null && \ +# Install gettext for envsubst command +# This image must forever use UID 964 for redis user so our volumes are +# safe in the future. This should *never* change, the last test is there +# to make sure of that. + dnf install -y yum-utils gettext policycoreutils && \ + INSTALL_PKGS="redis" && \ + dnf install -y --setopt=tsflags=nodocs --nogpgcheck $INSTALL_PKGS && \ + rpm -V $INSTALL_PKGS && \ + dnf clean all && \ + mkdir -p /var/lib/redis/data && chown -R redis.0 /var/lib/redis + +# Get prefix path and path to scripts rather than hard-code them in scripts +ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/redis \ + REDIS_PREFIX=/usr + +COPY root / + +# this is needed due to issues with squash +# when this directory gets rm'd by the container-setup +# script. +RUN /usr/libexec/container-setup + +VOLUME ["/var/lib/redis/data"] + +USER 1001 + +ENTRYPOINT ["container-entrypoint"] +CMD ["run-redis"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..0772ce6 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +Redis 5 in-memory data structure store container image +==================== + +This container image includes Redis 5 in-memory data structure store for OpenShift and general usage. +Users can choose between RHEL, CentOS and Fedora based images. +The RHEL images are available in the [Red Hat Container Catalog](https://access.redhat.com/containers/), +the CentOS images are available on [Docker Hub](https://hub.docker.com/r/centos/), +and the Fedora images are available in [Fedora Registry](https://registry.fedoraproject.org/). +The resulting image can be run using [podman](https://github.com/containers/libpod). + +Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments + +Description +----------- + +Redis 5 available as container, is an advanced key-value store. +It is often referred to as a data structure server since keys can contain strings, hashes, lists, +sets and sorted sets. You can run atomic operations on these types, like appending to a string; +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist +it either by dumping the dataset to disk every once in a while, or by appending each command to a log. + + +Usage +----- + +For this, we will assume that you are using the `rhel8/redis-5` image. +If you want to set only the mandatory environment variables and not store +the database in a host directory, execute the following command: + +``` +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-5 +``` + +This will create a container named `redis_database`. Port 6379 will be exposed and mapped +to the host. + +If you want your database to be persistent across container executions, also add a +`-v /host/db/path:/var/lib/redis/data:Z` argument. This will be the Redis data directory. + +For protecting Redis data by a password, pass `REDIS_PASSWORD` environment variable +to the container like this: + +``` +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-5 +``` + +**Warning: since Redis is pretty fast an outside user can try up to +150k passwords per second against a good box. This means that you should +use a very strong password otherwise it will be very easy to break.** + + +Environment variables and volumes +---------------------------------- + +**`REDIS_PASSWORD`** + Password for the server access + + +You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. + +**`/var/lib/redis/data`** + Redis data directory + + +**Notice: When mouting a directory from the host into the container, ensure that the mounted +directory has the appropriate permissions and that the owner and group of the directory +matches the user UID or name which is running inside the container.** + + +Troubleshooting +--------------- +Redis logs into standard output, so the log is available in the container log. The log can be examined by running: + + podman logs + + +See also +-------- +Dockerfile and other sources for this container image are available on +https://github.com/sclorg/redis-container. +In that repository you also can find another versions of Python environment Dockerfiles. +Dockerfile for CentOS is called `Dockerfile`, Dockerfile for RHEL7 is called `Dockerfile.rhel7`, +for RHEL8 it's `Dockerfile.rhel8` and the Fedora Dockerfile is called Dockerfile.fedora. diff --git a/dead.package b/dead.package deleted file mode 100644 index 7290702..0000000 --- a/dead.package +++ /dev/null @@ -1 +0,0 @@ -container sources not used for building fedora containers anymore diff --git a/root/help.1 b/root/help.1 new file mode 100644 index 0000000..f0a8059 --- /dev/null +++ b/root/help.1 @@ -0,0 +1,107 @@ +.TH Redis 5 in\-memory data structure store container image +.PP +This container image includes Redis 5 in\-memory data structure store for OpenShift and general usage. +Users can choose between RHEL, CentOS and Fedora based images. +The RHEL images are available in the Red Hat Container Catalog +\[la]https://access.redhat.com/containers/\[ra], +the CentOS images are available on Docker Hub +\[la]https://hub.docker.com/r/centos/\[ra], +and the Fedora images are available in Fedora Registry +\[la]https://registry.fedoraproject.org/\[ra]\&. +The resulting image can be run using podman +\[la]https://github.com/containers/libpod\[ra]\&. + +.PP +Note: while the examples in this README are calling \fB\fCpodman\fR, you can replace any such calls by \fB\fCdocker\fR with the same arguments + +.SH Description +.PP +Redis 5 available as container, is an advanced key\-value store. +It is often referred to as a data structure server since keys can contain strings, hashes, lists, +sets and sorted sets. You can run atomic operations on these types, like appending to a string; +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding +performance, Redis works with an in\-memory dataset. Depending on your use case, you can persist +it either by dumping the dataset to disk every once in a while, or by appending each command to a log. + +.SH Usage +.PP +For this, we will assume that you are using the \fB\fCrhel8/redis\-5\fR image. +If you want to set only the mandatory environment variables and not store +the database in a host directory, execute the following command: + +.PP +.RS + +.nf +$ podman run \-d \-\-name redis\_database \-p 6379:6379 rhel8/redis\-5 + +.fi +.RE + +.PP +This will create a container named \fB\fCredis\_database\fR\&. Port 6379 will be exposed and mapped +to the host. + +.PP +If you want your database to be persistent across container executions, also add a +\fB\fC\-v /host/db/path:/var/lib/redis/data:Z\fR argument. This will be the Redis data directory. + +.PP +For protecting Redis data by a password, pass \fB\fCREDIS\_PASSWORD\fR environment variable +to the container like this: + +.PP +.RS + +.nf +$ podman run \-d \-\-name redis\_database \-e REDIS\_PASSWORD=strongpassword rhel8/redis\-5 + +.fi +.RE + +.PP +\fBWarning: since Redis is pretty fast an outside user can try up to +150k passwords per second against a good box. This means that you should +use a very strong password otherwise it will be very easy to break.\fP + +.SH Environment variables and volumes +.PP +\fB\fB\fCREDIS\_PASSWORD\fR\fP +.br + Password for the server access + +.PP +You can also set the following mount points by passing the \fB\fC\-v /host:/container:Z\fR flag to podman. + +.PP +\fB\fB\fC/var/lib/redis/data\fR\fP +.br + Redis data directory + +.PP +\fBNotice: When mouting a directory from the host into the container, ensure that the mounted +directory has the appropriate permissions and that the owner and group of the directory +matches the user UID or name which is running inside the container.\fP + +.SH Troubleshooting +.PP +Redis logs into standard output, so the log is available in the container log. The log can be examined by running: + +.PP +.RS + +.nf +podman logs + +.fi +.RE + +.SH See also +.PP +Dockerfile and other sources for this container image are available on + +\[la]https://github.com/sclorg/redis-container\[ra]\&. +In that repository you also can find another versions of Python environment Dockerfiles. +Dockerfile for CentOS is called \fB\fCDockerfile\fR, Dockerfile for RHEL7 is called \fB\fCDockerfile.rhel7\fR, +for RHEL8 it's \fB\fCDockerfile.rhel8\fR and the Fedora Dockerfile is called Dockerfile.fedora. diff --git a/root/usr/bin/container-entrypoint b/root/usr/bin/container-entrypoint new file mode 100755 index 0000000..9d8ad4d --- /dev/null +++ b/root/usr/bin/container-entrypoint @@ -0,0 +1,2 @@ +#!/bin/bash +exec "$@" diff --git a/root/usr/bin/run-redis b/root/usr/bin/run-redis new file mode 100755 index 0000000..9ca4202 --- /dev/null +++ b/root/usr/bin/run-redis @@ -0,0 +1,27 @@ +#!/bin/bash + +export_vars=$(cgroup-limits); export $export_vars +source ${CONTAINER_SCRIPTS_PATH}/common.sh +set -eu + +[ -f ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh ] && source ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh + +# Process the Redis configuration files +log_info 'Processing Redis configuration files ...' +if [[ -v REDIS_PASSWORD ]]; then + envsubst < ${CONTAINER_SCRIPTS_PATH}/password.conf.template >> /etc/redis.conf +else + log_info 'WARNING: setting REDIS_PASSWORD is recommended' +fi + +# Source post-init source if exists +if [ -f ${CONTAINER_SCRIPTS_PATH}/post-init.sh ]; then + log_info 'Sourcing post-init.sh ...' + source ${CONTAINER_SCRIPTS_PATH}/post-init.sh +fi + +# Restart the Redis server with public IP bindings +unset_env_vars +log_volume_info "${REDIS_DATADIR}" +log_info 'Running final exec -- Only Redis logs after this point' +exec ${REDIS_PREFIX}/bin/redis-server /etc/redis.conf --daemonize no "$@" 2>&1 diff --git a/root/usr/bin/usage b/root/usr/bin/usage new file mode 100755 index 0000000..d204ed2 --- /dev/null +++ b/root/usr/bin/usage @@ -0,0 +1,4 @@ +#!/bin/bash + +cat /usr/share/container-scripts/redis/README.md + diff --git a/root/usr/libexec/container-setup b/root/usr/libexec/container-setup new file mode 100755 index 0000000..0375238 --- /dev/null +++ b/root/usr/libexec/container-setup @@ -0,0 +1,31 @@ +#!/bin/bash + +source ${CONTAINER_SCRIPTS_PATH}/common.sh +set -eu + +# setup config file +if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then + mv /etc/opt/rh/rh-redis5/redis.conf /etc/redis.conf + ln -s /etc/redis.conf /etc/opt/rh/rh-redis5/redis.conf +fi + +# setup directory for data +chown -R redis:0 "${HOME}" /etc/redis.conf +restorecon -R "${HOME}" /etc/redis.conf + +# create a symlink for SCL datadir, so there is some reasonable content there +if [ -n "${ENABLED_COLLECTIONS:-}" ] ; then + rmdir /var/opt/rh/rh-redis5/lib/redis/ + ln -s /var/lib/redis /var/opt/rh/rh-redis5/lib/redis +fi + +# Loosen permission bits for group to avoid problems running container with +# arbitrary UID +# When only specifying user, group is 0, that's why /var/lib/redis must have +# owner redis.0; that allows to avoid a+rwx for this dir +chmod 0770 "${HOME}" "${REDIS_DATADIR}" +chmod 0660 /etc/redis.conf + +# adjust config with changes we do every-time +clear_config +envsubst < ${CONTAINER_SCRIPTS_PATH}/base.conf.template >> /etc/redis.conf diff --git a/root/usr/share/container-scripts/redis/README.md b/root/usr/share/container-scripts/redis/README.md new file mode 100644 index 0000000..0772ce6 --- /dev/null +++ b/root/usr/share/container-scripts/redis/README.md @@ -0,0 +1,85 @@ +Redis 5 in-memory data structure store container image +==================== + +This container image includes Redis 5 in-memory data structure store for OpenShift and general usage. +Users can choose between RHEL, CentOS and Fedora based images. +The RHEL images are available in the [Red Hat Container Catalog](https://access.redhat.com/containers/), +the CentOS images are available on [Docker Hub](https://hub.docker.com/r/centos/), +and the Fedora images are available in [Fedora Registry](https://registry.fedoraproject.org/). +The resulting image can be run using [podman](https://github.com/containers/libpod). + +Note: while the examples in this README are calling `podman`, you can replace any such calls by `docker` with the same arguments + +Description +----------- + +Redis 5 available as container, is an advanced key-value store. +It is often referred to as a data structure server since keys can contain strings, hashes, lists, +sets and sorted sets. You can run atomic operations on these types, like appending to a string; +incrementing the value in a hash; pushing to a list; computing set intersection, union and difference; +or getting the member with highest ranking in a sorted set. In order to achieve its outstanding +performance, Redis works with an in-memory dataset. Depending on your use case, you can persist +it either by dumping the dataset to disk every once in a while, or by appending each command to a log. + + +Usage +----- + +For this, we will assume that you are using the `rhel8/redis-5` image. +If you want to set only the mandatory environment variables and not store +the database in a host directory, execute the following command: + +``` +$ podman run -d --name redis_database -p 6379:6379 rhel8/redis-5 +``` + +This will create a container named `redis_database`. Port 6379 will be exposed and mapped +to the host. + +If you want your database to be persistent across container executions, also add a +`-v /host/db/path:/var/lib/redis/data:Z` argument. This will be the Redis data directory. + +For protecting Redis data by a password, pass `REDIS_PASSWORD` environment variable +to the container like this: + +``` +$ podman run -d --name redis_database -e REDIS_PASSWORD=strongpassword rhel8/redis-5 +``` + +**Warning: since Redis is pretty fast an outside user can try up to +150k passwords per second against a good box. This means that you should +use a very strong password otherwise it will be very easy to break.** + + +Environment variables and volumes +---------------------------------- + +**`REDIS_PASSWORD`** + Password for the server access + + +You can also set the following mount points by passing the `-v /host:/container:Z` flag to podman. + +**`/var/lib/redis/data`** + Redis data directory + + +**Notice: When mouting a directory from the host into the container, ensure that the mounted +directory has the appropriate permissions and that the owner and group of the directory +matches the user UID or name which is running inside the container.** + + +Troubleshooting +--------------- +Redis logs into standard output, so the log is available in the container log. The log can be examined by running: + + podman logs + + +See also +-------- +Dockerfile and other sources for this container image are available on +https://github.com/sclorg/redis-container. +In that repository you also can find another versions of Python environment Dockerfiles. +Dockerfile for CentOS is called `Dockerfile`, Dockerfile for RHEL7 is called `Dockerfile.rhel7`, +for RHEL8 it's `Dockerfile.rhel8` and the Fedora Dockerfile is called Dockerfile.fedora. diff --git a/root/usr/share/container-scripts/redis/base.conf.template b/root/usr/share/container-scripts/redis/base.conf.template new file mode 100644 index 0000000..9d3f01b --- /dev/null +++ b/root/usr/share/container-scripts/redis/base.conf.template @@ -0,0 +1 @@ +dir ${REDIS_DATADIR} diff --git a/root/usr/share/container-scripts/redis/common.sh b/root/usr/share/container-scripts/redis/common.sh new file mode 100644 index 0000000..7e336e4 --- /dev/null +++ b/root/usr/share/container-scripts/redis/common.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source ${CONTAINER_SCRIPTS_PATH}/helpers.sh + +# Data directory where Redis database files live. The data subdirectory is here +# because .bashrc lives in /var/lib/redis/ and we don't want a +# volume to override it. +export REDIS_DATADIR=/var/lib/redis/data + +# Be paranoid and stricter than we should be. +redis_password_regex='^[a-zA-Z0-9_~!@#$%^&*()-=<>,.?;:|]+$' + +# Make sure env variables don't propagate to redis process. +function unset_env_vars() { + log_info 'Cleaning up environment variable REDIS_PASSWORD ...' + unset REDIS_PASSWORD +} + +# Comment out settings that we'll set in container specifically +function clear_config() { + sed -e "s/^bind/#bind/" \ + -e "s/^logfile/#logfile/" \ + -e "s/^dir /#dir /" \ + -e "/^protected-mode/s/yes/no/" \ + -i /etc/redis.conf +} diff --git a/root/usr/share/container-scripts/redis/helpers.sh b/root/usr/share/container-scripts/redis/helpers.sh new file mode 100644 index 0000000..4e832fc --- /dev/null +++ b/root/usr/share/container-scripts/redis/helpers.sh @@ -0,0 +1,24 @@ +function log_info { + echo "---> `date +%T` $@" +} + +function log_and_run { + log_info "Running $@" + "$@" +} + +function log_volume_info { + CONTAINER_DEBUG=${CONTAINER_DEBUG:-} + if [[ "${CONTAINER_DEBUG,,}" != "true" ]]; then + return + fi + + log_info "Volume info for $@:" + set +e + log_and_run mount + while [ $# -gt 0 ]; do + log_and_run ls -alZ $1 + shift + done + set -e +} diff --git a/root/usr/share/container-scripts/redis/password.conf.template b/root/usr/share/container-scripts/redis/password.conf.template new file mode 100644 index 0000000..bd2eef3 --- /dev/null +++ b/root/usr/share/container-scripts/redis/password.conf.template @@ -0,0 +1,3 @@ +# password for the server +requirepass "${REDIS_PASSWORD}" + diff --git a/root/usr/share/container-scripts/redis/post-init.sh b/root/usr/share/container-scripts/redis/post-init.sh new file mode 100644 index 0000000..5ee2c96 --- /dev/null +++ b/root/usr/share/container-scripts/redis/post-init.sh @@ -0,0 +1,6 @@ +# This file serves for extending the container image, typically by changing +# the configuration, loading some data etc. + +# Feel free to add content to this file or rewrite it at all. +# You may also start redis server locally to load some data for example, +# but do not forget to stop it after it, so it can be restarted after it. diff --git a/root/usr/share/container-scripts/redis/scl_enable b/root/usr/share/container-scripts/redis/scl_enable new file mode 100644 index 0000000..5a25432 --- /dev/null +++ b/root/usr/share/container-scripts/redis/scl_enable @@ -0,0 +1,3 @@ +# This will make scl collection binaries work out of box. +unset BASH_ENV PROMPT_COMMAND ENV +source scl_source enable ${ENABLED_COLLECTIONS} diff --git a/root/usr/share/container-scripts/redis/validate-variables.sh b/root/usr/share/container-scripts/redis/validate-variables.sh new file mode 100644 index 0000000..57138a4 --- /dev/null +++ b/root/usr/share/container-scripts/redis/validate-variables.sh @@ -0,0 +1,15 @@ +function usage() { + [ $# == 1 ] && echo "error: $1" + echo "You can specify the following environment variables:" + echo " REDIS_PASSWORD (regex: '$redis_password_regex')" + exit 1 +} + +function validate_variables() { + # Check basic sanity of specified variables + if [[ -v REDIS_PASSWORD ]]; then + [[ "$REDIS_PASSWORD" =~ $redis_password_regex ]] || usage "Invalid password" + fi +} + +validate_variables diff --git a/sources b/sources new file mode 100644 index 0000000..e69de29 diff --git a/test/run b/test/run new file mode 100755 index 0000000..c13b261 --- /dev/null +++ b/test/run @@ -0,0 +1,290 @@ +#!/bin/bash +# +# Test the Redis image. +# +# IMAGE_NAME specifies the name of the candidate image used for testing. +# The image has to be available before this script is executed. +# + +set -o errexit +set -o nounset +shopt -s nullglob + +[ "${DEBUG:-0}" -eq 1 ] && set -x + +test -n "${IMAGE_NAME-}" || { echo 'make sure $IMAGE_NAME is defined' && false ;} +test -n "${VERSION-}" || { echo 'make sure $VERSION is defined' && false; } +test -n "${OS-}" || { echo 'make sure $OS is defined' && false; } + +test_exit=1 + +CIDFILE_DIR=$(mktemp --suffix=redis_test_cidfiles -d) + +function cleanup() { + local cidfile + for cidfile in $CIDFILE_DIR/* ; do + local CONTAINER + CONTAINER=$(cat $cidfile) + + echo "Stopping and removing container $CONTAINER..." + docker stop $CONTAINER >/dev/null + local exit_status + exit_status=$(docker inspect -f '{{.State.ExitCode}}' $CONTAINER) + if [ "$exit_status" != "0" ]; then + echo "Inspecting container $CONTAINER" + docker inspect $CONTAINER + echo "Dumping logs for $CONTAINER" + docker logs $CONTAINER + fi + docker rm -v $CONTAINER >/dev/null + rm $cidfile + echo "Done." + done + rmdir $CIDFILE_DIR + + # Report whole test result + if [ "$test_exit" -eq 0 ] ; then + echo "Test succeeded." + else + echo "Test failed." + fi + exit $test_exit +} +trap cleanup EXIT SIGINT + +function get_cid() { + local id="$1" ; shift || return 1 + echo $(cat "$CIDFILE_DIR/$id") +} + +function get_container_ip() { + local id="$1" ; shift + docker inspect --format='{{.NetworkSettings.IPAddress}}' $(get_cid "$id") +} + +function connection_works() { + local container_ip="$1"; shift + local password="$1"; shift + if [ "$(redis_cmd "$container_ip" "$password" ping)" == "PONG" ] ; then + return 0 + fi + return 1 +} + +function redis_cmd() { + local container_ip="$1"; shift + local password="$1"; shift + # if empty password is given, then no password will be specified + docker run --rm "$IMAGE_NAME" redis-cli -h "$container_ip" ${password:+-a "$password"} "$@" +} + +function test_connection() { + local name=$1 ; shift + local password=$1 ; shift + local ip + ip=$(get_container_ip $name) + echo " Testing Redis connection to $ip (password='${password:-}')..." + local max_attempts=10 + local sleep_time=2 + local i + for i in $(seq $max_attempts); do + echo " Trying to connect..." + if connection_works "$ip" "$password" ; then + echo " Success!" + echo + return 0 + fi + sleep $sleep_time + done + echo " Giving up: Failed to connect. Logs:" + docker logs $(get_cid $name) + return 1 +} + +function test_redis() { + local container_ip="$1" + local password="$2" + + echo " Testing Redis (password='${password:-}')" + redis_cmd "$container_ip" "$password" set a 1 >/dev/null + redis_cmd "$container_ip" "$password" set b 2 >/dev/null + test "$(redis_cmd "$container_ip" "$password" get b)" == '2' + echo " Success!" + echo +} + +function create_container() { + local name=$1 ; shift + cidfile="$CIDFILE_DIR/$name" + # create container with a cidfile in a directory for cleanup + local container_id + [ "${DEBUG:-0}" -eq 1 ] && echo "DEBUG: docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d \"$@\" $IMAGE_NAME ${CONTAINER_ARGS:-}" >&2 + container_id="$(docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d "$@" $IMAGE_NAME ${CONTAINER_ARGS:-})" + [ "${DEBUG:-0}" -eq 1 ] && echo "Created container $container_id" + return 0 +} + +function run_change_password_test() { + local tmpdir=$(mktemp -d) + mkdir "${tmpdir}/data" && chmod -R a+rwx "${tmpdir}" + + # Create Redis container with persistent volume and set the initial password + create_container "testpass1" -e REDIS_PASSWORD=foo \ + -v ${tmpdir}:/var/lib/redis/data:Z + test_connection testpass1 foo + docker stop $(get_cid testpass1) >/dev/null + + # Create second container with changed password + create_container "testpass2" -e REDIS_PASSWORD=bar \ + -v ${tmpdir}:/var/lib/redis/data:Z + test_connection testpass2 bar + + # The old password should not work anymore + container_ip="$(get_container_ip testpass2)" + if connection_works "$container_ip" foo ; then + return 1 + fi +} + +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 + fi + else + if ! $success ; then + echo " Connection ($PASS) access denied as expected" + return + fi + fi + echo " Connection ($PASS) login assertion failed" + exit 1 +} + +function assert_local_access() { + local id="$1" ; shift + docker exec $(get_cid "$id") bash -c 'redis-cli ping' +} + +# Make sure the invocation of docker run fails. +function assert_container_creation_fails() { + + # Time the docker run command. It should fail. If it doesn't fail, + # redis will keep running so we kill it with SIGKILL to make sure + # timeout returns a non-zero value. + local ret=0 + timeout -s 9 --preserve-status 60s docker run --rm "$@" $IMAGE_NAME >/dev/null || ret=$? + + # Timeout will exit with a high number. + if [ $ret -gt 10 ]; then + return 1 + fi +} + +function try_image_invalid_combinations() { + assert_container_creation_fails -e REDIS_PASSWORD="pass with space" "$@" +} + +function run_container_creation_tests() { + echo " Testing image entrypoint usage" + try_image_invalid_combinations + echo " Success!" + echo +} + +test_scl_usage() { + local name="$1" + local run_cmd="$2" + local expected="$3" + + echo " Testing the image SCL enable" + local out + out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}") + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi + out=$(docker exec $(get_cid $name) /bin/bash -c "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi + out=$(docker exec $(get_cid $name) /bin/sh -ic "${run_cmd}" 2>&1) + if ! echo "${out}" | grep -q "${expected}"; then + echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'" + return 1 + fi +} + +run_doc_test() { + local tmpdir=$(mktemp -d) + local f + echo " Testing documentation in the container image" + # Extract the help.1 file from the container + docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /help.1" >${tmpdir}/help.1 + # Check whether the help.1 file includes some important information + for term in 6379 "REDIS\_PASSWORD" volume; do + if ! cat ${tmpdir}/help.1 | grep -F -q -e "${term}" ; then + echo "ERROR: File /help.1 does not include '${term}'." + return 1 + fi + done + # Check whether the file uses the correct format + if ! file ${tmpdir}/help.1 | grep -q roff ; then + echo "ERROR: /help.1 is not in troff or groff format" + return 1 + fi + echo " Success!" + echo +} + +function run_tests() { + local name=$1 ; shift + envs=${PASS:+"-e REDIS_PASSWORD=$PASS"} + PASS=${PASS:-} + create_container $name $envs + test_connection "$name" "$PASS" + # Only check version on rhel/centos builds + if [ "$OS" != "fedora" ]; then + echo " Testing scl usage" + test_scl_usage $name 'redis-server --version' "$VERSION" + fi + echo " Testing login accesses" + local container_ip + container_ip=$(get_container_ip $name) + assert_login_access "$container_ip" "$PASS" true + if [ -n "$PASS" ] ; then + assert_login_access "$container_ip" "${PASS}_foo" false + fi + assert_local_access "$name" + echo " Success!" + echo + test_redis "$container_ip" "$PASS" +} + +# Tests. + +# Test whether documentation is part of the image +run_container_creation_tests + +# Normal tests without password +run_tests no_pass +# Normal tests with password +PASS=pass run_tests no_root +# Test with arbitrary uid for the container without password +DOCKER_ARGS="-u 12345" run_tests no_pass_altuid +# Test with arbitrary uid for the container with password +DOCKER_ARGS="-u 12345" PASS=pass run_tests no_root_altuid + +# Test the password change +run_change_password_test + +# Test whether documentation is part of the image +run_doc_test + +test_exit=0