tests(refactor): mail_hostname.bats (#3027)

* chore: Use a common method to check domain and fqdn config

* chore: Shift other test cases into shared test methods

* chore: Add another shared method for checking mail headers

* chore: Add another shared method for checking hostname

* refactor: Improve quality of shared test methods

Based on changes from an earlier closed hostname PR from Oct 2021 with additional revision to use `assert_output` and more thorough checking of values expected in output.

* chore: Move clean shutdown test to `process-check-restart.bats`

This was originally a single test case in `tests.bats` intended for `supervisord` testing.

It seems at some point it got reassigned to a hostname override test container, and then migrated to separate test file for hostname override test by accident.

It now belongs in the correct place again, as hostname config should have nothing to do with a graceful shutdown?

* chore: Prepare for migrating to use `test/helper/setup.bash`

* chore: Rename containers and configured FQDN settings

* chore: Convert to using common container setup helpers

Wait for SMTP port is left at the end to avoid additional start-up delays.

* chore: Use `_run_in_container_bash` helper

* chore: Be more specific on matching mail headers

- I could do multiple container grep calls instead, but opted to match by lines in file. This better ensures values are being matched to the correct lines.
- Renamed the test case descriptions.
- Expanded test coverage of the 4th container as it represents another DNS config, while the 3rd is just the 4th container with the `SRS_DOMAINNAME` env added, no value in more coverage there.

* chore: Remove redundant test coverage in `tests.bats`

These checks are performed in `mail_hostname.bats` with better coverage.

* chore: Move each containers setup into it's own test-case instead

* chore: Re-arrange container name IDs

The original `fqdn-with-subdomain` is now `with-nis-domain` which is more accurate. A new test case will properly cover the default `--hostname` only config that is not a bare domain.

* chore: Re-arrange test cases to align with new ID ordering

This commit just shifts the test cases, no new changes to any content beyond that.

* chore: Add new test case for default config

* chore: Review feedback `_run_in_container_bash` to `_run_in_container`

Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>

* chore: Additional review feedback

- Fix a suggested change bug with quote wrapping an interpolated variable.
- Convert two other `_bash` methods that were missed from review.
- Apply the last two suggested changes from review.

* chore: `_exec_in_container_bash` to `_exec_in_container`

The `| head -n 1` can be dropped if we know for sure it's only one line, which is what we expect. Quotes can then be dropped too.

---------

Co-authored-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
This commit is contained in:
Brennan Kinney 2023-01-30 01:34:14 +13:00 committed by GitHub
parent ed63a6f90a
commit 14829a8459
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 235 additions and 184 deletions

View file

@ -127,6 +127,8 @@ ENV_PROCESS_LIST=(
assert_success
pgrep --full 'fetchmail-2.rc'
assert_success
_should_stop_cleanly
}
# Split into separate test case for the benefit of minimizing CPU + RAM overhead of clamd.
@ -140,6 +142,7 @@ ENV_PROCESS_LIST=(
_common_container_setup 'CONTAINER_ARGS_ENV_CUSTOM'
_should_restart_when_killed 'clamd'
_should_stop_cleanly
}
function _should_restart_when_killed() {
@ -190,3 +193,16 @@ function _check_if_process_is_running() {
# Original output (if any) for assertions
echo "${IS_RUNNING}"
}
# The process manager (supervisord) should perform a graceful shutdown:
# NOTE: Time limit should never be below these configured values:
# - supervisor-app.conf:stopwaitsecs
# - docker-compose.yml:stop_grace_period
function _should_stop_cleanly() {
run docker stop -t 60 "${CONTAINER_NAME}"
assert_success
# Running `docker rm -f` too soon after `docker stop` can result in failure during teardown with:
# "Error response from daemon: removal of container "${CONTAINER_NAME}" is already in progress"
sleep 1
}

View file

@ -1,213 +1,260 @@
load "${REPOSITORY_ROOT}/test/test_helper/common"
load "${REPOSITORY_ROOT}/test/helper/common"
load "${REPOSITORY_ROOT}/test/helper/setup"
BATS_TEST_NAME_PREFIX='[Network - Hostname] '
CONTAINER1_NAME='dms-test_hostname_fqdn-with-subdomain'
CONTAINER2_NAME='dms-test_hostname_bare-domain'
CONTAINER3_NAME='dms-test_hostname_env-override-hostname'
CONTAINER4_NAME='dms-test_hostname_with-nis-domain'
CONTAINER5_NAME='dms-test_hostname_env-srs-domainname'
function setup_file() {
local PRIVATE_CONFIG
# NOTE: Required until postsrsd package updated:
# `--ulimit` is a workaround for some environments when using ENABLE_SRS=1:
# PR 2730: https://github.com/docker-mailserver/docker-mailserver/commit/672e9cf19a3bb1da309e8cea6ee728e58f905366
PRIVATE_CONFIG=$(duplicate_config_for_container . mail_override_hostname)
docker run --rm -d --name mail_override_hostname \
-v "${PRIVATE_CONFIG}":/tmp/docker-mailserver \
-v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \
-e PERMIT_DOCKER=network \
-e ENABLE_SRS=1 \
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
--hostname unknown.domain.tld \
--tty \
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" \
"${NAME}"
function teardown() { _default_teardown ; }
PRIVATE_CONFIG_TWO=$(duplicate_config_for_container . mail_non_subdomain_hostname)
docker run --rm -d --name mail_non_subdomain_hostname \
-v "${PRIVATE_CONFIG_TWO}":/tmp/docker-mailserver \
-v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \
-e PERMIT_DOCKER=network \
-e ENABLE_SRS=1 \
--hostname domain.com \
--tty \
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" \
"${NAME}"
@test "should update configuration correctly (Standard FQDN setup)" {
export CONTAINER_NAME="${CONTAINER1_NAME}"
PRIVATE_CONFIG_THREE=$(duplicate_config_for_container . mail_srs_domainname)
docker run --rm -d --name mail_srs_domainname \
-v "${PRIVATE_CONFIG_THREE}":/tmp/docker-mailserver \
-v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \
-e PERMIT_DOCKER=network \
-e ENABLE_SRS=1 \
-e SRS_DOMAINNAME='srs.my-domain.com' \
--domainname 'my-domain.com' \
--hostname 'mail' \
--tty \
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" \
"${NAME}"
# Should be using the default `--hostname mail.example.test`
local CUSTOM_SETUP_ARGUMENTS=(
--env ENABLE_AMAVIS=1
--env ENABLE_SRS=1
--env PERMIT_DOCKER='container'
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
PRIVATE_CONFIG_FOUR=$(duplicate_config_for_container . mail_domainname)
docker run --rm -d --name mail_domainname \
-v "${PRIVATE_CONFIG_FOUR}":/tmp/docker-mailserver \
-v "$(pwd)/test/test-files":/tmp/docker-mailserver-test:ro \
-e PERMIT_DOCKER=network \
-e ENABLE_SRS=1 \
--domainname 'my-domain.com' \
--hostname 'mail' \
--tty \
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)" \
"${NAME}"
_should_have_expected_hostname 'mail.example.test'
wait_for_smtp_port_in_container mail_override_hostname
wait_for_smtp_port_in_container mail_non_subdomain_hostname
wait_for_smtp_port_in_container mail_srs_domainname
wait_for_smtp_port_in_container mail_domainname
_should_be_configured_to_domainname 'example.test'
_should_be_configured_to_fqdn 'mail.example.test'
docker exec mail_override_hostname /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt"
docker exec mail_non_subdomain_hostname /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt"
_should_have_correct_mail_headers 'mail.example.test' 'example.test'
}
function teardown_file() {
# Running `docker rm -f` too soon after `docker stop` can result in failure during teardown with:
# "Error response from daemon: removal of container mail_domainname is already in progress"
sleep 1
@test "should update configuration correctly (Bare Domain)" {
export CONTAINER_NAME="${CONTAINER2_NAME}"
docker rm -f mail_override_hostname mail_non_subdomain_hostname mail_srs_domainname mail_domainname
local CUSTOM_SETUP_ARGUMENTS=(
--hostname 'bare-domain.test'
--env ENABLE_AMAVIS=1
--env ENABLE_SRS=1
--env PERMIT_DOCKER='container'
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
_should_have_expected_hostname 'bare-domain.test'
_should_be_configured_to_domainname 'bare-domain.test'
# Bare domain configured, thus no subdomain:
_should_be_configured_to_fqdn 'bare-domain.test'
_should_have_correct_mail_headers 'bare-domain.test'
}
@test "checking SRS: SRS_DOMAINNAME is used correctly" {
repeat_until_success_or_timeout 15 docker exec mail_srs_domainname grep "SRS_DOMAIN=srs.my-domain.com" /etc/default/postsrsd
}
@test "should update configuration correctly (ENV OVERRIDE_HOSTNAME)" {
export CONTAINER_NAME="${CONTAINER3_NAME}"
@test "checking SRS: DOMAINNAME is handled correctly" {
repeat_until_success_or_timeout 15 docker exec mail_domainname grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd
}
local CUSTOM_SETUP_ARGUMENTS=(
--hostname 'original.example.test'
--env OVERRIDE_HOSTNAME='mail.override.test'
--env ENABLE_AMAVIS=1
--env ENABLE_SRS=1
--env PERMIT_DOCKER='container'
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
@test "checking configuration: hostname/domainname override: check container hostname is applied correctly" {
run docker exec mail_override_hostname /bin/bash -c "hostname | grep unknown.domain.tld"
assert_success
}
# Should be the original `--hostname` (`hostname -f`), not `OVERRIDE_HOSTNAME`:
_should_have_expected_hostname 'original.example.test'
@test "checking configuration: hostname/domainname override: check overriden hostname is applied to all configs" {
run docker exec mail_override_hostname /bin/bash -c "cat /etc/mailname | grep my-domain.com"
assert_success
_should_be_configured_to_domainname 'override.test'
_should_be_configured_to_fqdn 'mail.override.test'
run docker exec mail_override_hostname /bin/bash -c "postconf -n | grep mydomain | grep my-domain.com"
assert_success
run docker exec mail_override_hostname /bin/bash -c "postconf -n | grep myhostname | grep mail.my-domain.com"
assert_success
run docker exec mail_override_hostname /bin/bash -c "doveconf | grep hostname | grep mail.my-domain.com"
assert_success
run docker exec mail_override_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep AuthservID | grep mail.my-domain.com"
assert_success
run docker exec mail_override_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep TrustedAuthservIDs | grep mail.my-domain.com"
assert_success
run docker exec mail_override_hostname /bin/bash -c "cat /etc/amavis/conf.d/05-node_id | grep myhostname | grep mail.my-domain.com"
assert_success
}
@test "checking configuration: hostname/domainname override: check hostname in postfix HELO message" {
run docker exec mail_override_hostname /bin/bash -c "nc -w 1 0.0.0.0 25 | grep mail.my-domain.com"
assert_success
}
@test "checking configuration: hostname/domainname override: check headers of received mail" {
run docker exec mail_override_hostname /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/new | wc -l | grep 1"
assert_success
run docker exec mail_override_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep mail.my-domain.com"
assert_success
# test whether the container hostname is not found in received mail
run docker exec mail_override_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep unknown.domain.tld"
_should_have_correct_mail_headers 'mail.override.test' 'override.test' 'original.example.test'
# Container hostname should not be found in received mail (due to `OVERRIDE_HOSTNAME`):
_run_in_container grep -R 'original.example.test' /var/mail/localhost.localdomain/user1/new/
assert_failure
}
@test "checking SRS: OVERRIDE_HOSTNAME is handled correctly" {
run docker exec mail_override_hostname grep "SRS_DOMAIN=my-domain.com" /etc/default/postsrsd
@test "should update configuration correctly (--hostname + --domainname)" {
export CONTAINER_NAME="${CONTAINER4_NAME}"
local CUSTOM_SETUP_ARGUMENTS=(
--hostname 'mail'
--domainname 'example.test'
--env ENABLE_AMAVIS=1
--env ENABLE_SRS=1
--env PERMIT_DOCKER='container'
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_smtp_port_in_container
# Differs from the first test case, matches exact `--hostname` value:
_should_have_expected_hostname 'mail'
_should_be_configured_to_domainname 'example.test'
_should_be_configured_to_fqdn 'mail.example.test'
# Likewise `--hostname` value will always match the third parameter:
_should_have_correct_mail_headers 'mail.example.test' 'example.test' 'mail'
}
# This test is purely for testing the ENV `SRS_DOMAINNAME` (not relevant to these tests?)
@test "should give priority to ENV in postsrsd config (ENV SRS_DOMAINNAME)" {
export CONTAINER_NAME="${CONTAINER5_NAME}"
local CUSTOM_SETUP_ARGUMENTS=(
--hostname 'mail'
--domainname 'example.test'
--env ENABLE_SRS=1
--env SRS_DOMAINNAME='srs.example.test'
--env PERMIT_DOCKER='container'
--ulimit "nofile=$(ulimit -Sn):$(ulimit -Hn)"
)
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
# PostSRSd should be configured correctly:
_run_in_container grep '^SRS_DOMAIN=' /etc/default/postsrsd
assert_output "SRS_DOMAIN=srs.example.test"
assert_success
}
@test "checking dovecot: postmaster address" {
run docker exec mail_override_hostname /bin/sh -c "grep 'postmaster_address = postmaster@my-domain.com' /etc/dovecot/conf.d/15-lda.conf"
function _should_have_expected_hostname() {
local EXPECTED_FQDN=${1}
_run_in_container "hostname"
assert_output "${EXPECTED_FQDN}"
assert_success
_run_in_container grep -E "[[:space:]]+${EXPECTED_FQDN}" /etc/hosts
assert_success
}
#
# non-subdomain tests
#
function _should_be_configured_to_domainname() {
local EXPECTED_DOMAIN=${1}
@test "checking configuration: non-subdomain: check container hostname is applied correctly" {
run docker exec mail_non_subdomain_hostname /bin/bash -c "hostname | grep domain.com"
# setup-stack.sh:_setup_mailname
_run_in_container cat /etc/mailname
assert_output "${EXPECTED_DOMAIN}"
assert_success
# Postfix
_run_in_container postconf mydomain
assert_output "mydomain = ${EXPECTED_DOMAIN}"
assert_success
# PostSRSd
_run_in_container grep '^SRS_DOMAIN=' /etc/default/postsrsd
assert_output "SRS_DOMAIN=${EXPECTED_DOMAIN}"
assert_success
# Dovecot
_run_in_container grep '^postmaster_address' /etc/dovecot/conf.d/15-lda.conf
assert_output "postmaster_address = postmaster@${EXPECTED_DOMAIN}"
assert_success
}
@test "checking configuration: non-subdomain: check overriden hostname is applied to all configs" {
run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/mailname | grep domain.com"
function _should_be_configured_to_fqdn() {
local EXPECTED_FQDN=${1}
# Postfix
_run_in_container postconf myhostname
assert_output "myhostname = ${EXPECTED_FQDN}"
assert_success
# Postfix HELO message should contain FQDN (hostname)
_run_in_container nc -w 1 0.0.0.0 25
assert_output --partial "220 ${EXPECTED_FQDN} ESMTP"
assert_success
run docker exec mail_non_subdomain_hostname /bin/bash -c "postconf -n | grep mydomain | grep domain.com"
# Dovecot
_run_in_container doveconf hostname
assert_output "hostname = ${EXPECTED_FQDN}"
assert_success
run docker exec mail_non_subdomain_hostname /bin/bash -c "postconf -n | grep myhostname | grep domain.com"
# OpenDMARC
_run_in_container grep '^AuthservID' /etc/opendmarc.conf
assert_output --partial " ${EXPECTED_FQDN}"
assert_success
_run_in_container grep '^TrustedAuthservIDs' /etc/opendmarc.conf
assert_output --partial " ${EXPECTED_FQDN}"
assert_success
run docker exec mail_non_subdomain_hostname /bin/bash -c "doveconf | grep hostname | grep domain.com"
assert_success
run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep AuthservID | grep domain.com"
assert_success
run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/opendmarc.conf | grep TrustedAuthservIDs | grep domain.com"
assert_success
run docker exec mail_non_subdomain_hostname /bin/bash -c "cat /etc/amavis/conf.d/05-node_id | grep myhostname | grep domain.com"
# Amavis
_run_in_container grep '^\$myhostname' /etc/amavis/conf.d/05-node_id
assert_output "\$myhostname = \"${EXPECTED_FQDN}\";"
assert_success
}
@test "checking configuration: non-subdomain: check hostname in postfix HELO message" {
run docker exec mail_non_subdomain_hostname /bin/bash -c "nc -w 1 0.0.0.0 25 | grep domain.com"
assert_success
}
@test "checking configuration: non-subdomain: check headers of received mail" {
run docker exec mail_non_subdomain_hostname /bin/sh -c "ls -A /var/mail/localhost.localdomain/user1/new | wc -l | grep 1"
assert_success
run docker exec mail_non_subdomain_hostname /bin/sh -c "cat /var/mail/localhost.localdomain/user1/new/* | grep domain.com"
assert_success
}
@test "checking SRS: non-subdomain is handled correctly" {
docker exec mail_non_subdomain_hostname cat /etc/default/postsrsd
run docker exec mail_non_subdomain_hostname grep "SRS_DOMAIN=domain.com" /etc/default/postsrsd
assert_success
}
@test "checking dovecot: non-subdomain postmaster address" {
run docker exec mail_non_subdomain_hostname /bin/sh -c "grep 'postmaster_address = postmaster@domain.com' /etc/dovecot/conf.d/15-lda.conf"
assert_success
}
#
# clean exit
#
@test "checking that the container stops cleanly: mail_override_hostname" {
run docker stop -t 60 mail_override_hostname
assert_success
}
@test "checking that the container stops cleanly: mail_non_subdomain_hostname" {
run docker stop -t 60 mail_non_subdomain_hostname
assert_success
}
@test "checking that the container stops cleanly: mail_srs_domainname" {
run docker stop -t 60 mail_srs_domainname
assert_success
}
@test "checking that the container stops cleanly: mail_domainname" {
run docker stop -t 60 mail_domainname
function _should_have_correct_mail_headers() {
local EXPECTED_FQDN=${1}
# NOTE: The next two params should not differ for bare domains:
local EXPECTED_DOMAINPART=${2:-${EXPECTED_FQDN}}
# Required when EXPECTED_FQDN would not match the container hostname:
# (eg: OVERRIDE_HOSTNAME or `--hostname mail --domainname example.test`)
local EXPECTED_HOSTNAME=${3:-${EXPECTED_FQDN}}
_run_in_container_bash "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt"
assert_success
_wait_for_empty_mail_queue_in_container
_count_files_in_directory_in_container '/var/mail/localhost.localdomain/user1/new/' '1'
# MTA hostname (sender?) is used in filename of stored mail:
local MAIL_FILEPATH=$(_exec_in_container find /var/mail/localhost.localdomain/user1/new -maxdepth 1 -type f)
run echo "${MAIL_FILEPATH}"
assert_success
assert_output --partial ".${EXPECTED_HOSTNAME},"
# Mail headers should contain EXPECTED_FQDN for lines Received + by + Message-Id
# For `ENABLE_SRS=1`, EXPECTED_DOMAINPART should match lines Return-Path + envelope-from
_run_in_container cat "${MAIL_FILEPATH}"
assert_success
assert_line --index 0 --partial 'Return-Path: <SRS0='
assert_line --index 0 --partial "@${EXPECTED_DOMAINPART}>"
# Passed on from Postfix to Dovecot via LMTP:
assert_line --index 2 --partial "Received: from ${EXPECTED_FQDN}"
assert_line --index 3 --partial "by ${EXPECTED_FQDN} with LMTP"
assert_line --index 5 --partial '(envelope-from <SRS0='
assert_line --index 5 --partial "@${EXPECTED_DOMAINPART}>"
# Arrived via Postfix:
# NOTE: The first `localhost` in this line would actually be `mail.external.tld`,
# but Amavis is changing that. It also changes protocol from SMTP to ESMTP.
assert_line --index 7 --partial 'Received: from localhost (localhost [127.0.0.1])'
assert_line --index 8 --partial "by ${EXPECTED_FQDN} (Postfix) with ESMTP id"
assert_line --index 14 --partial 'Message-Id:'
assert_line --index 14 --partial "@${EXPECTED_FQDN}>"
# Mail contents example:
#
# Return-Path: <SRS0=Smtf=5T=external.tld=user@example.test>
# Delivered-To: user1@localhost.localdomain
# Received: from mail.example.test
# by mail.example.test with LMTP
# id jvJfJk23zGPeBgAAUi6ngw
# (envelope-from <SRS0=Smtf=5T=external.tld=user@example.test>)
# for <user1@localhost.localdomain>; Sun, 22 Jan 2023 04:10:53 +0000
# Received: from localhost (localhost [127.0.0.1])
# by mail.example.test (Postfix) with ESMTP id 8CFC4C30F9C4
# for <user1@localhost.localdomain>; Sun, 22 Jan 2023 04:10:53 +0000 (UTC)
# From: Docker Mail Server <dockermailserver@external.tld>
# To: Existing Local User <user1@localhost.localdomain>
# Date: Sat, 22 May 2010 07:43:25 -0400
# Subject: Test Message existing-user1.txt
# Message-Id: <20230122041053.5A5F1C2F608E@mail.example.test>
#
# This is a test mail.
}

View file

@ -244,18 +244,6 @@ function teardown_file() { _default_teardown ; }
assert_failure
}
@test "system: sets the server fqdn" {
_run_in_container hostname
assert_success
assert_output "mail.example.test"
}
@test "system: sets the server domain name in /etc/mailname" {
_run_in_container cat /etc/mailname
assert_success
assert_output "example.test"
}
@test "system: postfix should not log to syslog" {
_run_in_container grep 'postfix' /var/log/syslog
assert_failure