mirror of
https://github.com/docker-mailserver/docker-mailserver
synced 2024-12-18 14:04:17 +01:00
47f8d50beb
* chore: Detect missing final newline in configs read These lines will be not be processed by `read`, emit a warning to raise awareness. * fix: Ensure parsed config has final newline appended (when possible) This functionality was handled in `accounts.sh` via a similar sed command (that the linked references also offer). `printf` is better for this, no shellcheck comment required either. We additionally don't attempt to modify files that are read-only. * fix: Ensure parsed configs have CRLF to LF corrected (where possible) Likewise, this runtime fix was only covering two config files. It now applies to all callers of this method. * fix: Sanitize `postfix-master.cf` via helper This feature should have been using the helper to avoid user error from their config updates accidentally introducing subtle breakage implicitly (due to CRLF or missing final newline). * tests: Add test cases for new helpers * tests: `rm` is redundant when using `BATS_TEST_TMPDIR` This temporary directory is created and removed implicitly. Even after a test failure. * chore: Remove old `postfix-virtual.cf` migration logic This was introduced in 2018, there should be no one needing to rely on this anymore? * tests: Remove comment on sed failure concern * chore: Add entry to `CHANGELOG.md` * Apply suggestions from code review Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> --------- Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
204 lines
6.9 KiB
Bash
204 lines
6.9 KiB
Bash
#!/bin/bash
|
|
|
|
# ? ABOUT: Functions defined here should be used when initializing tests.
|
|
|
|
# ! ATTENTION: Functions prefixed with `__` are intended for internal use within this file only, not in tests.
|
|
# ! ATTENTION: This script must not use functions from `common.bash` to
|
|
# ! avoid dependency hell.
|
|
|
|
# ! -------------------------------------------------------------------
|
|
# ? >> Miscellaneous initialization functionality
|
|
|
|
# Does pre-flight checks for each test: check whether certain required variables
|
|
# are set and exports other variables.
|
|
#
|
|
# ## Note
|
|
#
|
|
# This function is internal and should not be used in tests.
|
|
function __initialize_variables() {
|
|
function __check_if_set() {
|
|
if [[ ${!1+set} != 'set' ]]; then
|
|
echo "ERROR: (helper/setup.sh) '${1:?No variable name given to __check_if_set}' is not set" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
local REQUIRED_VARIABLES_FOR_TESTS=(
|
|
'REPOSITORY_ROOT'
|
|
'IMAGE_NAME'
|
|
'CONTAINER_NAME'
|
|
)
|
|
|
|
for VARIABLE in "${REQUIRED_VARIABLES_FOR_TESTS[@]}"; do
|
|
__check_if_set "${VARIABLE}"
|
|
done
|
|
|
|
export SETUP_FILE_MARKER TEST_TIMEOUT_IN_SECONDS NUMBER_OF_LOG_LINES
|
|
SETUP_FILE_MARKER="${BATS_TMPDIR:?}/$(basename "${BATS_TEST_FILENAME:?}").setup_file"
|
|
TEST_TIMEOUT_IN_SECONDS=${TEST_TIMEOUT_IN_SECONDS:-120}
|
|
NUMBER_OF_LOG_LINES=${NUMBER_OF_LOG_LINES:-10}
|
|
}
|
|
|
|
# ? << Miscellaneous initialization functionality
|
|
# ! -------------------------------------------------------------------
|
|
# ? >> File setup
|
|
|
|
# Print the private config path for the given container or test file,
|
|
# if no container name was given.
|
|
#
|
|
# @param ${1} = container name [OPTIONAL]
|
|
function _print_private_config_path() {
|
|
local TARGET_NAME=${1:-$(basename "${BATS_TEST_FILENAME}")}
|
|
echo "${REPOSITORY_ROOT}/test/duplicate_configs/${TARGET_NAME}"
|
|
}
|
|
|
|
|
|
# Create a dedicated configuration directory for a test file.
|
|
#
|
|
# @param ${1} = relative source in test/config folder
|
|
# @param ${2} = (optional) container name, defaults to ${BATS_TEST_FILENAME}
|
|
# @return = path to the folder where the config is duplicated
|
|
function _duplicate_config_for_container() {
|
|
local OUTPUT_FOLDER
|
|
OUTPUT_FOLDER=$(_print_private_config_path "${2}")
|
|
|
|
if [[ -z ${OUTPUT_FOLDER} ]]; then
|
|
echo "'OUTPUT_FOLDER' in '_duplicate_config_for_container' is empty" >&2
|
|
return 1
|
|
fi
|
|
|
|
rm -rf "${OUTPUT_FOLDER:?}/"
|
|
mkdir -p "${OUTPUT_FOLDER}"
|
|
cp -r "${REPOSITORY_ROOT}/test/config/${1:?}/." "${OUTPUT_FOLDER}" || return $?
|
|
|
|
echo "${OUTPUT_FOLDER}"
|
|
}
|
|
|
|
# Common defaults appropriate for most tests.
|
|
#
|
|
# Override variables in test cases within a file when necessary:
|
|
# - Use `export <VARIABLE>` in `setup_file()` to overrides for all test cases.
|
|
# - Use `local <VARIABLE>` to override within a specific test case.
|
|
#
|
|
# ## Attenton
|
|
#
|
|
# The ENV `CONTAINER_NAME` must be set before this method is called. It only affects the
|
|
# `TEST_TMP_CONFIG` directory created, but will be used in `common_container_create()`
|
|
# and implicitly in other helper methods.
|
|
#
|
|
# ## Example
|
|
#
|
|
# For example, if you need an immutable config volume that can't be affected by other tests
|
|
# in the file, then use `local TEST_TMP_CONFIG=$(_duplicate_config_for_container . "${UNIQUE_ID_HERE}")`
|
|
function _init_with_defaults() {
|
|
__initialize_variables
|
|
|
|
export TEST_TMP_CONFIG
|
|
TEST_TMP_CONFIG=$(_duplicate_config_for_container . "${CONTAINER_NAME}")
|
|
|
|
# Common complimentary test files, read-only safe to share across containers:
|
|
export TEST_FILES_CONTAINER_PATH='/tmp/docker-mailserver-test'
|
|
export TEST_FILES_VOLUME="${REPOSITORY_ROOT}/test/files:${TEST_FILES_CONTAINER_PATH}:ro"
|
|
|
|
# The config volume cannot be read-only as some data needs to be written at container startup
|
|
#
|
|
# - dovecot-quotas.cf (setup-stack.sh:_setup_dovecot_quotas)
|
|
# - postfix-aliases.cf (setup-stack.sh:_setup_postfix_aliases)
|
|
# TODO: Check how many tests need write access. Consider using `docker create` + `docker cp` for easier cleanup.
|
|
export TEST_CONFIG_VOLUME="${TEST_TMP_CONFIG}:/tmp/docker-mailserver"
|
|
|
|
# Default Root CA cert used in TLS tests with `openssl` commands:
|
|
export TEST_CA_CERT="${TEST_FILES_CONTAINER_PATH}/ssl/example.test/with_ca/ecdsa/ca-cert.ecdsa.pem"
|
|
}
|
|
|
|
|
|
# ? << File setup
|
|
# ! -------------------------------------------------------------------
|
|
# ? >> Container startup
|
|
|
|
# Waits until the container has finished starting up.
|
|
#
|
|
# @param ${1} = container name
|
|
#
|
|
# TODO: Should also fail early on "docker logs ${1} | egrep '^[ FATAL ]'"?
|
|
function _wait_for_finished_setup_in_container() {
|
|
local TARGET_CONTAINER_NAME=${1:?Container name must be provided}
|
|
local STATUS=0
|
|
_repeat_until_success_or_timeout \
|
|
--fatal-test "_container_is_running ${1}" \
|
|
"${TEST_TIMEOUT_IN_SECONDS}" \
|
|
bash -c "docker logs ${TARGET_CONTAINER_NAME} | grep 'is up and running'" || STATUS=1
|
|
|
|
if [[ ${STATUS} -eq 1 ]]; then
|
|
echo "Last ${NUMBER_OF_LOG_LINES} lines of container (${TARGET_CONTAINER_NAME}) log"
|
|
docker logs "${1}" | tail -n "${NUMBER_OF_LOG_LINES}"
|
|
fi
|
|
|
|
return "${STATUS}"
|
|
}
|
|
|
|
# Uses `docker create` to create a container with proper defaults without starting it instantly.
|
|
#
|
|
# @param ${1} = Pass an array by it's variable name as a string; it will be used as a
|
|
# reference for appending extra config into the `docker create` below [OPTIONAL]
|
|
#
|
|
# ## Note
|
|
#
|
|
# Using array reference for a single input parameter, as this method is still
|
|
# under development while adapting tests to it and requirements it must serve
|
|
# (eg: support base config matrix in CI)
|
|
function _common_container_create() {
|
|
[[ -n ${1} ]] && local -n X_EXTRA_ARGS=${1}
|
|
|
|
run docker create \
|
|
--tty \
|
|
--name "${CONTAINER_NAME}" \
|
|
--hostname "${TEST_FQDN:-mail.example.test}" \
|
|
--volume "${TEST_FILES_VOLUME}" \
|
|
--volume "${TEST_CONFIG_VOLUME}" \
|
|
--env ENABLE_AMAVIS=0 \
|
|
--env ENABLE_CLAMAV=0 \
|
|
--env ENABLE_UPDATE_CHECK=0 \
|
|
--env ENABLE_SPAMASSASSIN=0 \
|
|
--env ENABLE_FAIL2BAN=0 \
|
|
--env POSTFIX_INET_PROTOCOLS=ipv4 \
|
|
--env DOVECOT_INET_PROTOCOLS=ipv4 \
|
|
--env LOG_LEVEL=debug \
|
|
"${X_EXTRA_ARGS[@]}" \
|
|
"${IMAGE_NAME}"
|
|
|
|
assert_success
|
|
}
|
|
|
|
# Starts a container given by it's name.
|
|
# Uses `CONTAINER_NAME` as the name for the `docker start` command.
|
|
#
|
|
# ## Attenton
|
|
#
|
|
# The ENV `CONTAINER_NAME` must be set before this method is called.
|
|
function _common_container_start() {
|
|
run docker start "${CONTAINER_NAME:?Container name must be set}"
|
|
assert_success
|
|
}
|
|
|
|
# Using `create` and `start` instead of only `run` allows to modify
|
|
# the container prior to starting it. Otherwise use this combined method.
|
|
#
|
|
# ## Note
|
|
#
|
|
# This function forwards all arguments to `_common_container_create` at present.
|
|
function _common_container_setup() {
|
|
_common_container_create "${@}"
|
|
_common_container_start
|
|
|
|
_wait_for_finished_setup_in_container "${CONTAINER_NAME}"
|
|
}
|
|
|
|
# Can be used in BATS' `teardown_file` function as a default value.
|
|
#
|
|
# @param ${1} = container name [OPTIONAL]
|
|
function _default_teardown() {
|
|
local TARGET_CONTAINER_NAME=${1:-${CONTAINER_NAME}}
|
|
docker rm -f "${TARGET_CONTAINER_NAME}"
|
|
}
|