359 lines
9.3 KiB
Bash
Executable file
359 lines
9.3 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# Test the Redis image.
|
|
#
|
|
# IMAGE_NAME specifies the name of the candidate image used for testing.
|
|
# The image has to be available before this script is executed.
|
|
#
|
|
|
|
set -o errexit
|
|
set -o nounset
|
|
shopt -s nullglob
|
|
|
|
[ "${DEBUG:-0}" -eq 1 ] && set -x
|
|
|
|
test -n "${IMAGE_NAME-}" || { echo 'make sure $IMAGE_NAME is defined' && false ;}
|
|
test -n "${VERSION-}" || { echo 'make sure $VERSION is defined' && false; }
|
|
test -n "${OS-}" || { echo 'make sure $OS is defined' && false; }
|
|
|
|
test_short_summary=''
|
|
TESTSUITE_RESULT=0
|
|
|
|
TEST_LIST="\
|
|
run_container_creation_tests
|
|
run_tests_no_root
|
|
run_tests_no_pass
|
|
run_tests_no_pass_altuid
|
|
run_tests_no_root_altuid
|
|
run_change_password_test
|
|
run_doc_test
|
|
"
|
|
|
|
CIDFILE_DIR=$(mktemp --suffix=redis_test_cidfiles -d)
|
|
|
|
function cleanup() {
|
|
local cidfile
|
|
for cidfile in $CIDFILE_DIR/* ; do
|
|
local CONTAINER
|
|
CONTAINER=$(cat $cidfile)
|
|
|
|
echo "Stopping and removing container $CONTAINER..."
|
|
docker stop $CONTAINER >/dev/null
|
|
local exit_status
|
|
exit_status=$(docker inspect -f '{{.State.ExitCode}}' $CONTAINER)
|
|
if [ "$exit_status" != "0" ]; then
|
|
echo "Inspecting container $CONTAINER"
|
|
docker inspect $CONTAINER
|
|
echo "Dumping logs for $CONTAINER"
|
|
docker logs $CONTAINER
|
|
fi
|
|
docker rm -v $CONTAINER >/dev/null
|
|
rm $cidfile
|
|
echo "Done."
|
|
done
|
|
rmdir $CIDFILE_DIR
|
|
|
|
echo "$test_short_summary"
|
|
|
|
if [ $TESTSUITE_RESULT -eq 0 ] ; then
|
|
echo "Tests for ${IMAGE_NAME} succeeded."
|
|
else
|
|
echo "Tests for ${IMAGE_NAME} failed."
|
|
fi
|
|
exit $TESTSUITE_RESULT
|
|
}
|
|
trap cleanup EXIT SIGINT
|
|
|
|
check_result() {
|
|
local result="$1"
|
|
if [[ "$result" != "0" ]]; then
|
|
TESTCASE_RESULT=1
|
|
fi
|
|
return $result
|
|
}
|
|
|
|
function get_cid() {
|
|
local id="$1" ; shift || return 1
|
|
echo $(cat "$CIDFILE_DIR/$id")
|
|
}
|
|
|
|
function get_container_ip() {
|
|
local id="$1" ; shift
|
|
docker inspect --format='{{.NetworkSettings.IPAddress}}' $(get_cid "$id")
|
|
}
|
|
|
|
function connection_works() {
|
|
local container_ip="$1"; shift
|
|
local password="$1"; shift
|
|
if [ "$(redis_cmd "$container_ip" "$password" ping)" == "PONG" ] ; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
function redis_cmd() {
|
|
local container_ip="$1"; shift
|
|
local password="$1"; shift
|
|
# if empty password is given, then no password will be specified
|
|
docker run --rm "$IMAGE_NAME" redis-cli -h "$container_ip" ${password:+-a "$password"} "$@"
|
|
}
|
|
|
|
function test_connection() {
|
|
local name=$1 ; shift
|
|
local password=$1 ; shift
|
|
local ip
|
|
ip=$(get_container_ip $name)
|
|
echo " Testing Redis connection to $ip (password='${password:-}')..."
|
|
local max_attempts=10
|
|
local sleep_time=2
|
|
local i
|
|
for i in $(seq $max_attempts); do
|
|
echo " Trying to connect..."
|
|
if connection_works "$ip" "$password" ; then
|
|
echo " Success!"
|
|
echo
|
|
return 0
|
|
fi
|
|
sleep $sleep_time
|
|
done
|
|
echo " Giving up: Failed to connect. Logs:"
|
|
docker logs $(get_cid $name)
|
|
return 1
|
|
}
|
|
|
|
function test_redis() {
|
|
local container_ip="$1"
|
|
local password="$2"
|
|
|
|
echo " Testing Redis (password='${password:-}')"
|
|
redis_cmd "$container_ip" "$password" set a 1 >/dev/null
|
|
check_result $?
|
|
redis_cmd "$container_ip" "$password" set b 2 >/dev/null
|
|
check_result $?
|
|
test "$(redis_cmd "$container_ip" "$password" get b)" == '2'
|
|
echo " Success!"
|
|
echo
|
|
}
|
|
|
|
function create_container() {
|
|
local name=$1 ; shift
|
|
cidfile="$CIDFILE_DIR/$name"
|
|
# create container with a cidfile in a directory for cleanup
|
|
local container_id
|
|
[ "${DEBUG:-0}" -eq 1 ] && echo "DEBUG: docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d \"$@\" $IMAGE_NAME ${CONTAINER_ARGS:-}" >&2
|
|
container_id="$(docker run ${DOCKER_ARGS:-} --cidfile $cidfile -d "$@" $IMAGE_NAME ${CONTAINER_ARGS:-})"
|
|
[ "${DEBUG:-0}" -eq 1 ] && echo "Created container $container_id"
|
|
[ x"$container_id" == "x" ] && return 1 || return 0
|
|
}
|
|
|
|
function run_change_password_test() {
|
|
local tmpdir=$(mktemp -d)
|
|
mkdir "${tmpdir}/data" && chmod -R a+rwx "${tmpdir}"
|
|
|
|
# Create Redis container with persistent volume and set the initial password
|
|
create_container "testpass1" -e REDIS_PASSWORD=foo \
|
|
-v ${tmpdir}:/var/lib/redis/data:Z
|
|
check_result $?
|
|
test_connection testpass1 foo
|
|
check_result $?
|
|
docker stop $(get_cid testpass1) >/dev/null
|
|
|
|
# Create second container with changed password
|
|
create_container "testpass2" -e REDIS_PASSWORD=bar \
|
|
-v ${tmpdir}:/var/lib/redis/data:Z
|
|
check_result $?
|
|
test_connection testpass2 bar
|
|
check_result $?
|
|
# The old password should not work anymore
|
|
container_ip="$(get_container_ip testpass2)"
|
|
connection_works "$container_ip" foo
|
|
check_result $?
|
|
}
|
|
|
|
function assert_login_access() {
|
|
local container_ip=$1; shift
|
|
local PASS=$1 ; shift
|
|
local success=$1 ; shift
|
|
|
|
if connection_works "$container_ip" "$PASS" ; then
|
|
if $success ; then
|
|
echo " Connection ($PASS) access granted as expected"
|
|
return 0
|
|
fi
|
|
else
|
|
if ! $success ; then
|
|
echo " Connection ($PASS) access denied as expected"
|
|
return 0
|
|
fi
|
|
fi
|
|
echo " Connection ($PASS) login assertion failed"
|
|
return 1
|
|
}
|
|
|
|
function assert_local_access() {
|
|
local id="$1" ; shift
|
|
docker exec $(get_cid "$id") bash -c 'redis-cli ping'
|
|
}
|
|
|
|
# Make sure the invocation of docker run fails.
|
|
function assert_container_creation_fails() {
|
|
|
|
# Time the docker run command. It should fail. If it doesn't fail,
|
|
# redis will keep running so we kill it with SIGKILL to make sure
|
|
# timeout returns a non-zero value.
|
|
local ret=0
|
|
timeout -s 9 --preserve-status 60s docker run --rm "$@" $IMAGE_NAME >/dev/null || ret=$?
|
|
|
|
# Timeout will exit with a high number.
|
|
if [ $ret -gt 10 ]; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function try_image_invalid_combinations() {
|
|
assert_container_creation_fails -e REDIS_PASSWORD="pass with space" "$@"
|
|
check_result $?
|
|
}
|
|
|
|
function run_container_creation_tests() {
|
|
local ret
|
|
echo " Testing image entrypoint usage"
|
|
try_image_invalid_combinations
|
|
ret=$?
|
|
if [ $ret -eq 0 ]; then
|
|
echo " Success!"
|
|
else
|
|
echo " Failed!"
|
|
fi
|
|
echo
|
|
return $ret
|
|
}
|
|
|
|
test_scl_usage() {
|
|
local name="$1"
|
|
local run_cmd="$2"
|
|
local expected="$3"
|
|
|
|
echo " Testing the image SCL enable"
|
|
local out
|
|
out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}")
|
|
if ! echo "${out}" | grep -q "${expected}"; then
|
|
echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'"
|
|
return 1
|
|
fi
|
|
out=$(docker exec $(get_cid $name) /bin/bash -c "${run_cmd}" 2>&1)
|
|
if ! echo "${out}" | grep -q "${expected}"; then
|
|
echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'"
|
|
return 1
|
|
fi
|
|
out=$(docker exec $(get_cid $name) /bin/sh -ic "${run_cmd}" 2>&1)
|
|
if ! echo "${out}" | grep -q "${expected}"; then
|
|
echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
run_doc_test() {
|
|
local tmpdir=$(mktemp -d)
|
|
local f
|
|
echo " Testing documentation in the container image"
|
|
# Extract the help.1 file from the container
|
|
docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /help.1" >${tmpdir}/help.1
|
|
# Check whether the help.1 file includes some important information
|
|
for term in 6379 "REDIS\_PASSWORD" volume; do
|
|
if ! cat ${tmpdir}/help.1 | grep -F -q -e "${term}" ; then
|
|
echo "ERROR: File /help.1 does not include '${term}'."
|
|
return 1
|
|
fi
|
|
done
|
|
# Check whether the file uses the correct format
|
|
if ! file ${tmpdir}/help.1 | grep -q roff ; then
|
|
echo "ERROR: /help.1 is not in troff or groff format"
|
|
return 1
|
|
fi
|
|
echo " Success!"
|
|
echo
|
|
}
|
|
|
|
function run_tests() {
|
|
local name=$1 ; shift
|
|
local ret
|
|
envs=${PASS:+"-e REDIS_PASSWORD=$PASS"}
|
|
PASS=${PASS:-}
|
|
create_container $name $envs
|
|
ret=$?
|
|
check_result $ret
|
|
test_connection "$name" "$PASS"
|
|
ret=$?
|
|
check_result $ret
|
|
# Only check version on rhel/centos builds
|
|
if [ "$OS" != "fedora" ]; then
|
|
echo " Testing scl usage"
|
|
test_scl_usage $name 'redis-server --version' "$VERSION"
|
|
check_result $?
|
|
fi
|
|
echo " Testing login accesses"
|
|
local container_ip
|
|
container_ip=$(get_container_ip $name)
|
|
assert_login_access "$container_ip" "$PASS" true
|
|
ret=$?
|
|
check_result $ret
|
|
if [ -n "$PASS" ] ; then
|
|
assert_login_access "$container_ip" "${PASS}_foo" false
|
|
check_result $?
|
|
fi
|
|
assert_local_access "$name"
|
|
ret=$?
|
|
check_result $ret
|
|
if [ $ret -ne 0 ]; then
|
|
echo " Local access FAILED."
|
|
else
|
|
echo " Local access SUCCESS."
|
|
fi
|
|
echo
|
|
test_redis "$container_ip" "$PASS"
|
|
check_result $?
|
|
}
|
|
|
|
function run_tests_no_root() {
|
|
# Normal tests with password
|
|
PASS=pass run_tests no_root
|
|
}
|
|
|
|
function run_tests_no_pass() {
|
|
# Normal tests without password
|
|
run_tests no_pass
|
|
}
|
|
|
|
function run_tests_no_pass_altuid() {
|
|
# Test with arbitrary uid for the container without password
|
|
DOCKER_ARGS="-u 12345" run_tests no_pass_altuid
|
|
}
|
|
|
|
function run_tests_no_root_altuid() {
|
|
# Test with arbitrary uid for the container with password
|
|
DOCKER_ARGS="-u 12345" PASS=pass run_tests no_root_altuid
|
|
}
|
|
|
|
function run_all_tests() {
|
|
for test_case in $TEST_SET; do
|
|
echo "Running test $test_case ...."
|
|
TESTCASE_RESULT=0
|
|
$test_case
|
|
check_result $?
|
|
local test_msg
|
|
if [ $TESTCASE_RESULT -eq 0 ]; then
|
|
test_msg="[PASSED]"
|
|
else
|
|
test_msg="[FAILED]"
|
|
TESTSUITE_RESULT=1
|
|
fi
|
|
printf -v test_short_summary "%s %s for '%s' %s\n" "${test_short_summary}" "${test_msg}" "$test_case"
|
|
[ -n "${FAIL_QUICKLY:-}" ] && cleanup "${APP_NAME}" && return 1
|
|
done;
|
|
}
|
|
|
|
TEST_SET=${TESTS:-$TEST_LIST} run_all_tests
|
|
|
|
echo "Success!"
|
|
cleanup
|