From ef104e06c4a495a2164f176c27690a1cbbf830ad Mon Sep 17 00:00:00 2001 From: Andrea Dell'Amico Date: Fri, 21 Aug 2026 17:11:51 +0200 Subject: [PATCH] Optionally restart the service every week. --- README.md | 17 +++++ defaults/main.yml | 8 +++ tasks/mailman.yml | 44 ++++++++++++ ...ailman-verified-restart.service.systemd.j2 | 9 +++ templates/mailman-verified-restart.sh.j2 | 68 +++++++++++++++++++ .../mailman-verified-restart.timer.systemd.j2 | 11 +++ 6 files changed, 157 insertions(+) create mode 100644 templates/mailman-verified-restart.service.systemd.j2 create mode 100644 templates/mailman-verified-restart.sh.j2 create mode 100644 templates/mailman-verified-restart.timer.systemd.j2 diff --git a/README.md b/README.md index 52a556b..aa566f0 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,20 @@ mailman_empty_list_header_matches: The operation is idempotent. A list is modified only when its header-match collection is empty; lists with one or more existing rules are left untouched. + +## Verified weekly restart + +The optional weekly restart performs separate stop and start operations. It +waits for both systemd and `mailman status` to confirm that Mailman has stopped +before starting it, then checks both signals again to confirm startup: + +```yaml +mailman_enable_weekly_verified_restart: true +mailman_weekly_verified_restart_on_calendar: 'Sun *-*-* 04:00:00' +mailman_weekly_verified_restart_stop_timeout: 120 +mailman_weekly_verified_restart_start_timeout: 120 +``` + +The timer is deliberately not persistent, so a missed run is not executed +immediately after a server boot. Failures are recorded by systemd in the +`mailman-verified-restart.service` journal. diff --git a/defaults/main.yml b/defaults/main.yml index f7dfcf2..7378b13 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -35,6 +35,14 @@ mailman_custom_templates: - 'it/list:user:notice:welcome.txt' mailman_enable_daily_notifications: true + +# Periodically perform a verified stop/start cycle. The timer is disabled by +# default and, when enabled, uses the managed host's local timezone. +mailman_enable_weekly_verified_restart: false +mailman_weekly_verified_restart_on_calendar: 'Sun *-*-* 04:00:00' +mailman_weekly_verified_restart_stop_timeout: 120 +mailman_weekly_verified_restart_start_timeout: 120 + # Documentation that must be followed to configure the social auth providers # https://django-allauth.readthedocs.io/en/latest/installation.html mailman_use_social_account_providers: False diff --git a/tasks/mailman.yml b/tasks/mailman.yml index c56b0cd..3428204 100644 --- a/tasks/mailman.yml +++ b/tasks/mailman.yml @@ -111,6 +111,50 @@ tags: [ 'mailman', 'mailman_conf' ] +- name: Configure the verified weekly Mailman restart + block: + - name: Install the verified Mailman restart script + ansible.builtin.template: + src: mailman-verified-restart.sh.j2 + dest: /usr/local/sbin/mailman-verified-restart + owner: root + group: root + mode: '0750' + + - name: Install the verified Mailman restart service + ansible.builtin.template: + src: mailman-verified-restart.service.systemd.j2 + dest: /etc/systemd/system/mailman-verified-restart.service + owner: root + group: root + mode: '0644' + register: mailman_verified_restart_service_install + + - name: Install the weekly Mailman restart timer + ansible.builtin.template: + src: mailman-verified-restart.timer.systemd.j2 + dest: /etc/systemd/system/mailman-verified-restart.timer + owner: root + group: root + mode: '0644' + register: mailman_verified_restart_timer_install + + - name: Reload systemd after installing the restart units + ansible.builtin.systemd: + daemon_reload: true + when: >- + mailman_verified_restart_service_install is changed or + mailman_verified_restart_timer_install is changed + + - name: Enable and start the weekly Mailman restart timer + ansible.builtin.systemd: + name: mailman-verified-restart.timer + state: started + enabled: true + + when: mailman_enable_weekly_verified_restart | bool + tags: [ 'mailman', 'mailman_conf', 'mailman_weekly_restart' ] + - name: Seed header matches on lists without existing rules block: - name: Create the Mailman administration scripts directory diff --git a/templates/mailman-verified-restart.service.systemd.j2 b/templates/mailman-verified-restart.service.systemd.j2 new file mode 100644 index 0000000..305fd5d --- /dev/null +++ b/templates/mailman-verified-restart.service.systemd.j2 @@ -0,0 +1,9 @@ +[Unit] +Description=Verified stop and start cycle for Mailman 3 +After=network-online.target remote-fs.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/mailman-verified-restart +TimeoutStartSec={{ (mailman_weekly_verified_restart_stop_timeout | int) + (mailman_weekly_verified_restart_start_timeout | int) + 30 }} diff --git a/templates/mailman-verified-restart.sh.j2 b/templates/mailman-verified-restart.sh.j2 new file mode 100644 index 0000000..94880d2 --- /dev/null +++ b/templates/mailman-verified-restart.sh.j2 @@ -0,0 +1,68 @@ +#!/bin/bash + +set -uo pipefail + +readonly SERVICE='mailman.service' +readonly MAILMAN='{{ mailman_bindir }}/mailman' +readonly CONFIG='{{ mailman_conf_dir }}/mailman.cfg' +readonly STOP_TIMEOUT={{ mailman_weekly_verified_restart_stop_timeout | int }} +readonly START_TIMEOUT={{ mailman_weekly_verified_restart_start_timeout | int }} + +log() +{ + printf 'mailman-verified-restart: %s\n' "$*" +} + +mailman_is_stopped() +{ + ! systemctl is-active --quiet "${SERVICE}" && + ! "${MAILMAN}" -C "${CONFIG}" status >/dev/null 2>&1 +} + +mailman_is_started() +{ + systemctl is-active --quiet "${SERVICE}" && + "${MAILMAN}" -C "${CONFIG}" status >/dev/null 2>&1 +} + +wait_for_state() +{ + local check_function="$1" + local timeout="$2" + local deadline=$((SECONDS + timeout)) + + while (( SECONDS < deadline )); do + if "${check_function}"; then + return 0 + fi + sleep 1 + done + "${check_function}" +} + +log 'stopping Mailman' +if ! systemctl stop "${SERVICE}"; then + log 'ERROR: systemctl could not stop Mailman' + exit 1 +fi + +if ! wait_for_state mailman_is_stopped "${STOP_TIMEOUT}"; then + log 'ERROR: Mailman did not stop completely within the timeout; start skipped' + exit 1 +fi +log 'Mailman is completely stopped' + +systemctl reset-failed "${SERVICE}" >/dev/null 2>&1 || true + +log 'starting Mailman' +if ! systemctl start "${SERVICE}"; then + log 'ERROR: systemctl could not start Mailman' + exit 1 +fi + +if ! wait_for_state mailman_is_started "${START_TIMEOUT}"; then + log 'ERROR: Mailman did not become active within the timeout' + exit 1 +fi + +log 'Mailman is active and its master process is running' diff --git a/templates/mailman-verified-restart.timer.systemd.j2 b/templates/mailman-verified-restart.timer.systemd.j2 new file mode 100644 index 0000000..49e8ac2 --- /dev/null +++ b/templates/mailman-verified-restart.timer.systemd.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Weekly verified restart of Mailman 3 + +[Timer] +OnCalendar={{ mailman_weekly_verified_restart_on_calendar }} +AccuracySec=1min +Persistent=false +Unit=mailman-verified-restart.service + +[Install] +WantedBy=timers.target