diff options
| -rw-r--r-- | Mailman/bin/docs/mailmanctl.txt | 27 | ||||
| -rw-r--r-- | Mailman/bin/master.py | 3 | ||||
| -rw-r--r-- | Mailman/tests/helpers.py | 81 |
3 files changed, 110 insertions, 1 deletions
diff --git a/Mailman/bin/docs/mailmanctl.txt b/Mailman/bin/docs/mailmanctl.txt new file mode 100644 index 000000000..bd9023371 --- /dev/null +++ b/Mailman/bin/docs/mailmanctl.txt @@ -0,0 +1,27 @@ +Mailman queue runner control +============================ + +Mailman has a number of queue runners which process messages in its queue file +directories. In normal operation, a command line script called 'mailmanctl' +is used to start, stop and manage the queue runners. mailmanctl actually is +just a wrapper around the real queue runner watcher script called master.py. + +Because master.py runs in the foreground, we can't start it directly, so we'll +start it via mailmanctl. + + >>> from Mailman.tests.helpers import Watcher + >>> watcher = Watcher() + >>> watcher.start() + + >>> import os + + # This will raise an exception if the process doesn't exist. + >>> os.kill(watcher.pid, 0) + +It's also easy to stop the queue runners via the mailmanctl program. + + >>> watcher.stop() + >>> os.kill(watcher.pid, 0) + Traceback (most recent call last): + ... + OSError: [Errno ...] No such process diff --git a/Mailman/bin/master.py b/Mailman/bin/master.py index e2a80934f..e4de11acc 100644 --- a/Mailman/bin/master.py +++ b/Mailman/bin/master.py @@ -350,9 +350,10 @@ qrunner %s reached maximum restart limit of %d, not restarting.""", # The child has already exited. log.info('ESRCH on pid: %d', pid) # Wait for all the children to go away. - while True: + while kids: try: pid, status = os.wait() + del kids[pid] except OSError, e: if e.errno == errno.ECHILD: break diff --git a/Mailman/tests/helpers.py b/Mailman/tests/helpers.py index 180ac8af8..3f35b73dc 100644 --- a/Mailman/tests/helpers.py +++ b/Mailman/tests/helpers.py @@ -17,8 +17,11 @@ """Various test helpers.""" +from __future__ import with_statement + __metaclass__ = type __all__ = [ + 'Watcher', 'digest_mbox', 'get_queue_messages', 'make_testable_runner', @@ -26,8 +29,14 @@ __all__ = [ import os +import time +import errno import mailbox +import subprocess + +from datetime import datetime, timedelta +from Mailman.configuration import config from Mailman.queue import Switchboard @@ -82,3 +91,75 @@ def digest_mbox(mlist): """ path = os.path.join(mlist.full_path, 'digest.mbox') return mailbox.mbox(path) + + + +class Watcher: + """A doctest stand-in for the queue file watcher.""" + + def __init__(self): + self.exe = os.path.join(config.BIN_DIR, 'mailmanctl') + self.returncode = None + self.stdout = None + self.stderr = None + self.pid = None + + def start(self): + """Start the watcher and wait until it actually starts.""" + process = subprocess.Popen( + (self.exe, '-C', config.filename, '-q', 'start')) + stdout, stderr = process.communicate() + # Wait until the pid file exists. + until = datetime.now() + timedelta(seconds=2) + while datetime.now() < until: + try: + with open(config.PIDFILE) as f: + pid = int(f.read().strip()) + break + except IOError, error: + if error.errno == errno.ENOENT: + time.sleep(0.1) + else: + raise + else: + # This will usually cause the doctest to fail. + return 'Time out' + # Now wait until the process actually exists. + until = datetime.now() + timedelta(seconds=2) + while datetime.now() < until: + try: + os.kill(pid, 0) + break + except OSError, error: + if error.errno == errno.ESRCH: + time.sleep(0.1) + else: + raise + else: + return 'Time out' + self.returncode = process.returncode + self.stdout = stdout + self.stderr = stderr + self.pid = pid + + def stop(self): + """Stop the watcher and wait until it actually stops.""" + process = subprocess.Popen( + (self.exe, '-C', config.filename, '-q', 'stop')) + stdout, stderr = process.communicate() + # Now wait until the process stops. + until = datetime.now() + timedelta(seconds=2) + while datetime.now() < until: + try: + os.kill(self.pid, 0) + time.sleep(0.1) + except OSError, error: + if error.errno == errno.ESRCH: + break + else: + raise + else: + return 'Time out' + self.returncode = process.returncode + self.stdout = stdout + self.stderr = stderr |
