423 lines
13 KiB
Bash
Executable file
423 lines
13 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# Test the MongoDB image.
|
|
#
|
|
# IMAGE_NAME specifies a name of the candidate image used for testing.
|
|
# The image has to be available before this script is executed.
|
|
#
|
|
|
|
set -exo nounset
|
|
shopt -s nullglob
|
|
|
|
IMAGE_NAME=${IMAGE_NAME-centos/mongodb-32-centos7-candidate}
|
|
|
|
CIDFILE_DIR=$(mktemp --suffix=mongodb_test_cidfiles -d)
|
|
|
|
function cleanup() {
|
|
for cidfile in $CIDFILE_DIR/* ; do
|
|
CONTAINER=$(cat $cidfile)
|
|
|
|
echo "Stopping and removing container $CONTAINER..."
|
|
docker stop $CONTAINER
|
|
docker rm $CONTAINER
|
|
rm $cidfile
|
|
echo "Done."
|
|
done
|
|
rmdir $CIDFILE_DIR
|
|
}
|
|
trap cleanup EXIT SIGINT
|
|
|
|
function get_cid() {
|
|
local id="$1" ; shift || return 1
|
|
echo -n $(cat "$CIDFILE_DIR/$id")
|
|
}
|
|
|
|
function get_container_ip() {
|
|
local id="$1" ; shift
|
|
docker inspect --format='{{.NetworkSettings.IPAddress}}' $(get_cid "$id")
|
|
}
|
|
|
|
function mongo_cmd() {
|
|
docker run --rm $IMAGE_NAME mongo "$DB" --host $CONTAINER_IP -u "$USER" -p"$PASS" --eval "${@}"
|
|
}
|
|
|
|
function mongo_admin_cmd() {
|
|
docker run --rm $IMAGE_NAME mongo admin --host $CONTAINER_IP -u admin -p"$ADMIN_PASS" --eval "${@}"
|
|
}
|
|
|
|
# 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,
|
|
# container will keep running so we kill it with SIGKILL to make sure
|
|
# timeout returns a non-zero value.
|
|
set +e
|
|
timeout -s 9 --preserve-status 10s docker run --rm "$@" $IMAGE_NAME
|
|
ret=$?
|
|
set -e
|
|
|
|
# Timeout will exit with a high number.
|
|
if [ $ret -gt 30 ]; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function run_container_creation_tests() {
|
|
echo " Testing wrong user variables usage"
|
|
|
|
assert_container_creation_fails -e MONGODB_USER=user -e MONGODB_PASSWORD=pass
|
|
assert_container_creation_fails -e MONGODB_DATABASE=db -e MONGODB_PASSWORD=pass
|
|
assert_container_creation_fails -e MONGODB_DATABASE=db -e MONGODB_USER=user
|
|
assert_container_creation_fails -e MONGODB_USER=user -e MONGODB_DATABASE=db -e MONGODB_PASSWORD=pass
|
|
|
|
assert_container_creation_fails -e MONGODB_ADMIN_PASSWORD=Pass -e MONGODB_USER=user
|
|
assert_container_creation_fails -e MONGODB_ADMIN_PASSWORD=Pass -e MONGODB_PASSWORD=pass
|
|
assert_container_creation_fails -e MONGODB_ADMIN_PASSWORD=Pass -e MONGODB_DATABASE=db
|
|
|
|
echo " Success!"
|
|
echo " Testing good user variables usage"
|
|
assert_container_creation_fails -e MONGODB_ADMIN_PASSWORD=Pass || [ $? -eq 1 ]
|
|
assert_container_creation_fails -e MONGODB_ADMIN_PASSWORD=Pass -e MONGODB_USER=user -e MONGODB_DATABASE=db -e MONGODB_PASSWORD=pass || [ $? -eq 1 ]
|
|
echo " Success!"
|
|
}
|
|
|
|
function test_connection() {
|
|
local name=$1 ; shift
|
|
CONTAINER_IP=$(get_container_ip $name)
|
|
echo " Testing MongoDB connection to $CONTAINER_IP..."
|
|
local max_attempts=20
|
|
local sleep_time=2
|
|
for i in $(seq $max_attempts); do
|
|
echo " Trying to connect..."
|
|
set +e
|
|
mongo_cmd "db.getSiblingDB('test_database');"
|
|
status=$?
|
|
set -e
|
|
if [ $status -eq 0 ]; then
|
|
echo " Success!"
|
|
return 0
|
|
fi
|
|
sleep $sleep_time
|
|
done
|
|
echo " Giving up: Failed to connect. Logs:"
|
|
docker logs $(get_cid $name)
|
|
return 1
|
|
}
|
|
|
|
test_scl_usage() {
|
|
local name="$1"
|
|
local run_cmd="$2"
|
|
local expected="$3"
|
|
|
|
echo " Testing the image SCL enable"
|
|
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
|
|
}
|
|
|
|
function test_mongo() {
|
|
echo " Testing MongoDB"
|
|
if [ -v ADMIN_PASS ]; then
|
|
echo " Testing Admin user privileges"
|
|
mongo_admin_cmd "db=db.getSiblingDB('${DB}');db.dropUser('${USER}');"
|
|
mongo_admin_cmd "db=db.getSiblingDB('${DB}');db.createUser({user:'${USER}',pwd:'${PASS}',roles:['readWrite','userAdmin','dbAdmin']});"
|
|
mongo_admin_cmd "db=db.getSiblingDB('${DB}');db.testData.insert({x:0});"
|
|
mongo_cmd "db.createUser({user:'test_user2',pwd:'test_password2',roles:['readWrite']});"
|
|
fi
|
|
echo " Testing user privileges"
|
|
mongo_cmd "db.testData.insert({ y : 1 });"
|
|
mongo_cmd "db.testData.insert({ z : 2 });"
|
|
mongo_cmd "db.testData.find().forEach(printjson);"
|
|
mongo_cmd "db.testData.count();"
|
|
mongo_cmd "db.testData.drop();"
|
|
mongo_cmd "db.dropDatabase();"
|
|
echo " Success!"
|
|
}
|
|
|
|
function test_config_option() {
|
|
local env_var=$1 ; shift
|
|
local env_val=$1 ; shift
|
|
local config_part=$1 ; shift
|
|
|
|
local name="configuration_${env_var}"
|
|
|
|
# If $value is a string, it needs to be in simple quotes ''.
|
|
DOCKER_ARGS="
|
|
-e MONGODB_DATABASE=db
|
|
-e MONGODB_USER=user
|
|
-e MONGODB_PASSWORD=password
|
|
-e MONGODB_ADMIN_PASSWORD=adminPassword
|
|
-e $env_var=${env_val}
|
|
"
|
|
create_container ${name} ${DOCKER_ARGS}
|
|
|
|
# need to set these because `mongo_cmd` relies on global variables
|
|
USER=user
|
|
PASS=password
|
|
DB=db
|
|
|
|
test_connection ${name}
|
|
|
|
# If nothing is found, grep returns 1 and test fails.
|
|
docker exec $(get_cid $name) bash -c "cat /etc/mongod.conf" | grep -q "${config_part}"
|
|
|
|
docker stop $(get_cid ${name})
|
|
}
|
|
|
|
function run_configuration_tests() {
|
|
echo " Testing image configuration settings"
|
|
test_config_option MONGODB_QUIET true "quiet: true"
|
|
echo " Success!"
|
|
}
|
|
|
|
wait_for_cid() {
|
|
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 ] && break
|
|
echo "Waiting for container start..."
|
|
attempt=$(( $attempt + 1 ))
|
|
sleep $sleep_time
|
|
done
|
|
}
|
|
|
|
function create_container() {
|
|
local name=$1 ; shift
|
|
local cargs=${CONTAINER_ARGS:-}
|
|
cid_file="$CIDFILE_DIR/$name"
|
|
# create container with a cidfile in a directory for cleanup
|
|
docker run --cidfile $cid_file -d $cargs "$@" $IMAGE_NAME
|
|
echo "Created container $(cat $cid_file)"
|
|
wait_for_cid
|
|
}
|
|
|
|
function assert_login_access() {
|
|
local USER=$1 ; shift
|
|
local PASS=$1 ; shift
|
|
local success=$1 ; shift
|
|
|
|
if mongo_cmd 'db.version()' ; then
|
|
if $success ; then
|
|
echo " $USER($PASS) access granted as expected"
|
|
return
|
|
fi
|
|
else
|
|
if ! $success ; then
|
|
echo " $USER($PASS) access denied as expected"
|
|
return
|
|
fi
|
|
fi
|
|
echo " $USER($PASS) login assertion failed"
|
|
exit 1
|
|
}
|
|
|
|
function run_tests() {
|
|
local name=$1 ; shift
|
|
envs="-e MONGODB_USER=$USER -e MONGODB_PASSWORD=$PASS -e MONGODB_DATABASE=$DB"
|
|
if [ -v ADMIN_PASS ]; then
|
|
envs="$envs -e MONGODB_ADMIN_PASSWORD=$ADMIN_PASS"
|
|
fi
|
|
create_container $name $envs
|
|
CONTAINER_IP=$(get_container_ip $name)
|
|
test_connection $name
|
|
echo " Testing scl usage"
|
|
test_scl_usage $name 'mongo --version' '3.2'
|
|
test_mongo $name
|
|
}
|
|
|
|
function run_change_password_test() {
|
|
local name="change_password"
|
|
|
|
local database='db'
|
|
local user='user'
|
|
local password='password'
|
|
local admin_password='adminPassword'
|
|
|
|
local volume_dir
|
|
volume_dir=`mktemp -d --tmpdir mongodb-testdata.XXXXX`
|
|
chmod a+rwx ${volume_dir}
|
|
|
|
DOCKER_ARGS="
|
|
-e MONGODB_DATABASE=${database}
|
|
-e MONGODB_USER=${user}
|
|
-e MONGODB_PASSWORD=${password}
|
|
-e MONGODB_ADMIN_PASSWORD=${admin_password}
|
|
-v ${volume_dir}:/var/lib/mongodb/data:Z
|
|
"
|
|
create_container ${name} ${DOCKER_ARGS}
|
|
|
|
# need to set these because `mongo_cmd` relies on global variables
|
|
USER=${user}
|
|
PASS=${password}
|
|
DB=${database}
|
|
|
|
# need this to wait for the container to start up
|
|
CONTAINER_IP=$(get_container_ip ${name})
|
|
test_connection ${name}
|
|
|
|
echo " Testing login"
|
|
|
|
assert_login_access ${user} ${password} true
|
|
DB='admin' assert_login_access 'admin' ${admin_password} true
|
|
|
|
echo " Changing passwords"
|
|
|
|
docker stop $(get_cid ${name})
|
|
DOCKER_ARGS="
|
|
-e MONGODB_DATABASE=${database}
|
|
-e MONGODB_USER=${user}
|
|
-e MONGODB_PASSWORD=NEW_${password}
|
|
-e MONGODB_ADMIN_PASSWORD=NEW_${admin_password}
|
|
-v ${volume_dir}:/var/lib/mongodb/data:Z
|
|
"
|
|
create_container "${name}_NEW" ${DOCKER_ARGS}
|
|
|
|
# need to set this because `mongo_cmd` relies on global variables
|
|
PASS="NEW_${password}"
|
|
|
|
# need this to wait for the container to start up
|
|
CONTAINER_IP=$(get_container_ip "${name}_NEW")
|
|
test_connection "${name}_NEW"
|
|
|
|
echo " Testing login with new passwords"
|
|
|
|
assert_login_access ${user} "NEW_${password}" true
|
|
assert_login_access ${user} ${password} false
|
|
|
|
DB='admin' assert_login_access 'admin' "NEW_${admin_password}" true
|
|
DB='admin' assert_login_access 'admin' ${admin_password} false
|
|
|
|
# need to remove volume_dir with sudo because of permissions of files written
|
|
# by the Docker container
|
|
sudo rm -rf ${volume_dir}
|
|
|
|
echo " Success!"
|
|
}
|
|
|
|
function run_mount_config_test() {
|
|
local name="mount_config"
|
|
echo " Testing config file mount"
|
|
local database='db'
|
|
local user='user'
|
|
local password='password'
|
|
local admin_password='adminPassword'
|
|
|
|
local volume_dir
|
|
volume_dir=`mktemp -d --tmpdir mongodb-testdata.XXXXX`
|
|
chmod a+rwx ${volume_dir}
|
|
config_file=$volume_dir/mongod.conf
|
|
echo "dbpath=/var/lib/mongodb/dbpath
|
|
unixSocketPrefix = /var/lib/mongodb" > $config_file
|
|
chmod a+r ${config_file}
|
|
|
|
DOCKER_ARGS="
|
|
-e MONGODB_DATABASE=${database}
|
|
-e MONGODB_USER=${user}
|
|
-e MONGODB_PASSWORD=${password}
|
|
-e MONGODB_ADMIN_PASSWORD=${admin_password}
|
|
-v ${config_file}:/etc/mongod.conf:Z
|
|
-v ${volume_dir}:/var/lib/mongodb/dbpath:Z
|
|
"
|
|
create_container ${name} ${DOCKER_ARGS}
|
|
|
|
# need to set these because `mongo_cmd` relies on global variables
|
|
USER=${user}
|
|
PASS=${password}
|
|
DB=${database}
|
|
|
|
# need this to wait for the container to start up
|
|
CONTAINER_IP=$(get_container_ip ${name})
|
|
echo " Testing mongod is running"
|
|
test_connection ${name}
|
|
echo " Testing config file works"
|
|
docker exec $(get_cid ${name}) bash -c "test -S /var/lib/mongodb/mongodb-27017.sock"
|
|
|
|
# need to remove volume_dir with sudo because of permissions of files written
|
|
# by the Docker container
|
|
sudo rm -rf ${volume_dir}
|
|
|
|
echo " Success!"
|
|
}
|
|
|
|
run_doc_test() {
|
|
local tmpdir=$(mktemp -d)
|
|
local f
|
|
echo " Testing documentation in the container image"
|
|
# Extract the help files from the container
|
|
for f in /usr/share/container-scripts/mongodb/README.md help.1 ; do
|
|
docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /${f}" >${tmpdir}/$(basename ${f})
|
|
# Check whether the files include some important information
|
|
for term in MONGODB_ADMIN_PASSWORD volume ; do
|
|
if ! cat ${tmpdir}/$(basename ${f}) | grep -q -e "${term}" ; then
|
|
echo "ERROR: File /${f} does not include '${term}'."
|
|
return 1
|
|
fi
|
|
done
|
|
done
|
|
# Check whether the files use 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
|
|
}
|
|
|
|
run_WT_cache_test() {
|
|
local name="WT_cache_size"
|
|
echo " Testing setting of WT cache size"
|
|
local database='db'
|
|
local user='user'
|
|
local password='password'
|
|
local admin_password='adminPassword'
|
|
|
|
DOCKER_ARGS="
|
|
-e MONGODB_DATABASE=${database}
|
|
-e MONGODB_USER=${user}
|
|
-e MONGODB_PASSWORD=${password}
|
|
-e MONGODB_ADMIN_PASSWORD=${admin_password}
|
|
"
|
|
# need to set these because `mongo_cmd` relies on global variables
|
|
USER=${user}
|
|
PASS=${password}
|
|
DB=${database}
|
|
ADMIN_PASS=${admin_password}
|
|
|
|
# minimum is 1G
|
|
create_container "${name}_200M" ${DOCKER_ARGS} -m 200M
|
|
test_connection ${name}_200M
|
|
mongo_admin_cmd "if (db.serverStatus()['wiredTiger']['cache']['maximum bytes configured'] == Math.pow(2,30)){quit(0)}; quit(1)"
|
|
# if greater that 1G, use 60% of (RAM - 1G)
|
|
create_container "${name}_6G" ${DOCKER_ARGS} -m 6G
|
|
test_connection ${name}_6G
|
|
mongo_admin_cmd "if (db.serverStatus()['wiredTiger']['cache']['maximum bytes configured'] == 3*Math.pow(2,30)){quit(0)}; quit(1)"
|
|
|
|
echo " Success!"
|
|
echo
|
|
}
|
|
|
|
|
|
# Tests.
|
|
run_container_creation_tests
|
|
run_configuration_tests
|
|
USER="user1" PASS="pass1" DB="test_db" ADMIN_PASS="r00t" run_tests admin
|
|
# Test with random uid in container
|
|
CONTAINER_ARGS="-u 12345" USER="user1" PASS="pass1" DB="test_db" ADMIN_PASS="r00t" run_tests admin_altuid
|
|
run_change_password_test
|
|
run_mount_config_test
|
|
run_doc_test
|
|
run_WT_cache_test
|