diff --git a/templates/mailman-hyperkitty-archiver-reconcile.py.j2 b/templates/mailman-hyperkitty-archiver-reconcile.py.j2 new file mode 100644 index 0000000..6c36f4a --- /dev/null +++ b/templates/mailman-hyperkitty-archiver-reconcile.py.j2 @@ -0,0 +1,108 @@ +#!{{ mailman_bindir }}/python3 + +"""Disable HyperKitty for Mailman lists whose archive policy is never.""" + +import argparse +import fcntl +import logging +import sys + +from mailman.config import config +from mailman.core.initialize import initialize +from mailman.interfaces.listmanager import IListManager +from mailman.interfaces.mailinglist import IListArchiverSet +from zope.component import getUtility + + +CONFIG_FILE = {{ (mailman_conf_dir + '/mailman.cfg') | to_json }} +LOCK_FILE = {{ mailman_hyperkitty_archiver_reconciliation_lock | to_json }} +ARCHIVER_NAME = 'hyperkitty' + + +logging.basicConfig( + level=logging.INFO, + format='mailman-hyperkitty-archiver-reconcile: %(levelname)s: %(message)s') +LOG = logging.getLogger(__name__) + + +def normalize_policy(value): + if hasattr(value, 'name'): + value = value.name + if isinstance(value, int): + return {0: 'never', 1: 'private', 2: 'public'}.get(value, str(value)) + return str(value).strip().lower() + + +def reconcile(dry_run): + initialize(CONFIG_FILE) + lists = sorted( + getUtility(IListManager).mailing_lists, + key=lambda mailing_list: mailing_list.fqdn_listname.casefold()) + candidates = [] + + for mailing_list in lists: + if normalize_policy(mailing_list.archive_policy) != 'never': + continue + archiver = IListArchiverSet(mailing_list).get(ARCHIVER_NAME) + if archiver is not None and archiver.is_enabled: + candidates.append((mailing_list, archiver)) + + changed = 0 + if dry_run: + for mailing_list, _archiver in candidates: + LOG.info( + 'would disable %s for %s (archive policy is never)', + ARCHIVER_NAME, mailing_list.fqdn_listname) + config.db.abort() + else: + for mailing_list, archiver in candidates: + # Re-read both rows before changing them so a concurrent Postorius + # update cannot be overwritten from the initial snapshot. + config.db.store.refresh(mailing_list) + config.db.store.refresh(archiver) + if (normalize_policy(mailing_list.archive_policy) != 'never' or + not archiver.is_enabled): + LOG.warning( + 'skipping %s: settings changed during reconciliation', + mailing_list.fqdn_listname) + continue + archiver.is_enabled = False + changed += 1 + LOG.info( + 'disabled %s for %s (archive policy is never)', + ARCHIVER_NAME, mailing_list.fqdn_listname) + config.db.commit() + + LOG.info( + '%d Core lists, %d inconsistent archiver settings, %d changed', + len(lists), len(candidates), changed) + return 0 + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--dry-run', action='store_true', + help='report inconsistent lists without changing Mailman Core') + arguments = parser.parse_args() + + try: + with open(LOCK_FILE, 'w') as lock_handle: + try: + fcntl.flock(lock_handle, fcntl.LOCK_EX | fcntl.LOCK_NB) + except OSError: + LOG.error('another reconciliation process holds %s', LOCK_FILE) + return 1 + return reconcile(arguments.dry_run) + except Exception as error: + try: + config.db.abort() + except Exception: + # Preserve the original initialization or database exception. + pass + LOG.exception('reconciliation failed: %s', error) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/templates/mailman-hyperkitty-archiver-reconcile.service.systemd.j2 b/templates/mailman-hyperkitty-archiver-reconcile.service.systemd.j2 new file mode 100644 index 0000000..d3db464 --- /dev/null +++ b/templates/mailman-hyperkitty-archiver-reconcile.service.systemd.j2 @@ -0,0 +1,13 @@ +[Unit] +Description=Disable HyperKitty for Mailman lists with archive policy never +After=network-online.target mailman.service +Wants=network-online.target +ConditionPathExists={{ mailman_conf_dir }}/mailman.cfg + +[Service] +Type=oneshot +User={{ mailman_user }} +Group={{ mailman_user }} +ExecStart={{ mailman_hyperkitty_archiver_reconciliation_script }} +TimeoutStartSec=5min +Nice=10 diff --git a/templates/mailman-hyperkitty-archiver-reconcile.timer.systemd.j2 b/templates/mailman-hyperkitty-archiver-reconcile.timer.systemd.j2 new file mode 100644 index 0000000..8a41c4d --- /dev/null +++ b/templates/mailman-hyperkitty-archiver-reconcile.timer.systemd.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Periodically reconcile Mailman and HyperKitty archive settings + +[Timer] +OnActiveSec={{ mailman_hyperkitty_archiver_reconciliation_on_active }} +OnUnitActiveSec={{ mailman_hyperkitty_archiver_reconciliation_interval }} +AccuracySec=1min +Persistent=false +Unit=mailman-hyperkitty-archiver-reconcile.service + +[Install] +WantedBy=timers.target