2023-01-16 07:39:46 +00:00
|
|
|
load "${REPOSITORY_ROOT}/test/helper/common"
|
|
|
|
load "${REPOSITORY_ROOT}/test/helper/change-detection"
|
|
|
|
load "${REPOSITORY_ROOT}/test/helper/setup"
|
2021-09-13 08:09:01 +00:00
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
BATS_TEST_NAME_PREFIX='[Change Detection] '
|
|
|
|
|
|
|
|
CONTAINER1_NAME='dms-test_changedetector_one'
|
|
|
|
CONTAINER2_NAME='dms-test_changedetector_two'
|
2021-11-20 20:33:49 +00:00
|
|
|
|
2021-09-13 08:09:01 +00:00
|
|
|
function setup_file() {
|
2023-01-16 07:39:46 +00:00
|
|
|
export CONTAINER_NAME
|
|
|
|
|
|
|
|
local CUSTOM_SETUP_ARGUMENTS=(
|
|
|
|
--env LOG_LEVEL=trace
|
|
|
|
)
|
|
|
|
|
|
|
|
CONTAINER_NAME=${CONTAINER1_NAME}
|
2023-01-21 23:05:28 +00:00
|
|
|
_init_with_defaults
|
|
|
|
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
2023-01-16 07:39:46 +00:00
|
|
|
|
|
|
|
CONTAINER_NAME=${CONTAINER2_NAME}
|
|
|
|
# NOTE: No `init_with_defaults` used here,
|
|
|
|
# Intentionally sharing previous containers config instead.
|
2023-01-21 23:05:28 +00:00
|
|
|
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
|
2023-01-16 07:39:46 +00:00
|
|
|
|
|
|
|
# Set default implicit container fallback for helpers:
|
|
|
|
CONTAINER_NAME=${CONTAINER1_NAME}
|
2021-09-13 08:09:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function teardown_file() {
|
2023-01-16 07:39:46 +00:00
|
|
|
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}"
|
2021-09-13 08:09:01 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
@test "changedetector service is ready" {
|
2023-01-21 23:05:28 +00:00
|
|
|
_wait_for_service changedetector "${CONTAINER1_NAME}"
|
|
|
|
_wait_for_service changedetector "${CONTAINER2_NAME}"
|
2021-09-13 08:09:01 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
# NOTE: Non-deterministic behaviour - One container will perform change detection before the other.
|
|
|
|
# Depending on the timing of the other container checking for a lock file, the lock may no longer be
|
|
|
|
# present, avoiding the 5 second delay. The first container to create a lock is not deterministic either.
|
|
|
|
# NOTE: Change detection at this point typically occurs at 2 or 4 seconds since the service was up,
|
|
|
|
# thus expect 2-8 seconds to complete.
|
|
|
|
@test "should detect and process changes (in both containers with shared config)" {
|
|
|
|
_create_change_event
|
2022-05-30 00:53:30 +00:00
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
_should_perform_standard_change_event "${CONTAINER1_NAME}"
|
|
|
|
_should_perform_standard_change_event "${CONTAINER2_NAME}"
|
|
|
|
}
|
|
|
|
|
|
|
|
# Both containers should acknowledge the foreign lock file added,
|
|
|
|
# blocking an attempt to process the pending change event detected.
|
|
|
|
@test "should find existing lock file and block processing changes (without removing lock)" {
|
|
|
|
_prepare_blocking_lock_test
|
|
|
|
|
|
|
|
# Wait until the 2nd change event attempts to process:
|
2023-01-21 23:05:28 +00:00
|
|
|
_should_block_change_event_from_processing 2 "${CONTAINER1_NAME}"
|
2023-01-16 07:39:46 +00:00
|
|
|
# NOTE: Although the service is restarted, a change detection should still occur (previous checksum still exists):
|
2023-01-21 23:05:28 +00:00
|
|
|
_should_block_change_event_from_processing 1 "${CONTAINER2_NAME}"
|
2023-01-16 07:39:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@test "should remove lock file when stale" {
|
|
|
|
# Avoid a race condition (to remove the lock file) by removing the 2nd container:
|
|
|
|
docker rm -f "${CONTAINER2_NAME}"
|
|
|
|
# Make the previously created lock file become stale:
|
|
|
|
docker exec "${CONTAINER1_NAME}" touch -d '60 seconds ago' /tmp/docker-mailserver/check-for-changes.sh.lock
|
|
|
|
|
|
|
|
# A 2nd change event should complete (or may already have if quick enough?):
|
2023-01-21 23:05:28 +00:00
|
|
|
_wait_until_change_detection_event_completes 2 "${CONTAINER1_NAME}"
|
2022-05-30 00:53:30 +00:00
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
# Should have removed the stale lock file, then handle the change event:
|
|
|
|
run _get_logs_since_last_change_detection "${CONTAINER1_NAME}"
|
|
|
|
assert_output --partial 'Lock file older than 1 minute - removing stale lock file'
|
|
|
|
_assert_has_standard_change_event_logs
|
|
|
|
}
|
|
|
|
|
|
|
|
function _should_perform_standard_change_event() {
|
|
|
|
local CONTAINER_NAME=${1}
|
|
|
|
|
|
|
|
# Wait for change detection event to start and complete processing:
|
|
|
|
# NOTE: An explicit count is provided as the 2nd container may have already completed processing.
|
2023-01-21 23:05:28 +00:00
|
|
|
_wait_until_change_detection_event_completes 1
|
2023-01-16 07:39:46 +00:00
|
|
|
|
|
|
|
# Container should have created it's own lock file,
|
|
|
|
# and later removed it when finished processing:
|
2023-01-21 23:05:28 +00:00
|
|
|
run _get_logs_since_last_change_detection
|
2023-01-16 07:39:46 +00:00
|
|
|
_assert_has_standard_change_event_logs
|
|
|
|
}
|
|
|
|
|
|
|
|
function _should_block_change_event_from_processing() {
|
2023-01-21 23:05:28 +00:00
|
|
|
local EXPECTED_COUNT=${1}
|
|
|
|
local CONTAINER_NAME=${2}
|
2023-01-16 07:39:46 +00:00
|
|
|
|
|
|
|
# Once the next change event has started, the processing blocked log ('another execution') should be present:
|
2023-01-21 23:05:28 +00:00
|
|
|
_wait_until_change_detection_event_begins "${EXPECTED_COUNT}"
|
2023-01-16 07:39:46 +00:00
|
|
|
|
|
|
|
run _get_logs_since_last_change_detection "${CONTAINER_NAME}"
|
|
|
|
_assert_foreign_lock_exists
|
|
|
|
# This additionally verifies that the change event processing is incomplete (blocked):
|
|
|
|
_assert_no_lock_actions_performed
|
|
|
|
}
|
|
|
|
|
|
|
|
function _assert_has_standard_change_event_logs() {
|
|
|
|
assert_output --partial "Creating lock '/tmp/docker-mailserver/check-for-changes.sh.lock'"
|
fix(changedetector): Use service `reload` commands instead of `supervisorctl restart <service>` (#2947)
With `reload` a change detection event during local testing can be processed in less than a second according to logs. Previously this was 5+ seconds (_plus additional downtime for Postfix/Dovecot to become available again_).
In the past it was apparently an issue to use `<service> reload` due to a concern with the PID for wrapper scripts that `supervisorctl` managed, thus `supervisorctl <service> restart` had been used. Past discussions with maintainers suggest this is not likely an issue anymore, and `reload` should be fine to switch to now :+1:
---
**NOTE:** It may not be an issue in the CI, but on _**local systems running tests may risk failure in `setup-cli.bats` from a false positive**_ due to 1 second polling window of the test helper method, and a change event being possible to occur entirely between the two checks undetected by the current approach.
If this is a problem, we may need to think of a better way to catch the change. The `letsencrypt` test counts how many change events are expected to have been processed, and this could technically be leveraged by the test helper too.
---
**NOTE:** These two lines (_with regex pattern for postfix_) are output in the terminal when using the services respective `reload` commands:
```
postfix/master.*: reload -- version .*, configuration /etc/postfix
dovecot: master: Warning: SIGHUP received - reloading configuration
```
I wasn't sure how to match them as they did not appear in the `changedetector` log (_**EDIT:** they appear in the main log output, eg `docker logs <container name>`_).
Instead I've just monitored the `changedetector` log messages, which should be ok for logic that previously needed to ensure Dovecot / Postfix was back up after the `restart` was issued.
---
Commit history:
* chore: Change events `reload` Dovecot and Postfix instead of `restart`
Reloading is faster than restarting the processes.
Restarting is a bit heavy handed here and may no longer be necessary for general usage?
* tests: Adapt tests to support service `reload` instead of `restart`
* chore: Additional logging for debugging change event logs
* fix: Wait on change detection, then verify directory created
Change detection is too fast now (0-1 seconds vs 5+).
Directory being waited on here was created near the end of a change event, reducing that time to detect a change by the utility method further.
We can instead check that the directory exists after the change detection event is completed.
* chore: Keep using the maildir polling check
We don't presently use remote storage in tests, but it might be relevant in future when testing NFS.
This at least avoids any confusing failure happening when that scenario is tested.
2022-12-23 12:57:24 +00:00
|
|
|
assert_output --partial 'Reloading services due to detected changes'
|
|
|
|
assert_output --partial 'Removed lock'
|
|
|
|
assert_output --partial 'Completed handling of detected change'
|
2021-09-13 08:09:01 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
function _assert_foreign_lock_exists() {
|
|
|
|
assert_output --partial "Lock file '/tmp/docker-mailserver/check-for-changes.sh.lock' exists"
|
|
|
|
assert_output --partial "- another execution of 'check-for-changes.sh' is happening - trying again shortly"
|
2021-09-13 08:09:01 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
function _assert_no_lock_actions_performed() {
|
|
|
|
refute_output --partial 'Lock file older than 1 minute - removing stale lock file'
|
|
|
|
refute_output --partial "Creating lock '/tmp/docker-mailserver/check-for-changes.sh.lock'"
|
|
|
|
refute_output --partial 'Removed lock'
|
|
|
|
}
|
2022-05-30 00:53:30 +00:00
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
function _prepare_blocking_lock_test {
|
2023-01-21 23:05:28 +00:00
|
|
|
local CONTAINER_NAME=${CONTAINER2_NAME}
|
2023-01-16 07:39:46 +00:00
|
|
|
# Temporarily disable the Container2 changedetector service:
|
2023-01-21 23:05:28 +00:00
|
|
|
_exec_in_container_bash 'supervisorctl stop changedetector'
|
|
|
|
_exec_in_container_bash 'rm -f /var/log/supervisor/changedetector.log'
|
2023-01-16 07:39:46 +00:00
|
|
|
|
|
|
|
# Create a foreign lock file to prevent change processing (in both containers):
|
2023-01-21 23:05:28 +00:00
|
|
|
_exec_in_container_explicit "${CONTAINER1_NAME}" /bin/bash -c 'touch /tmp/docker-mailserver/check-for-changes.sh.lock'
|
2023-01-16 07:39:46 +00:00
|
|
|
# Create a new change to detect (that the foreign lock should prevent from processing):
|
|
|
|
_create_change_event
|
|
|
|
|
|
|
|
# Restore Container2 changedetector service:
|
|
|
|
# NOTE: The last known checksum is retained in Container2,
|
|
|
|
# It will be compared to and start a change event.
|
2023-01-21 23:05:28 +00:00
|
|
|
_exec_in_container_bash 'supervisorctl start changedetector'
|
2023-01-16 07:39:46 +00:00
|
|
|
}
|
2022-05-30 00:53:30 +00:00
|
|
|
|
2023-01-16 07:39:46 +00:00
|
|
|
function _create_change_event() {
|
|
|
|
echo '' >>"${TEST_TMP_CONFIG}/postfix-accounts.cf"
|
2021-09-13 08:09:01 +00:00
|
|
|
}
|