diff options
| author | Barry Warsaw | 2011-06-01 17:09:32 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2011-06-01 17:09:32 -0400 |
| commit | bf8b285acb8c2500e52ae2582f27513b9842de54 (patch) | |
| tree | 53e30be0bb665d66a9350fe58d22697c4c0a860e | |
| parent | 0f85fb344688e1982e9320e79b7fb38eefc1ac53 (diff) | |
| download | mailman-bf8b285acb8c2500e52ae2582f27513b9842de54.tar.gz mailman-bf8b285acb8c2500e52ae2582f27513b9842de54.tar.zst mailman-bf8b285acb8c2500e52ae2582f27513b9842de54.zip | |
50 files changed, 442 insertions, 577 deletions
diff --git a/data/mailman.in b/data/mailman.in deleted file mode 100644 index ac7d660d5..000000000 --- a/data/mailman.in +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh -# -# mailman This shell script starts and stops GNU Mailman. -# -# Copyright (C) 2001-2011 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# Copy this file to /etc/init.d/ (or /etc/rc.d/init.d/ depending on -# your system) and activate it as such: -# -# On Debian, type "update-rc.d mailman defaults" -# On RedHat, and derivatives, install with "chkconfig --add mailman" -# -# chkconfig: 2345 98 12 -# description: Mailman is the GNU Mailing List Manager, a program that \ -# manages electronic mail discussion groups. For more \ -# on GNU Mailman see http://www.list.org -# processname: mailmanctl -# config: @prefix@/Mailman/mm_cfg.py -# pidfile: @prefix@/data/master-qrunner.pid - -PYTHON=@PYTHON@ -MAILMANHOME=@prefix@ -MAILMANCTL=$MAILMANHOME/bin/mailmanctl - -case "$1" in -'start') - #rm -f $MAILMANHOME/locks/* - $PYTHON $MAILMANCTL -s -q start - ;; - -'stop') - $PYTHON $MAILMANCTL -q stop - ;; - -'restart') - $PYTHON $MAILMANCTL -q restart - ;; - -esac -exit 0 diff --git a/data/paths.py.in b/data/paths.py.in deleted file mode 100644 index d1bd6347e..000000000 --- a/data/paths.py.in +++ /dev/null @@ -1,90 +0,0 @@ -# -*- python -*- - -# Copyright (C) 1998-2011 by the Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. - -# configure turns this file into paths.py which is installed in the bin -# directory. By importing this module, sys.path gets `hacked' so that the -# $prefix/Mailman and $prefix/pythonlib directories are inserted at the start -# of that list. This file exports two attributes that other modules may use -# to get the absolute path to the installed Mailman distribution. -# -# Note that we can't use site.addsitedir() because that ends up appending -# directories to sys.path and we really need to add them to the front so that -# they override anything in the system Python. - -import os -import sys - -# Some scripts expect this attribute to be in this module -prefix = '@prefix@' -exec_prefix = '@exec_prefix@' - -# Work around a bogus autoconf 2.12 bug -if exec_prefix == '${prefix}': - exec_prefix = prefix - -pythonlib = os.path.join(prefix, 'pythonlib', 'lib', 'python') - -# Hack the path to include the parent directory of $prefix/Mailman -sys.path.insert(0, prefix) - -# Much of this is ripped off from site.py -paths = set() -for dirname in sys.path: - try: - if os.path.isdir(dirname): - paths.add(os.path.normcase(os.path.abspath(dirname))) - except TypeError: - pass - -extra_paths = [pythonlib] -for name in sorted(os.listdir(pythonlib)): - if os.path.splitext(name)[1] == '.pth': - filename = os.path.join(pythonlib, name) - try: - fp = open(filename, 'rU') - except IOError: - continue - try: - for line in fp: - if line.startswith('#'): - continue - if line.startswith('import'): - exec line - continue - line = line.rstrip() - path = os.path.abspath(os.path.join(pythonlib, line)) - path = os.path.normcase(path) - if not path in paths and os.path.exists(path): - # Here's what's different than site.py! - extra_paths.append(path) - paths.add(path) - finally: - fp.close() -# Add the new paths to the front of sys.path -sys.path[0:0] = extra_paths - - -# Arabic and Hebrew (RFC-1556) encoding aliases. (temporary solution) -import encodings.aliases -encodings.aliases.aliases.update({ - 'iso_8859_6_e': 'iso8859_6', - 'iso_8859_6_i': 'iso8859_6', - 'iso_8859_8_e': 'iso8859_8', - 'iso_8859_8_i': 'iso8859_8', - }) @@ -67,7 +67,7 @@ with open('src/mailman/version.py') as fp: template = Template('$script = mailman.bin.$script:main') scripts = set( template.substitute(script=script) - for script in ('mailman', 'qrunner', 'master', 'onebounce') + for script in ('mailman', 'runner', 'master', 'onebounce') ) diff --git a/src/mailman/Archiver/HyperArch.py b/src/mailman/Archiver/HyperArch.py index 646608590..85e9b0e7c 100644 --- a/src/mailman/Archiver/HyperArch.py +++ b/src/mailman/Archiver/HyperArch.py @@ -62,7 +62,7 @@ NL = '\n' # regular expressions. We see this as crashes in the Python test suite when # running test_re.py and test_sre.py. The fix is to set the stack limit to # 2048; the general recommendation is to do in the shell before running the -# test suite. But that's inconvenient for a daemon like the qrunner. +# test suite. But that's inconvenient for a daemon like the runner. # # AFAIK, this problem only affects the archiver, so we're adding this work # around to this file (it'll get imported by the bundled pipermail or by the diff --git a/src/mailman/app/moderator.py b/src/mailman/app/moderator.py index a2f838934..1b9f21d2a 100644 --- a/src/mailman/app/moderator.py +++ b/src/mailman/app/moderator.py @@ -141,9 +141,9 @@ def handle_message(mlist, id, action, # delivery errors will cause duplicates. if 'filebase' in msgdata: del msgdata['filebase'] - # Queue the file for delivery by qrunner. Trying to deliver the - # message directly here can lead to a huge delay in web turnaround. - # Log the moderation and add a header. + # Queue the file for delivery. Trying to deliver the message directly + # here can lead to a huge delay in web turnaround. Log the moderation + # and add a header. msg['X-Mailman-Approved-At'] = formatdate(localtime=True) vlog.info('held message approved, message-id: %s', msg.get('message-id', 'n/a')) diff --git a/src/mailman/bin/docs/master.rst b/src/mailman/bin/docs/master.rst new file mode 100644 index 000000000..c4410bf16 --- /dev/null +++ b/src/mailman/bin/docs/master.rst @@ -0,0 +1,53 @@ +====================== +Mailman runner control +====================== + +Mailman has a number of *runner subprocesses* which perform long-running tasks +such as listening on an LMTP port, processing REST API requests, or processing +messages in a queue directory. In normal operation, the ``bin/mailman`` +command is used to start, stop and manage the runners. This is just a wrapper +around the real master watcher, which handles runner starting, stopping, +exiting, and log file reopening. + + >>> from mailman.testing.helpers import TestableMaster + +Start the master in a sub-thread. + + >>> master = TestableMaster() + >>> master.start() + +There should be a process id for every runner that claims to be startable. + + >>> from lazr.config import as_boolean + >>> startable_runners = [conf for conf in config.runner_configs + ... if as_boolean(conf.start)] + >>> len(list(master.runner_pids)) == len(startable_runners) + True + +Now verify that all the runners are running. +:: + + >>> import os + + # This should produce no output. + >>> for pid in master.runner_pids: + ... os.kill(pid, 0) + +Stop the master process, which should also kill (and not restart) the child +runner processes. + + >>> master.stop() + +None of the children are running now. + + >>> import errno + >>> for pid in master.runner_pids: + ... try: + ... os.kill(pid, 0) + ... print 'Process did not exit:', pid + ... except OSError as error: + ... if error.errno == errno.ESRCH: + ... # The child process exited. + ... pass + ... else: + ... raise diff --git a/src/mailman/bin/docs/master.txt b/src/mailman/bin/docs/master.txt deleted file mode 100644 index d3c07c768..000000000 --- a/src/mailman/bin/docs/master.txt +++ /dev/null @@ -1,51 +0,0 @@ -============================ -Mailman queue runner control -============================ - -Mailman has a number of queue runners which process messages in its queue file -directories. In normal operation, the ``bin/mailman`` command is used to -start, stop and manage the queue runners. This is just a wrapper around the -real queue runner watcher script called master.py. - - >>> from mailman.testing.helpers import TestableMaster - -Start the master in a sub-thread. - - >>> master = TestableMaster() - >>> master.start() - -There should be a process id for every qrunner that claims to be startable. - - >>> from lazr.config import as_boolean - >>> startable_qrunners = [qconf for qconf in config.qrunner_configs - ... if as_boolean(qconf.start)] - >>> len(list(master.qrunner_pids)) == len(startable_qrunners) - True - -Now verify that all the qrunners are running. -:: - - >>> import os - - # This should produce no output. - >>> for pid in master.qrunner_pids: - ... os.kill(pid, 0) - -Stop the master process, which should also kill (and not restart) the child -queue runner processes. - - >>> master.stop() - -None of the children are running now. - - >>> import errno - >>> for pid in master.qrunner_pids: - ... try: - ... os.kill(pid, 0) - ... print 'Process did not exit:', pid - ... except OSError, error: - ... if error.errno == errno.ESRCH: - ... # The child process exited. - ... pass - ... else: - ... raise diff --git a/src/mailman/bin/master.py b/src/mailman/bin/master.py index edc0452d7..ee15adbaa 100644 --- a/src/mailman/bin/master.py +++ b/src/mailman/bin/master.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Master sub-process watcher.""" +"""Master subprocess watcher.""" from __future__ import absolute_import, unicode_literals @@ -56,25 +56,25 @@ class ScriptOptions(Options): usage = _("""\ %prog [options] -Master sub-process watcher. +Master subprocess watcher. -Start and watch the configured queue runners and ensure that they stay alive -and kicking. Each are fork and exec'd in turn, with the master waiting on -their process ids. When it detects a child queue runner has exited, it may -restart it. +Start and watch the configured runners and ensure that they stay alive and +kicking. Each runner is forked and exec'd in turn, with the master waiting on +their process ids. When it detects a child runner has exited, it may restart +it. -The queue runners respond to SIGINT, SIGTERM, SIGUSR1 and SIGHUP. SIGINT, -SIGTERM and SIGUSR1 all cause the qrunners to exit cleanly. The master will -restart qrunners that have exited due to a SIGUSR1 or some kind of other exit -condition (say because of an exception). SIGHUP causes the master and the -qrunners to close their log files, and reopen then upon the next printed +The runners respond to SIGINT, SIGTERM, SIGUSR1 and SIGHUP. SIGINT, SIGTERM +and SIGUSR1 all cause a runner to exit cleanly. The master will restart +runners that have exited due to a SIGUSR1 or some kind of other exit condition +(say because of an uncaught exception). SIGHUP causes the master and the +runners to close their log files, and reopen then upon the next printed message. The master also responds to SIGINT, SIGTERM, SIGUSR1 and SIGHUP, which it -simply passes on to the qrunners. Note that the master will close and reopen +simply passes on to the runners. Note that the master will close and reopen its own log files on receipt of a SIGHUP. The master also leaves its own -process id in the file `data/master-qrunner.pid` but you normally don't need -to use this pid directly.""") +process id in the file `data/master.pid` but you normally don't need to use +this pid directly.""") def add_options(self): """See `Options`.""" @@ -82,7 +82,7 @@ to use this pid directly.""") '-n', '--no-restart', dest='restartable', default=True, action='store_false', help=_("""\ -Don't restart the qrunners when they exit because of an error or a SIGUSR1. +Don't restart the runners when they exit because of an error or a SIGUSR1. Use this only for debugging.""")) self.parser.add_option( '-f', '--force', @@ -98,9 +98,9 @@ apparently stale lock and make another attempt to claim the master lock.""")) '-r', '--runner', dest='runners', action='append', default=[], help=_("""\ -Override the default set of queue runners that the master watch will invoke -instead of the default set. Multiple -r options may be given. The values for --r are passed straight through to bin/qrunner.""")) +Override the default set of runners that the master will invoke, which is +typically defined in the configuration file. Multiple -r options may be +given. The values for -r are passed straight through to bin/runner.""")) def sanity_check(self): """See `Options`.""" @@ -152,13 +152,13 @@ def master_state(lock_file=None): def acquire_lock_1(force, lock_file=None): - """Try to acquire the master queue runner lock. + """Try to acquire the master lock. :param force: Flag that controls whether to force acquisition of the lock. :type force: bool :param lock_file: Path to the lock file, otherwise `config.LOCK_FILE`. :type lock_file: str - :return: The master queue runner lock. + :return: The master lock. :raises: `TimeOutError` if the lock could not be acquired. """ if lock_file is None: @@ -179,11 +179,12 @@ def acquire_lock_1(force, lock_file=None): def acquire_lock(force): - """Acquire the master queue runner lock. + """Acquire the master lock. - :return: The master queue runner lock or None if the lock couldn't be - acquired. In that case, an error messages is also printed to standard - error. + :param force: Flag that controls whether to force acquisition of the lock. + :type force: bool + :return: The master runner lock or None if the lock couldn't be acquired. + In that case, an error messages is also printed to standard error. """ try: lock = acquire_lock_1(force) @@ -193,23 +194,21 @@ def acquire_lock(force): if status is WatcherState.conflict: # Hostname matches and process exists. message = _("""\ -The master queue runner lock could not be acquired -because it appears as though another master is already running.""") +The master lock could not be acquired because it appears as though another +master is already running.""") elif status is WatcherState.stale_lock: # Hostname matches but the process does not exist. program = sys.argv[0] message = _("""\ -The master queue runner lock could not be acquired. -It appears as though there is a stale master lock. Try re-running -$program with the --force flag.""") +The master lock could not be acquired. It appears as though there is a stale +master lock. Try re-running $program with the --force flag.""") elif status is WatcherState.host_mismatch: # Hostname doesn't even match. hostname, pid, tempfile = lock.details message = _("""\ -The master qrunner lock could not be acquired, because it -appears as if some process on some other host may have acquired it. We can't -test for stale locks across host boundaries, so you'll have to clean this up -manually. +The master lock could not be acquired, because it appears as if some process +on some other host may have acquired it. We can't test for stale locks across +host boundaries, so you'll have to clean this up manually. Lock file: $config.LOCK_FILE Lock host: $hostname @@ -217,10 +216,10 @@ Lock host: $hostname Exiting.""") else: assert status is WatcherState.none, ( - 'Invalid enum value: %s' % status) + 'Invalid enum value: ${0}'.format(status)) hostname, pid, tempfile = lock.details message = _("""\ -For unknown reasons, the master qrunner lock could not be acquired. +For unknown reasons, the master lock could not be acquired. Lock file: $config.LOCK_FILE @@ -253,7 +252,7 @@ class PIDWatcher: :type pid: int :param info: The process information. :type info: 4-tuple consisting of - (queue-runner-name, slice-number, slice-count, restart-count) + (runner-name, slice-number, slice-count, restart-count) """ old_info = self._pids.get(pid) assert old_info is None, ( @@ -269,7 +268,7 @@ class PIDWatcher: :type pid: int :return: The process information. :rtype: 4-tuple consisting of - (queue-runner-name, slice-number, slice-count, restart-count) + (runner-name, slice-number, slice-count, restart-count) :raise KeyError: if the process id is not being tracked. """ return self._pids.pop(pid) @@ -285,7 +284,7 @@ class PIDWatcher: :return: The process information, or None if the process id is not being tracked. :rtype: 4-tuple consisting of - (queue-runner-name, slice-number, slice-count, restart-count) + (runner-name, slice-number, slice-count, restart-count) """ return self._pids.pop(pid, None) @@ -302,7 +301,7 @@ class Loop: def install_signal_handlers(self): """Install various signals handlers for control from the master.""" - log = logging.getLogger('mailman.qrunner') + log = logging.getLogger('mailman.runner') # Set up our signal handlers. Also set up a SIGALRM handler to # refresh the lock once per day. The lock lifetime is 1 day + 6 hours # so this should be plenty. @@ -311,7 +310,7 @@ class Loop: signal.alarm(SECONDS_IN_A_DAY) signal.signal(signal.SIGALRM, sigalrm_handler) signal.alarm(SECONDS_IN_A_DAY) - # SIGHUP tells the qrunners to close and reopen their log files. + # SIGHUP tells the runners to close and reopen their log files. def sighup_handler(signum, frame): reopen() for pid in self._kids: @@ -339,14 +338,14 @@ class Loop: signal.signal(signal.SIGINT, sigint_handler) def _start_runner(self, spec): - """Start a queue runner. + """Start a runner. - All arguments are passed to the qrunner process. + All arguments are passed to the process. - :param spec: A queue runner spec, in a format acceptable to - bin/qrunner's --runner argument, e.g. name:slice:count + :param spec: A runner spec, in a format acceptable to + bin/runner's --runner argument, e.g. name:slice:count :type spec: string - :return: The process id of the child queue runner. + :return: The process id of the child runner. :rtype: int """ pid = os.fork() @@ -355,77 +354,77 @@ class Loop: return pid # Child. # - # Set the environment variable which tells the qrunner that it's + # Set the environment variable which tells the runner that it's # running under bin/master control. This subtly changes the error - # behavior of bin/qrunner. + # behavior of bin/runner. os.environ['MAILMAN_UNDER_MASTER_CONTROL'] = '1' # Craft the command line arguments for the exec() call. rswitch = '--runner=' + spec - # Wherever master lives, so too must live the qrunner script. - exe = os.path.join(config.BIN_DIR, 'qrunner') + # Wherever master lives, so too must live the runner script. + exe = os.path.join(config.BIN_DIR, 'runner') # config.PYTHON, which is the absolute path to the Python interpreter, # must be given as argv[0] due to Python's library search algorithm. args = [sys.executable, sys.executable, exe, rswitch] if self._config_file is not None: args.extend(['-C', self._config_file]) - log = logging.getLogger('mailman.qrunner') + log = logging.getLogger('mailman.runner') log.debug('starting: %s', args) os.execl(*args) # We should never get here. raise RuntimeError('os.execl() failed') - def start_qrunners(self, qrunner_names=None): - """Start all the configured qrunners. + def start_runners(self, runner_names=None): + """Start all the configured runners. - :param qrunners: If given, a sequence of queue runner names to start. - If not given, this sequence is taken from the configuration file. - :type qrunners: a sequence of strings + :param runners: If given, a sequence of runner names to start. If not + given, this sequence is taken from the configuration file. + :type runners: a sequence of strings """ - if not qrunner_names: - qrunner_names = [] - for qrunner_config in config.qrunner_configs: - # Strip off the 'qrunner.' prefix. - assert qrunner_config.name.startswith('qrunner.'), ( - 'Unexpected qrunner configuration section name: %s', - qrunner_config.name) - qrunner_names.append(qrunner_config.name[8:]) - # For each qrunner we want to start, find their config section, which + if not runner_names: + runner_names = [] + for runner_config in config.runner_configs: + # Strip off the 'runner.' prefix. + assert runner_config.name.startswith('runner.'), ( + 'Unexpected runner configuration section name: {0}'.format( + runner_config.name)) + runner_names.append(runner_config.name[7:]) + # For each runner we want to start, find their config section, which # will tell us the name of the class to instantiate, along with the # number of hash space slices to manage. - for name in qrunner_names: - section_name = 'qrunner.' + name + for name in runner_names: + section_name = 'runner.' + name # Let AttributeError propagate. - qrunner_config = getattr(config, section_name) - if not as_boolean(qrunner_config.start): + runner_config = getattr(config, section_name) + if not as_boolean(runner_config.start): continue - # Find out how many qrunners to instantiate. This must be a power + # Find out how many runners to instantiate. This must be a power # of 2. - count = int(qrunner_config.instances) + count = int(runner_config.instances) assert (count & (count - 1)) == 0, ( - 'Queue runner "%s", not a power of 2: %s', name, count) + 'Runner "{0}", not a power of 2: {1}'.format(name, count)) for slice_number in range(count): - # qrunner name, slice #, # of slices, restart count + # runner name, slice #, # of slices, restart count info = (name, slice_number, count, 0) - spec = '%s:%d:%d' % (name, slice_number, count) + spec = '{0}:{1:d}:{2:d}'.format(name, slice_number, count) pid = self._start_runner(spec) - log = logging.getLogger('mailman.qrunner') - log.debug('[%d] %s', pid, spec) + log = logging.getLogger('mailman.runner') + log.debug('[{0:d}] {1}'.format(pid, spec)) self._kids.add(pid, info) def _pause(self): """Sleep until a signal is received.""" # Sleep until a signal is received. This prevents the master from - # existing immediately even if there are no qrunners (as happens in - # the test suite). + # exiting immediately even if there are no runners (as happens in the + # test suite). signal.pause() def loop(self): """Main loop. - Wait until all the qrunners have exited, restarting them if necessary - and configured to do so. + Wait until all the runner subprocesses have exited, restarting them if + necessary and configured to do so. """ - log = logging.getLogger('mailman.qrunner') + log = logging.getLogger('mailman.runner') self._pause() while True: try: @@ -451,8 +450,8 @@ class Loop: # because of a failure (i.e. no exit signal), and the no-restart # command line switch was not given. This lets us better handle # runaway restarts (e.g. if the subprocess had a syntax error!) - qrname, slice_number, count, restarts = self._kids.pop(pid) - config_name = 'qrunner.' + qrname + rname, slice_number, count, restarts = self._kids.pop(pid) + config_name = 'runner.' + rname restart = False if why == signal.SIGUSR1 and self._restartable: restart = True @@ -464,25 +463,25 @@ class Loop: # Are we permanently non-restartable? log.debug("""\ Master detected subprocess exit -(pid: %d, why: %s, class: %s, slice: %d/%d) %s""", - pid, why, qrname, slice_number + 1, count, +(pid: {0:d}, why: {1}, class: {2}, slice: {3:d}/{4:d}) {5}""", + pid, why, rname, slice_number + 1, count, ('[restarting]' if restart else '')) - # See if we've reached the maximum number of allowable restarts + # See if we've reached the maximum number of allowable restarts. if restarts > max_restarts: log.info("""\ -qrunner %s reached maximum restart limit of %d, not restarting.""", - qrname, max_restarts) +Runner {0} reached maximum restart limit of {1:d}, not restarting.""", + rname, max_restarts) # Now perhaps restart the process unless it exited with a # SIGTERM or we aren't restarting. if restart: - spec = '%s:%d:%d' % (qrname, slice_number, count) + spec = '{0}:{1:d}:{2:d}'.format(rname, slice_number, count) new_pid = self._start_runner(spec) - new_info = (qrname, slice_number, count, restarts) + new_info = (rname, slice_number, count, restarts) self._kids.add(new_pid, new_info) def cleanup(self): """Ensure that all children have exited.""" - log = logging.getLogger('mailman.qrunner') + log = logging.getLogger('mailman.runner') # Send SIGTERMs to all the child processes and wait for them all to # exit. for pid in self._kids: @@ -511,10 +510,9 @@ def main(): options = ScriptOptions() options.initialize() - - # Acquire the master lock, exiting if we can't acquire it. We'll let the - # caller handle any clean up or lock breaking. No with statement here - # because Lock's constructor doesn't support a timeout. + # Acquire the master lock, exiting if we can't. We'll let the caller + # handle any clean up or lock breaking. No `with` statement here because + # Lock's constructor doesn't support a timeout. lock = acquire_lock(options.options.force) try: with open(config.PID_FILE, 'w') as fp: @@ -522,7 +520,7 @@ def main(): loop = Loop(lock, options.options.restartable, options.options.config) loop.install_signal_handlers() try: - loop.start_qrunners(options.options.runners) + loop.start_runners(options.options.runners) loop.loop() finally: loop.cleanup() diff --git a/src/mailman/bin/qrunner.py b/src/mailman/bin/runner.py index 2e20cee61..b27ff04ea 100644 --- a/src/mailman/bin/qrunner.py +++ b/src/mailman/bin/runner.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""The queue runner.""" +"""The runner process.""" from __future__ import absolute_import, unicode_literals @@ -38,7 +38,6 @@ from mailman.options import Options from mailman.utilities.modules import find_name -COMMASPACE = ', ' log = None @@ -69,15 +68,20 @@ def r_callback(option, opt, value, parser): class ScriptOptions(Options): - """Options for bin/qrunner.""" + """Options for bin/runner.""" usage = _("""\ -Run one or more queue runners, once or repeatedly. +Start one or more runners. -Each named runner class is run in round-robin fashion. In other words, the -first named runner is run to consume all the files currently in its directory. -When that qrunner is done, the next one is run to consume all the files in -/its/ directory, and so on. The number of total iterations can be given on -the command line. +The runner named on the command line is started, and it can either run through +its main loop once (for those runners that support this) or continuously. The +latter is how the master runner starts all its subprocesses. + +When more than one runner is specified on the command line, they are each run +in round-robin fashion. All runners must support running its main loop once. +In other words, the first named runner is run once. When that runner is done, +the next one is run to consume all the files in *its* directory, and so on. +The number of total iterations can be given on the command line. This mode of +operation is primarily for debugging purposes. Usage: %prog [options] @@ -98,27 +102,32 @@ subtly changes some error handling behavior. type='string', default=[], action='callback', callback=r_callback, help=_("""\ -Run the named queue runner, which must be one of the strings returned by the --l option. Optional slice:range if given, is used to assign multiple qrunner -processes to a queue. range is the total number of qrunners for this queue -while slice is the number of this qrunner from [0..range). +Start the named runner, which must be one of the strings returned by the -l +option. + +For runners that manage a queue directory, optional slice:range if given, is +used to assign multiple runner processes to that queue. range is the total +number of runners for the queue while slice is the number of this runner from +[0..range). For runners that do not manage a queue, slice and range are +ignored. -When using the slice:range form, you must ensure that each qrunner for the +When using the slice:range form, you must ensure that each runner for the queue is given the same range value. If slice:runner is not given, then 1:1 is used. -Multiple -r options may be given, in which case each qrunner will run once in -round-robin fashion. The special runner 'All' is shorthand for a qrunner for -each listed by the -l option.""")) +Multiple -r options may be given, in which case each runner will run once in +round-robin fashion. The special runner 'All' is shorthand for running all +named runners listed by the -l option.""")) self.parser.add_option( '-o', '--once', default=False, action='store_true', help=_("""\ -Run each named queue runner exactly once through its main loop. Otherwise, -each queue runner runs indefinitely, until the process receives signal.""")) +Run each named runner exactly once through its main loop. Otherwise, each +runner runs indefinitely, until the process receives signal. This is not +compatible with runners that cannot be run once.""")) self.parser.add_option( '-l', '--list', default=False, action='store_true', - help=_('List the available queue runner names and exit.')) + help=_('List the available runner names and exit.')) self.parser.add_option( '-v', '--verbose', default=0, action='count', help=_("""\ @@ -133,21 +142,21 @@ Display more debugging information to the log file.""")) -def make_qrunner(name, slice, range, once=False): +def make_runner(name, slice, range, once=False): # Several conventions for specifying the runner name are supported. It # could be one of the shortcut names. If the name is a full module path, # use it explicitly. If the name starts with a dot, it's a class name - # relative to the Mailman.queue package. - qrunner_config = getattr(config, 'qrunner.' + name, None) - if qrunner_config is not None: + # relative to the Mailman.runner package. + runner_config = getattr(config, 'runner.' + name, None) + if runner_config is not None: # It was a shortcut name. - class_path = qrunner_config['class'] + class_path = runner_config['class'] elif name.startswith('.'): class_path = 'mailman.runners' + name else: class_path = name try: - qrclass = find_name(class_path) + runner_class = find_name(class_path) except ImportError: if os.environ.get('MAILMAN_UNDER_MASTER_CONTROL') is not None: # Exit with SIGTERM exit code so the master watcher won't try to @@ -159,13 +168,11 @@ def make_qrunner(name, slice, range, once=False): raise if once: # Subclass to hack in the setting of the stop flag in _do_periodic() - class Once(qrclass): + class Once(runner_class): def _do_periodic(self): self.stop() - qrunner = Once(name, slice) - else: - qrunner = qrclass(name, slice) - return qrunner + return Once(name, slice) + return runner_class(name, slice) @@ -174,34 +181,35 @@ def set_signals(loop): Signals caught are: SIGTERM, SIGINT, SIGUSR1 and SIGHUP. The latter is used to re-open the log files. SIGTERM and SIGINT are treated exactly the - same -- they cause qrunner to exit with no restart from the master. - SIGUSR1 also causes qrunner to exit, but the master watcher will restart - it in that case. + same -- they cause the runner to exit with no restart from the master. + SIGUSR1 also causes the runner to exit, but the master watcher will + restart it in that case. - :param loop: A loop queue runner instance. + :param loop: A runner instance. + :type loop: `IRunner` """ def sigterm_handler(signum, frame): - # Exit the qrunner cleanly + # Exit the runner cleanly loop.stop() loop.status = signal.SIGTERM - log.info('%s qrunner caught SIGTERM. Stopping.', loop.name()) + log.info('%s runner caught SIGTERM. Stopping.', loop.name()) signal.signal(signal.SIGTERM, sigterm_handler) def sigint_handler(signum, frame): - # Exit the qrunner cleanly + # Exit the runner cleanly loop.stop() loop.status = signal.SIGINT - log.info('%s qrunner caught SIGINT. Stopping.', loop.name()) + log.info('%s runner caught SIGINT. Stopping.', loop.name()) signal.signal(signal.SIGINT, sigint_handler) def sigusr1_handler(signum, frame): - # Exit the qrunner cleanly + # Exit the runner cleanly loop.stop() loop.status = signal.SIGUSR1 - log.info('%s qrunner caught SIGUSR1. Stopping.', loop.name()) + log.info('%s runner caught SIGUSR1. Stopping.', loop.name()) signal.signal(signal.SIGUSR1, sigusr1_handler) # SIGHUP just tells us to rotate our log files. def sighup_handler(signum, frame): reopen() - log.info('%s qrunner caught SIGHUP. Reopening logs.', loop.name()) + log.info('%s runner caught SIGHUP. Reopening logs.', loop.name()) signal.signal(signal.SIGHUP, sighup_handler) @@ -214,7 +222,7 @@ def main(): if options.options.list: descriptions = {} - for section in config.qrunner_configs: + for section in config.runner_configs: ignore, dot, shortname = section.name.rpartition('.') ignore, dot, classname = getattr(section, 'class').rpartition('.') descriptions[shortname] = classname @@ -227,30 +235,30 @@ def main(): # Fast track for one infinite runner. if len(options.options.runners) == 1 and not options.options.once: - qrunner = make_qrunner(*options.options.runners[0]) + runner = make_runner(*options.options.runners[0]) class Loop: status = 0 - def __init__(self, qrunner): - self._qrunner = qrunner + def __init__(self, runner): + self._runner = runner def name(self): - return self._qrunner.__class__.__name__ + return self._runner.__class__.__name__ def stop(self): - self._qrunner.stop() - loop = Loop(qrunner) - if qrunner.intercept_signals: + self._runner.stop() + loop = Loop(runner) + if runner.intercept_signals: set_signals(loop) # Now start up the main loop - log = logging.getLogger('mailman.qrunner') - log.info('%s qrunner started.', loop.name()) - qrunner.run() - log.info('%s qrunner exiting.', loop.name()) + log = logging.getLogger('mailman.runner') + log.info('%s runner started.', loop.name()) + runner.run() + log.info('%s runner exiting.', loop.name()) else: # Anything else we have to handle a bit more specially. - qrunners = [] + runners = [] for runner, rslice, rrange in options.options.runners: - qrunner = make_qrunner(runner, rslice, rrange, once=True) - qrunners.append(qrunner) - # This class is used to manage the main loop + runner = make_runner(runner, rslice, rrange, once=True) + runners.append(runner) + # This class is used to manage the main loop. class Loop: status = 0 def __init__(self): @@ -262,20 +270,20 @@ def main(): def isdone(self): return self._isdone loop = Loop() - if qrunner.intercept_signals: + if runner.intercept_signals: set_signals(loop) - log.info('Main qrunner loop started.') + log.info('Main runner loop started.') while not loop.isdone(): - for qrunner in qrunners: - # In case the SIGTERM came in the middle of this iteration + for runner in runners: + # In case the SIGTERM came in the middle of this iteration. if loop.isdone(): break if options.options.verbose: - log.info('Now doing a %s qrunner iteration', - qrunner.__class__.__bases__[0].__name__) - qrunner.run() + log.info('Now doing a %s runner iteration', + runner.__class__.__bases__[0].__name__) + runner.run() if options.options.once: break - log.info('Main qrunner loop exiting.') + log.info('Main runner loop exiting.') # All done sys.exit(loop.status) diff --git a/src/mailman/bin/tests/test_master.py b/src/mailman/bin/tests/test_master.py index 8f79250ed..a97817f24 100644 --- a/src/mailman/bin/tests/test_master.py +++ b/src/mailman/bin/tests/test_master.py @@ -64,7 +64,7 @@ class TestMasterLock(unittest.TestCase): state, lock = master.master_state(self.lock_file) self.assertEqual(state, master.WatcherState.none) # Acquire the lock as if another process had already started the - # master qrunner. + # master. my_lock.lock() try: state, lock = master.master_state(self.lock_file) diff --git a/src/mailman/chains/docs/moderation.txt b/src/mailman/chains/docs/moderation.txt index ce16d808d..d80f9bd2c 100644 --- a/src/mailman/chains/docs/moderation.txt +++ b/src/mailman/chains/docs/moderation.txt @@ -174,7 +174,7 @@ Nonmembers Registered nonmembers are handled very similarly to members, the main difference being that they usually have a default moderation action. This is -how the incoming queue runner adds sender addresses as nonmembers. +how the incoming runner adds sender addresses as nonmembers. >>> from zope.component import getUtility >>> from mailman.interfaces.usermanager import IUserManager diff --git a/src/mailman/commands/cli_control.py b/src/mailman/commands/cli_control.py index 45916894e..2caaff54e 100644 --- a/src/mailman/commands/cli_control.py +++ b/src/mailman/commands/cli_control.py @@ -42,7 +42,7 @@ from mailman.core.i18n import _ from mailman.interfaces.command import ICLISubCommand -qlog = logging.getLogger('mailman.qrunner') +qlog = logging.getLogger('mailman.runner') @@ -111,7 +111,7 @@ class Start: pid = os.fork() if pid: # parent - log(_("Starting Mailman's master queue runner")) + log(_("Starting Mailman's master runner")) return # child: Create a new session and become the session leader, but since # we won't be opening any terminal devices, don't do the @@ -190,7 +190,7 @@ class Stop(SignalCommand): """Stop the Mailman daemons.""" name = 'stop' - message = _("Shutting down Mailman's master queue runner") + message = _("Shutting down Mailman's master runner") signal = signal.SIGTERM @@ -198,7 +198,7 @@ class Reopen(SignalCommand): """Reopen the Mailman daemons.""" name = 'reopen' - message = _('Reopening the Mailman queue runners') + message = _('Reopening the Mailman runners') signal = signal.SIGHUP @@ -208,5 +208,5 @@ class Restart(SignalCommand): implements(ICLISubCommand) name = 'restart' - message = _('Restarting the Mailman queue runners') + message = _('Restarting the Mailman runners') signal = signal.SIGUSR1 diff --git a/src/mailman/commands/docs/control.txt b/src/mailman/commands/docs/control.rst index 78a15b7b2..70b870668 100644 --- a/src/mailman/commands/docs/control.txt +++ b/src/mailman/commands/docs/control.rst @@ -10,38 +10,38 @@ Set up ====== All we care about is the master process; normally it starts a bunch of -qrunners, but we don't care about any of them, so write a test configuration -file for the master that disables all the qrunners. +runners, but we don't care about any of them, so write a test configuration +file for the master that disables all the runners. >>> import shutil >>> from os.path import dirname, join - >>> config_file = join(dirname(config.filename), 'no-qrunners.cfg') + >>> config_file = join(dirname(config.filename), 'no-runners.cfg') >>> shutil.copyfile(config.filename, config_file) >>> with open(config_file, 'a') as fp: ... print >> fp, """\ - ... [qrunner.archive] + ... [runner.archive] ... start: no - ... [qrunner.bounces] + ... [runner.bounces] ... start: no - ... [qrunner.command] + ... [runner.command] ... start: no - ... [qrunner.in] + ... [runner.in] ... start: no - ... [qrunner.lmtp] + ... [runner.lmtp] ... start: no - ... [qrunner.news] + ... [runner.news] ... start: no - ... [qrunner.out] + ... [runner.out] ... start: no - ... [qrunner.pipeline] + ... [runner.pipeline] ... start: no - ... [qrunner.rest] + ... [runner.rest] ... start: no - ... [qrunner.retry] + ... [runner.retry] ... start: no - ... [qrunner.virgin] + ... [runner.virgin] ... start: no - ... [qrunner.digest] + ... [runner.digest] ... start: no ... """ @@ -59,11 +59,11 @@ Starting ... config = config_file >>> args = FakeArgs() -Starting the daemons prints a useful message and starts the master qrunner -watcher process in the background. +Starting the daemons prints a useful message and starts the master watcher +process in the background. >>> start.process(args) - Starting Mailman's master queue runner + Starting Mailman's master runner >>> import errno, os, time >>> from datetime import timedelta, datetime @@ -104,7 +104,7 @@ stops all the child processes too. >>> from mailman.commands.cli_control import Stop >>> stop = Stop() >>> stop.process(args) - Shutting down Mailman's master queue runner + Shutting down Mailman's master runner >>> def bury_master(): ... until = timedelta(seconds=10) + datetime.now() diff --git a/src/mailman/commands/docs/info.txt b/src/mailman/commands/docs/info.txt index 9658e93e6..83b3fe179 100644 --- a/src/mailman/commands/docs/info.txt +++ b/src/mailman/commands/docs/info.txt @@ -65,10 +65,10 @@ The File System Hierarchy layout is the same every by definition. EXT_DIR = /etc/mailman.d LIST_DATA_DIR = /var/lib/mailman/lists LOCK_DIR = /var/lock/mailman - LOCK_FILE = /var/lock/mailman/master-qrunner.lck + LOCK_FILE = /var/lock/mailman/master.lck LOG_DIR = /var/log/mailman MESSAGES_DIR = /var/lib/mailman/messages - PID_FILE = /var/run/mailman/master-qrunner.pid + PID_FILE = /var/run/mailman/master.pid PRIVATE_ARCHIVE_FILE_DIR = /var/lib/mailman/archives/private PUBLIC_ARCHIVE_FILE_DIR = /var/lib/mailman/archives/public QUEUE_DIR = /var/spool/mailman diff --git a/src/mailman/commands/docs/status.txt b/src/mailman/commands/docs/status.txt index 6deb7fdc0..7587157bc 100644 --- a/src/mailman/commands/docs/status.txt +++ b/src/mailman/commands/docs/status.txt @@ -18,18 +18,18 @@ The status is printed to stdout and a status code is returned. GNU Mailman is not running 0 -We can simulate the master queue runner starting up by acquiring its lock. +We can simulate the master starting up by acquiring its lock. >>> from flufl.lock import Lock >>> lock = Lock(config.LOCK_FILE) >>> lock.lock() -Getting the status confirms that the master queue runner is running. +Getting the status confirms that the master is running. >>> status.process(FakeArgs) GNU Mailman is running (master pid: ... -We shutdown the master queue runner, and confirm the status. +We shut down the master and confirm the status. >>> lock.unlock() >>> status.process(FakeArgs) diff --git a/src/mailman/config/config.py b/src/mailman/config/config.py index 4b2e9749d..c687777a3 100644 --- a/src/mailman/config/config.py +++ b/src/mailman/config/config.py @@ -160,8 +160,8 @@ class Configuration: # environment is not set. Because the var_dir setting in the config # file could be a relative path, and because 'bin/mailman start' # chdirs to $VAR_DIR, without this subprocesses bin/master and - # bin/qrunner will create $VAR_DIR hierarchies under $VAR_DIR when - # that path is relative. + # bin/runner will create $VAR_DIR hierarchies under $VAR_DIR when that + # path is relative. var_dir = os.environ.get('MAILMAN_VAR_DIR', category.var_dir) substitutions = dict( argv = bin_dir, @@ -237,9 +237,9 @@ class Configuration: makedirs(directory) @property - def qrunner_configs(self): - """Iterate over all the qrunner configuration sections.""" - for section in self._config.getByCategory('qrunner', []): + def runner_configs(self): + """Iterate over all the runner configuration sections.""" + for section in self._config.getByCategory('runner', []): yield section @property diff --git a/src/mailman/config/mailman.cfg b/src/mailman/config/mailman.cfg index fba1d333e..8f64365e8 100644 --- a/src/mailman/config/mailman.cfg +++ b/src/mailman/config/mailman.cfg @@ -37,60 +37,60 @@ log_dir: /var/log/mailman lock_dir: /var/lock/mailman etc_dir: /etc ext_dir: /etc/mailman.d -pid_file: /var/run/mailman/master-qrunner.pid +pid_file: /var/run/mailman/master.pid [language.en] -[qrunner.archive] +[runner.archive] class: mailman.runners.archive.ArchiveRunner -[qrunner.bad] +[runner.bad] class: mailman.runners.fake.BadRunner # The bad runner is just a placeholder for its switchboard. start: no -[qrunner.bounces] +[runner.bounces] class: mailman.runners.bounce.BounceRunner -[qrunner.command] +[runner.command] class: mailman.runners.command.CommandRunner -[qrunner.in] +[runner.in] class: mailman.runners.incoming.IncomingRunner -[qrunner.lmtp] +[runner.lmtp] class: mailman.runners.lmtp.LMTPRunner -[qrunner.maildir] +[runner.maildir] class: mailman.runners.maildir.MaildirRunner # This is still experimental. start: no -[qrunner.news] +[runner.news] class: mailman.runners.news.NewsRunner -[qrunner.out] +[runner.out] class: mailman.runners.outgoing.OutgoingRunner -[qrunner.pipeline] +[runner.pipeline] class: mailman.runners.pipeline.PipelineRunner -[qrunner.rest] +[runner.rest] class: mailman.runners.rest.RESTRunner -[qrunner.retry] +[runner.retry] class: mailman.runners.retry.RetryRunner sleep_time: 15m -[qrunner.shunt] +[runner.shunt] class: mailman.runners.fake.ShuntRunner # The shunt runner is just a placeholder for its switchboard. start: no -[qrunner.virgin] +[runner.virgin] class: mailman.runners.virgin.VirginRunner -[qrunner.digest] +[runner.digest] class: mailman.runners.digest.DigestRunner [style.default] diff --git a/src/mailman/config/schema.cfg b/src/mailman/config/schema.cfg index 2e5bfb2c5..40c4756c2 100644 --- a/src/mailman/config/schema.cfg +++ b/src/mailman/config/schema.cfg @@ -80,7 +80,7 @@ layout: dev var_dir: /var/tmp/mailman # This is where the Mailman queue files directories will be created. queue_dir: $var_dir/queue -# This is the directory containing the Mailman 'qrunner' and 'master' commands +# This is the directory containing the Mailman 'runner' and 'master' commands # if set to the string '$argv', it will be taken as the directory containing # the 'bin/mailman' command. bin_dir: $argv @@ -113,10 +113,10 @@ template_dir: :source: # defined. For these, the directory containing the file must already exist, # or be one of the directories created by Mailman as per above. # -# This is where PID file for the master queue runner is stored. -pid_file: $var_dir/master-qrunner.pid +# This is where PID file for the master runner is stored. +pid_file: $var_dir/master.pid # Lock file. -lock_file: $lock_dir/master-qrunner.lck +lock_file: $lock_dir/master.lck [devmode] @@ -130,7 +130,7 @@ enabled: no # enabled. This way messages can't be accidentally sent to real addresses. recipient: -# This gets set by the testing layers so that the qrunner subprocesses produce +# This gets set by the testing layers so that the runner subprocesses produce # predictable dates and times. testing: no @@ -162,28 +162,31 @@ password_length: 8 user_friendly_passwords: yes -[qrunner.master] -# Define which process queue runners, and how many of them, to start. +[runner.master] +# Define which runners, and how many of them, to start. -# The full import path to the class for this queue runner. +# The full import path to the class for this runner. class: mailman.core.runner.Runner -# The directory path that this queue runner scans. +# The queue directory path that this runner scans. This is ignored for +# runners that don't manage a queue directory. path: $QUEUE_DIR/$name -# The number of parallel queue runners. This must be a power of 2. +# The number of parallel runners. This must be a power of 2. This is ignored +# for runners that don't manage a queue directory. instances: 1 -# Whether to start this queue runner or not. +# Whether to start this runner or not. start: yes -# The maximum number of restarts for this queue runner. When the runner exits +# The maximum number of restarts for this runner. When the runner exits # because of an error or other unexpected problem, it is automatically # restarted, until the maximum number of restarts has been reached. max_restarts: 10 -# The sleep interval for the queue runner. It wakes up once every interval to -# process the files in its slice of the queue directory. +# The sleep interval for the runner. It wakes up once every interval to +# process the files in its slice of the queue directory. Some runners may +# ignore this. sleep_time: 1s [database] @@ -227,7 +230,7 @@ debug: no # - locks -- Lock state changes # - mischief -- Various types of hostile activity # - post -- Information about messages posted to mailing lists -# - qrunner -- qrunner start/stops +# - runner -- Runner process start/stops # - smtp -- Successful SMTP activity # - smtp-failure -- Unsuccessful SMTP activity # - subscribe -- Information about leaves/joins @@ -261,7 +264,7 @@ level: info [logging.mischief] -[logging.qrunner] +[logging.runner] [logging.smtp] path: smtp.log @@ -508,7 +511,7 @@ postfix_map_cmd: /usr/sbin/postmap [bounces] -# How often should the bounce qrunner process queued detected bounces? +# How often should the bounce runner process queued detected bounces? register_bounces_every: 15m diff --git a/src/mailman/core/docs/runner.txt b/src/mailman/core/docs/runner.rst index 4262dc87a..00781578f 100644 --- a/src/mailman/core/docs/runner.txt +++ b/src/mailman/core/docs/runner.rst @@ -1,30 +1,32 @@ -============= -Queue runners -============= +======= +Runners +======= -The queue runners (*qrunner*) are the processes that move messages around the -Mailman system. Each qrunner is responsible for a slice of the hash space in -a queue directory. It processes all the files in its slice, sleeps a little -while, then wakes up and runs through its queue files again. +The *runners* are the processes that perform long-running tasks, such as +moving messages around the Mailman queues. Some runners don't manage queues, +such as the LMTP and REST API handling runners. Each runner that manages a +queue directory though, is responsible for a slice of the hash space. It +processes all the files in its slice, sleeps a little while, then wakes up and +runs through its queue files again. Basic architecture ================== -The basic architecture of qrunner is implemented in the base class that all +The basic architecture of runner is implemented in the base class that all runners inherit from. This base class implements a ``.run()`` method that runs continuously in a loop until the ``.stop()`` method is called. - >>> mlist = create_list('_xtest@example.com') + >>> mlist = create_list('test@example.com') -Here is a very simple derived qrunner class. Queue runners use a -configuration section in the configuration files to determine run -characteristics, such as the queue directory to use. Here we push a -configuration section for the test runner. +Here is a very simple derived runner class. Runners use a configuration +section in the configuration files to determine run characteristics, such as +the queue directory to use. Here we push a configuration section for the test +runner. :: >>> config.push('test-runner', """ - ... [qrunner.test] + ... [runner.test] ... max_restarts: 1 ... """) @@ -43,12 +45,12 @@ configuration section for the test runner. >>> runner = TestableRunner('test') -This qrunner doesn't do much except run once, storing the message and metadata +This runner doesn't do much except run once, storing the message and metadata on instance variables. >>> msg = message_from_string("""\ ... From: aperson@example.com - ... To: _xtest@example.com + ... To: test@example.com ... ... A test message. ... """) @@ -58,7 +60,7 @@ on instance variables. >>> runner.run() >>> print runner.msg.as_string() From: aperson@example.com - To: _xtest@example.com + To: test@example.com <BLANKLINE> A test message. <BLANKLINE> @@ -67,7 +69,7 @@ on instance variables. bar : no foo : yes lang : en - listname : _xtest@example.com + listname : test@example.com version : 3 XXX More of the Runner API should be tested. diff --git a/src/mailman/core/logging.py b/src/mailman/core/logging.py index 0ff129897..09efd7771 100644 --- a/src/mailman/core/logging.py +++ b/src/mailman/core/logging.py @@ -130,8 +130,7 @@ def initialize(propagate=None): log_format = logger_config.format log_datefmt = logger_config.datefmt # Propagation to the root logger is how we handle logging to stderr - # when the queue runners are not run as a subprocess of 'bin/mailman - # start'. + # when the runners are not run as a subprocess of 'bin/mailman start'. log.propagate = (as_boolean(logger_config.propagate) if propagate is None else propagate) # Set the logger's level. diff --git a/src/mailman/core/runner.py b/src/mailman/core/runner.py index 3d876ac3d..c24563e68 100644 --- a/src/mailman/core/runner.py +++ b/src/mailman/core/runner.py @@ -54,15 +54,16 @@ class Runner: intercept_signals = True def __init__(self, name, slice=None): - """Create a queue runner. + """Create a runner. - :param slice: The slice number for this queue runner. This is passed - directly to the underlying `ISwitchboard` object. + :param slice: The slice number for this runner. This is passed + directly to the underlying `ISwitchboard` object. This is ignored + for runners that don't manage a queue. :type slice: int or None """ # Grab the configuration section. self.name = name - section = getattr(config, 'qrunner.' + name) + section = getattr(config, 'runner.' + name) substitutions = config.paths substitutions['name'] = name self.queue_directory = expand(section.path, substitutions) @@ -87,7 +88,7 @@ class Runner: def run(self): """See `IRunner`.""" - # Start the main loop for this queue runner. + # Start the main loop for this runner. try: while True: # Once through the loop that processes all the files in the diff --git a/src/mailman/core/switchboard.py b/src/mailman/core/switchboard.py index 5d9eb65ce..4c7b31a67 100644 --- a/src/mailman/core/switchboard.py +++ b/src/mailman/core/switchboard.py @@ -69,10 +69,10 @@ class Switchboard: @staticmethod def initialize(): """Initialize the global switchboards for input/output.""" - for conf in config.qrunner_configs: + for conf in config.runner_configs: name = conf.name.split('.')[-1] assert name not in config.switchboards, ( - 'Duplicate qrunner name: {0}'.format(name)) + 'Duplicate runner name: {0}'.format(name)) substitutions = config.paths substitutions['name'] = name path = expand(conf.path, substitutions) @@ -118,7 +118,7 @@ class Switchboard: _metadata = {} # Calculate the SHA hexdigest of the message to get a unique base # filename. We're also going to use the digest as a hash into the set - # of parallel qrunner processes. + # of parallel runner processes. data = _metadata.copy() data.update(_kws) listname = data.get('listname', '--nolist--') diff --git a/src/mailman/docs/START.txt b/src/mailman/docs/START.txt index 678122205..d7ef55b22 100644 --- a/src/mailman/docs/START.txt +++ b/src/mailman/docs/START.txt @@ -84,7 +84,7 @@ this, Mailman will also create any necessary run-time directories and log files. Try ``bin/mailman --help`` for more details. You can use the commands -``bin/mailman start`` to start the queue runner daemons, and of course +``bin/mailman start`` to start the runner subprocess daemons, and of course ``bin/mailman stop`` to stop them. The `web ui`_ is being developed as a separate, Django-based project. For diff --git a/src/mailman/interfaces/runner.py b/src/mailman/interfaces/runner.py index baf9bd2a1..1761f9426 100644 --- a/src/mailman/interfaces/runner.py +++ b/src/mailman/interfaces/runner.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Interface for queue runners.""" +"""Interface for runners.""" from __future__ import absolute_import, unicode_literals @@ -30,29 +30,28 @@ from zope.interface import Interface, Attribute class IRunner(Interface): - """The queue runner.""" + """The runner.""" def run(): - """Start the queue runner.""" + """Start the runner.""" def stop(): - """Stop the queue runner on the next iteration through the loop.""" + """Stop the runner on the next iteration through the loop.""" queue_directory = Attribute( 'The queue directory. Overridden in subclasses.') sleep_time = Attribute("""\ - The number of seconds this queue runner will sleep between iterations - through the main loop. If given, overrides - `config.QRUNNER_SLEEP_TIME` + The number of seconds this runner will sleep between iterations + through the main loop. """) intercept_signals = Attribute("""\ - Should the qrunner mechanism intercept signals? + Should the runner mechanism intercept signals? - In general, the qrunner catches SIGINT, SIGTERM, SIGUSR1, and SIGHUP - to manage the process. Some qrunners need to manage their own - signals, and set this attribute to False. + In general, the runner catches SIGINT, SIGTERM, SIGUSR1, and SIGHUP to + manage the process. Some runners need to manage their own signals, + and set this attribute to False. """) def _one_iteration(): @@ -76,15 +75,15 @@ class IRunner(Interface): def _clean_up(): """Clean up upon exit from the main processing loop. - Called when the queue runner's main loop is stopped, this should - perform any necessary resource deallocation. + Called when the runner's main loop is stopped, this should perform any + necessary resource deallocation. """ def _dispose(mlist, msg, msgdata): """Dispose of a single message destined for a mailing list. - Called for each message that the queue runner is responsible for, this - is the primary overridable method for processing each message. + Called for each message that the runner is responsible for, this is + the primary overridable method for processing each message. Subclasses, must provide implementation for this method. :param mlist: The mailing list this message is destined for. @@ -93,16 +92,16 @@ class IRunner(Interface): :type msg: `email.message.Message` :param msgdata: The message metadata. :type msgdata: dict - :return: True if the message should continue to be queue, False if the - message should be deleted automatically. + :return: True if the message should continue to be queued, False if + the message should be deleted automatically. :rtype: bool """ def _do_periodic(): """Do some arbitrary periodic processing. - Called every once in a while both from the queue runner's main loop, - and from the runner's hash slice processing loop. You can do whatever + Called every once in a while both from the runner's main loop, and + from the runner's hash slice processing loop. You can do whatever special periodic processing you want here. """ @@ -110,9 +109,9 @@ class IRunner(Interface): """Sleep for a little while. :param filecnt: The number of messages in the queue the last time - through. Queue runners can decide to continue to do work, or - sleep for a while based on this value. By default, the base queue - runner only snoozes when there was nothing to do last time around. + through. Runners can decide to continue to do work, or sleep for + a while based on this value. By default, the base runner only + snoozes when there was nothing to do last time around. :type filecnt: int """ diff --git a/src/mailman/model/docs/bounce.rst b/src/mailman/model/docs/bounce.rst index 41784cd9c..b1491e607 100644 --- a/src/mailman/model/docs/bounce.rst +++ b/src/mailman/model/docs/bounce.rst @@ -34,8 +34,8 @@ recorded. There is a suite of bounce detectors that are used to heuristically extract the bouncing email addresses. Various techniques are employed including VERP, -DSN, and magic. It is the bounce queue's responsibility to extract the set of -bouncing email addrsses. These are passed one-by-one to the registration +DSN, and magic. It is the bounce runner's responsibility to extract the set +of bouncing email addresses. These are passed one-by-one to the registration interface. >>> event = processor.register(mlist, 'anne@example.com', msg) diff --git a/src/mailman/pipeline/docs/digests.txt b/src/mailman/pipeline/docs/digests.txt index b0a44ec5b..d4d563180 100644 --- a/src/mailman/pipeline/docs/digests.txt +++ b/src/mailman/pipeline/docs/digests.txt @@ -110,4 +110,4 @@ The digest has been moved to a unique file. Test message 8 Test message 9 -Digests are actually crafted and sent by a separate digest queue runner. +Digests are actually crafted and sent by a separate digest runner. diff --git a/src/mailman/pipeline/to_usenet.py b/src/mailman/pipeline/to_usenet.py index 46ec21ed9..38ed70970 100644 --- a/src/mailman/pipeline/to_usenet.py +++ b/src/mailman/pipeline/to_usenet.py @@ -65,6 +65,6 @@ class ToUsenet: log.error('NNTP gateway improperly configured: %s', COMMASPACE.join(error)) return - # Put the message in the news runner's queue + # Put the message in the news runner's queue. config.switchboards['news'].enqueue( msg, msgdata, listname=mlist.fqdn_listname) diff --git a/src/mailman/rules/docs/moderation.txt b/src/mailman/rules/docs/moderation.txt index fdca04599..eacc1cff3 100644 --- a/src/mailman/rules/docs/moderation.txt +++ b/src/mailman/rules/docs/moderation.txt @@ -101,9 +101,9 @@ case the rule does not match. Unregistered nonmembers ======================= -The incoming queue runner ensures that all sender addresses are registered in -the system, but it is the moderation rule that subscribes nonmember addresses -to the mailing list if they are not already subscribed. +The incoming runner ensures that all sender addresses are registered in the +system, but it is the moderation rule that subscribes nonmember addresses to +the mailing list if they are not already subscribed. :: >>> from mailman.interfaces.usermanager import IUserManager @@ -132,7 +132,7 @@ cperson is neither a member, nor a nonmember of the mailing list. >>> dump_list(mlist.nonmembers.members, key=memberkey) <Member: Bart Person <bperson@example.com> on test@example.com as MemberRole.nonmember> - + However, when the nonmember moderation rule runs, it adds the cperson as a nonmember of the list. The rule also matches. diff --git a/src/mailman/runners/archive.py b/src/mailman/runners/archive.py index 0a9e104d8..1aa54d17c 100644 --- a/src/mailman/runners/archive.py +++ b/src/mailman/runners/archive.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Archive queue runner.""" +"""Archive runner.""" __metaclass__ = type __all__ = [ diff --git a/src/mailman/runners/bounce.py b/src/mailman/runners/bounce.py index 8902dcbbc..c2fd244b4 100644 --- a/src/mailman/runners/bounce.py +++ b/src/mailman/runners/bounce.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Bounce queue runner.""" +"""Bounce runner.""" import logging diff --git a/src/mailman/runners/command.py b/src/mailman/runners/command.py index 68aabc784..1c58fdccf 100644 --- a/src/mailman/runners/command.py +++ b/src/mailman/runners/command.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""-request robot command queue runner.""" +"""-request robot command runner.""" __metaclass__ = type __all__ = [ diff --git a/src/mailman/runners/digest.py b/src/mailman/runners/digest.py index 59ce1da3d..9745100cb 100644 --- a/src/mailman/runners/digest.py +++ b/src/mailman/runners/digest.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Digest queue runner.""" +"""Digest runner.""" from __future__ import absolute_import, unicode_literals @@ -291,7 +291,7 @@ class RFC1153Digester(Digester): class DigestRunner(Runner): - """The digest queue runner.""" + """The digest runner.""" def _dispose(self, mlist, msg, msgdata): """See `IRunner`.""" diff --git a/src/mailman/runners/docs/command.txt b/src/mailman/runners/docs/command.txt index 4bf06b350..3914069ff 100644 --- a/src/mailman/runners/docs/command.txt +++ b/src/mailman/runners/docs/command.txt @@ -1,11 +1,11 @@ -======================== -The command queue runner -======================== +================== +The command runner +================== -This queue runner's purpose is to process and respond to email commands. -Commands are extensible using the Mailman plug-in system, but Mailman comes -with a number of email commands out of the box. These are processed when a -message is sent to the list's ``-request`` address. +This runner's purpose is to process and respond to email commands. Commands +are extensible using the Mailman plug-in system, but Mailman comes with a +number of email commands out of the box. These are processed when a message +is sent to the list's ``-request`` address. >>> mlist = create_list('test@example.com') diff --git a/src/mailman/runners/docs/digester.txt b/src/mailman/runners/docs/digester.txt index c8f709a13..5a20db556 100644 --- a/src/mailman/runners/docs/digester.txt +++ b/src/mailman/runners/docs/digester.txt @@ -2,8 +2,8 @@ Digesting ========= -Mailman crafts and sends digests by a separate digest queue runner process. -This starts by a number of messages being posted to the mailing list. +Mailman crafts and sends digests by a separate digest runner process. This +starts by a number of messages being posted to the mailing list. :: >>> mlist = create_list('test@example.com') @@ -33,8 +33,8 @@ This starts by a number of messages being posted to the mailing list. >>> fill_digest() -The queue runner gets kicked off when a marker message gets dropped into the -digest queue. The message metadata points to the mailbox file containing the +The runner gets kicked off when a marker message gets dropped into the digest +queue. The message metadata points to the mailbox file containing the messages to put in the digest. :: @@ -69,8 +69,8 @@ There are 4 messages in the digest. >>> sum(1 for item in Mailbox(entry.msgdata['digest_path'])) 4 -When the queue runner runs, it processes the digest mailbox, crafting both the -plain text (RFC 1153) digest and the MIME digest. +When the runner runs, it processes the digest mailbox, crafting both the plain +text (RFC 1153) digest and the MIME digest. >>> from mailman.runners.digest import DigestRunner >>> from mailman.testing.helpers import make_testable_runner @@ -327,9 +327,7 @@ The marker message is sitting in the digest queue. version : 3 volume : 1 -The digest queue runner runs a loop, placing the two digests into the virgin -queue. -:: +The digest runner runs a loop, placing the two digests into the virgin queue. # Put the messages back in the queue for the runner to handle. >>> filebase = digestq.enqueue(entry.msg, entry.msgdata) diff --git a/src/mailman/runners/docs/incoming.txt b/src/mailman/runners/docs/incoming.txt index 22061044f..b16567c44 100644 --- a/src/mailman/runners/docs/incoming.txt +++ b/src/mailman/runners/docs/incoming.txt @@ -1,6 +1,6 @@ -========================= -The incoming queue runner -========================= +=================== +The incoming runner +=================== This runner's sole purpose in life is to decide the disposition of the message. It can either be accepted for delivery, rejected (i.e. bounced), @@ -20,7 +20,7 @@ above. Sender addresses ================ -The incoming queue runner ensures that the sender addresses on the message are +The incoming runner ensures that the sender addresses on the message are registered with the system. This is used for determining nonmember posting privileges. The addresses will not be linked to a user and will be unverified, so if the real user comes along later and claims the address, it @@ -57,7 +57,7 @@ mail server normally would. >>> from mailman.inject import inject_message >>> inject_message(mlist, msg) -The incoming queue runner runs until it is empty. +The incoming runner runs until it is empty. >>> from mailman.runners.incoming import IncomingRunner >>> from mailman.testing.helpers import make_testable_runner diff --git a/src/mailman/runners/docs/lmtp.txt b/src/mailman/runners/docs/lmtp.txt index c95c6aa2b..1ab42410b 100644 --- a/src/mailman/runners/docs/lmtp.txt +++ b/src/mailman/runners/docs/lmtp.txt @@ -9,7 +9,7 @@ with your mail server. Our LMTP server is fairly simple though; all it does is make sure that the message is destined for a valid endpoint, e.g. ``mylist-join@example.com``. -Let's start a testable LMTP queue runner. +Let's start a testable LMTP runner. >>> from mailman.testing import helpers >>> master = helpers.TestableMaster() @@ -18,7 +18,7 @@ Let's start a testable LMTP queue runner. It also helps to have a nice LMTP client. >>> lmtp = helpers.get_lmtp_client() - (220, '... Python LMTP queue runner 1.0') + (220, '... Python LMTP runner 1.0') >>> lmtp.lhlo('remote.example.org') (250, ...) diff --git a/src/mailman/runners/docs/news.txt b/src/mailman/runners/docs/news.txt index 7b573d39e..71febf95c 100644 --- a/src/mailman/runners/docs/news.txt +++ b/src/mailman/runners/docs/news.txt @@ -2,10 +2,10 @@ The news runner =============== -The news runner is the queue runner that gateways mailing list messages to an -NNTP newsgroup. One of the most important things this runner does is prepare -the message for Usenet (yes, I know that NNTP is not Usenet, but this runner -was originally written to gate to Usenet, which has its own rules). +The news runner gateways mailing list messages to an NNTP newsgroup. One of +the most important things this runner does is prepare the message for Usenet +(yes, I know that NNTP is not Usenet, but this runner was originally written +to gate to Usenet, which has its own rules). >>> mlist = create_list('_xtest@example.com') >>> mlist.linked_newsgroup = 'comp.lang.python' diff --git a/src/mailman/runners/docs/outgoing.txt b/src/mailman/runners/docs/outgoing.txt index a69fa36c5..b1ffb06a0 100644 --- a/src/mailman/runners/docs/outgoing.txt +++ b/src/mailman/runners/docs/outgoing.txt @@ -1,10 +1,10 @@ -===================== -Outgoing queue runner -===================== +=============== +Outgoing runner +=============== -The outgoing queue runner is the process that delivers messages to the -directly upstream SMTP server. It is this upstream SMTP server that performs -final delivery to the intended recipients. +The outgoing runner is the process that delivers messages to the directly +upstream SMTP server. It is this upstream SMTP server that performs final +delivery to the intended recipients. Messages that appear in the outgoing queue are processed individually through a *delivery module*, essentially a pluggable interface for determining how the @@ -59,7 +59,7 @@ destination mailing list name. Simulate that here too. ... tolist=True, ... listname=mlist.fqdn_listname) -Running the outgoing queue runner processes the message, delivering it to the +Running the outgoing runner processes the message, delivering it to the upstream SMTP. >>> from mailman.runners.outgoing import OutgoingRunner diff --git a/src/mailman/runners/incoming.py b/src/mailman/runners/incoming.py index c1f0d0e64..0ac907988 100644 --- a/src/mailman/runners/incoming.py +++ b/src/mailman/runners/incoming.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Incoming queue runner. +"""Incoming runner. This runner's sole purpose in life is to decide the disposition of the message. It can either be accepted for delivery, rejected (i.e. bounced), @@ -45,7 +45,7 @@ from mailman.interfaces.usermanager import IUserManager class IncomingRunner(Runner): - """The incoming queue runner.""" + """The incoming runner.""" def _dispose(self, mlist, msg, msgdata): """See `IRunner`.""" diff --git a/src/mailman/runners/lmtp.py b/src/mailman/runners/lmtp.py index 32d85b6ca..30d120d48 100644 --- a/src/mailman/runners/lmtp.py +++ b/src/mailman/runners/lmtp.py @@ -18,7 +18,7 @@ """Mailman LMTP runner (server). Most mail servers can be configured to deliver local messages via 'LMTP'[1]. -This module is actually an LMTP server rather than a standard queue runner. +This module is actually an LMTP server rather than a standard runner. The LMTP runner opens a local TCP port and waits for the mail server to connect to it. The messages it receives over LMTP are very minimally parsed @@ -46,7 +46,7 @@ from mailman.email.message import Message from mailman.interfaces.listmanager import IListManager elog = logging.getLogger('mailman.error') -qlog = logging.getLogger('mailman.qrunner') +qlog = logging.getLogger('mailman.runner') # We only care about the listname and the sub-addresses as in listname@ or @@ -83,7 +83,7 @@ ERR_502 = '502 Error: command HELO not implemented' ERR_550 = '550 Requested action not taken: mailbox unavailable' # XXX Blech -smtpd.__version__ = 'Python LMTP queue runner 1.0' +smtpd.__version__ = 'Python LMTP runner 1.0' @@ -185,7 +185,7 @@ class LMTPRunner(Runner, smtpd.SMTPServer): continue # The recipient is a valid mailing list. Find the subaddress # if there is one, and set things up to enqueue to the proper - # queue runner. + # queue. queue = None msgdata = dict(listname=listname, original_size=msg.original_size) diff --git a/src/mailman/runners/maildir.py b/src/mailman/runners/maildir.py index f0204f014..f9e0f41a9 100644 --- a/src/mailman/runners/maildir.py +++ b/src/mailman/runners/maildir.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Maildir pre-queue runner. +"""Maildir runner. Most MTAs can be configured to deliver messages to a `Maildir'[1]. This runner will read messages from a maildir's new/ directory and inject them into @@ -47,7 +47,8 @@ See the variable USE_MAILDIR in Defaults.py.in for enabling this delivery mechanism. """ -# NOTE: Maildir delivery is experimental in Mailman 2.1. +# NOTE: Maildir delivery is experimental in Mailman 2.1, and untested in +# Mailman 3. Instead, use LMTP delivery for Mailman 3. import os import errno diff --git a/src/mailman/runners/news.py b/src/mailman/runners/news.py index 0c79cea52..eb84f9e2d 100644 --- a/src/mailman/runners/news.py +++ b/src/mailman/runners/news.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""NNTP queue runner.""" +"""NNTP runner.""" import re import email diff --git a/src/mailman/runners/outgoing.py b/src/mailman/runners/outgoing.py index b1c71176b..f611ccf92 100644 --- a/src/mailman/runners/outgoing.py +++ b/src/mailman/runners/outgoing.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Outgoing queue runner.""" +"""Outgoing runner.""" import socket import logging @@ -45,7 +45,7 @@ smtp_log = logging.getLogger('mailman.smtp') class OutgoingRunner(Runner): - """The outgoing queue runner.""" + """The outgoing runner.""" def __init__(self, slice=None, numslices=1): super(OutgoingRunner, self).__init__(slice, numslices) diff --git a/src/mailman/runners/pipeline.py b/src/mailman/runners/pipeline.py index 6b9ad0a88..b088325b4 100644 --- a/src/mailman/runners/pipeline.py +++ b/src/mailman/runners/pipeline.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""The pipeline queue runner. +"""The pipeline runner. This runner's purpose is to take messages that have been approved for posting through the 'preparation pipeline'. This pipeline adds, deletes and modifies diff --git a/src/mailman/runners/tests/test_bounce.py b/src/mailman/runners/tests/test_bounce.py index 97123d1a4..bfe1664d6 100644 --- a/src/mailman/runners/tests/test_bounce.py +++ b/src/mailman/runners/tests/test_bounce.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Test the bounce queue runner.""" +"""Test the bounce runner.""" from __future__ import absolute_import, unicode_literals @@ -46,8 +46,8 @@ from mailman.testing.layers import ConfigLayer -class TestBounceQueue(unittest.TestCase): - """Test the bounce queue runner.""" +class TestBounceRunner(unittest.TestCase): + """Test the bounce runner.""" layer = ConfigLayer @@ -232,5 +232,5 @@ Message-Id: <third> def test_suite(): suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestBounceQueue)) + suite.addTest(unittest.makeSuite(TestBounceRunner)) return suite diff --git a/src/mailman/runners/tests/test_outgoing.py b/src/mailman/runners/tests/test_outgoing.py index 0036b4bd4..ddabfbef7 100644 --- a/src/mailman/runners/tests/test_outgoing.py +++ b/src/mailman/runners/tests/test_outgoing.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Test the outgoing queue runner.""" +"""Test the outgoing runner.""" from __future__ import absolute_import, unicode_literals @@ -55,10 +55,10 @@ from mailman.utilities.datetime import factory, now -def run_once(qrunner): +def run_once(runner): """Predicate for make_testable_runner(). - Ensures that the queue runner only runs once. + Ensures that the runner only runs once. """ return True @@ -92,8 +92,8 @@ Message-Id: <first> self._msgdata = {} def test_deliver_after(self): - # When the metadata has a deliver_after key in the future, the queue - # runner will re-enqueue the message rather than delivering it. + # When the metadata has a deliver_after key in the future, the runner + # will re-enqueue the message rather than delivering it. deliver_after = now() + timedelta(days=10) self._msgdata['deliver_after'] = deliver_after self._outq.enqueue(self._msg, self._msgdata, @@ -524,7 +524,7 @@ Message-Id: <first> msgdata = dict(last_recip_count=2, deliver_until=deliver_until) self._outq.enqueue(self._msg, msgdata, listname='test@example.com') - # Before the queue runner runs, several days pass. + # Before the runner runs, several days pass. factory.fast_forward(retry_period.days + 1) mark = LogFileMark('mailman.smtp') self._runner.run() diff --git a/src/mailman/runners/virgin.py b/src/mailman/runners/virgin.py index de5788a01..917e6f5dc 100644 --- a/src/mailman/runners/virgin.py +++ b/src/mailman/runners/virgin.py @@ -15,10 +15,10 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Virgin message queue runner. +"""Virgin runner. -This qrunner handles messages that the Mailman system gives virgin birth to. -E.g. acknowledgement responses to user posts or Replybot messages. They need +This runner handles messages that the Mailman system gives virgin birth to. +E.g. acknowledgment responses to user posts or replybot messages. They need to go through some minimal processing before they can be sent out to the recipient. """ diff --git a/src/mailman/testing/helpers.py b/src/mailman/testing/helpers.py index ed579e39c..1a168dd0c 100644 --- a/src/mailman/testing/helpers.py +++ b/src/mailman/testing/helpers.py @@ -66,20 +66,18 @@ from mailman.utilities.mailbox import Mailbox def make_testable_runner(runner_class, name=None, predicate=None): - """Create a queue runner that runs until its queue is empty. + """Create a runner that runs until its queue is empty. - :param runner_class: The queue runner's class. + :param runner_class: The runner class. :type runner_class: class :param name: Optional queue name; if not given, it is calculated from the class name. :type name: string or None :param predicate: Optional alternative predicate for deciding when to stop - the queue runner. When None (the default) it stops when the queue is - empty. + the runner. When None (the default) it stops when the queue is empty. :type predicate: callable that gets one argument, the queue runner. :return: A runner instance. """ - if name is None: assert runner_class.__name__.endswith('Runner'), ( 'Unparseable runner class name: %s' % runner_class.__name__) @@ -169,22 +167,22 @@ class TestableMaster(Master): # which would mean the signal.pause() never exits. pass - def start(self, *qrunners): + def start(self, *runners): """Start the master.""" - self.start_qrunners(qrunners) + self.start_runners(runners) self.thread.start() # Wait until all the children are definitely started. self.event.wait() def stop(self): """Stop the master by killing all the children.""" - for pid in self.qrunner_pids: + for pid in self.runner_pids: os.kill(pid, signal.SIGTERM) self.cleanup() self.thread.join() def loop(self): - """Wait until all the qrunners are actually running before looping.""" + """Wait until all the runners are actually running before looping.""" starting_kids = set(self._kids) while starting_kids: for pid in self._kids: @@ -207,8 +205,8 @@ class TestableMaster(Master): super(TestableMaster, self).loop() @property - def qrunner_pids(self): - """The pids of all the child qrunner processes.""" + def runner_pids(self): + """The pids of all the child runner processes.""" for pid in self._started_kids: yield pid diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py index 38d4c7f02..6ae9d195e 100644 --- a/src/mailman/testing/layers.py +++ b/src/mailman/testing/layers.py @@ -99,7 +99,7 @@ class ConfigLayer(MockAndMonkeyLayer): # We need a test configuration both for the foreground process and any # child processes that get spawned. lazr.config would allow us to do # it all in a string that gets pushed, and we'll do that for the - # foreground, but because we may be spawning processes (such as queue + # foreground, but because we may be spawning processes (such as # runners) we'll need a file that we can specify to the with the -C # option. Craft the full test configuration string here, push it, and # also write it out to a temp file for -C. diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg index ddb0f8440..8ed784621 100644 --- a/src/mailman/testing/testing.cfg +++ b/src/mailman/testing/testing.cfg @@ -25,40 +25,40 @@ incoming: mailman.testing.mta.FakeMTA [webservice] port: 9001 -[qrunner.archive] +[runner.archive] max_restarts: 1 -[qrunner.bounces] +[runner.bounces] max_restarts: 1 -[qrunner.command] +[runner.command] max_restarts: 1 -[qrunner.in] +[runner.in] max_restarts: 1 -[qrunner.lmtp] +[runner.lmtp] max_restarts: 1 -[qrunner.maildir] +[runner.maildir] max_restarts: 1 -[qrunner.news] +[runner.news] max_restarts: 1 -[qrunner.out] +[runner.out] max_restarts: 1 -[qrunner.pipeline] +[runner.pipeline] max_restarts: 1 -[qrunner.retry] +[runner.retry] max_restarts: 1 -[qrunner.shunt] +[runner.shunt] max_restarts: 1 -[qrunner.virgin] +[runner.virgin] max_restarts: 1 [archiver.prototype] |
