Merge lp:~barry/launchpad/435604-mailman into lp:launchpad

Proposed by Barry Warsaw
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~barry/launchpad/435604-mailman
Merge into: lp:launchpad
Diff against target: 824 lines
17 files modified
lib/canonical/launchpad/mailman/monkeypatches/__init__.py (+35/-80)
lib/canonical/launchpad/mailman/monkeypatches/mm_cfg.py.in (+65/-0)
lib/lp/services/mailman/doc/basic-integration.txt (+89/-0)
lib/lp/services/mailman/doc/bounces.txt (+1/-0)
lib/lp/services/mailman/doc/contact-address.txt (+2/-2)
lib/lp/services/mailman/doc/create-lists.txt (+2/-2)
lib/lp/services/mailman/doc/decorations.txt (+1/-2)
lib/lp/services/mailman/doc/logging.txt (+27/-24)
lib/lp/services/mailman/doc/messages.txt (+3/-1)
lib/lp/services/mailman/doc/modify-lists.txt (+5/-3)
lib/lp/services/mailman/doc/postings.txt (+17/-16)
lib/lp/services/mailman/doc/reactivate-lists.txt (+4/-2)
lib/lp/services/mailman/doc/recovery.txt (+9/-5)
lib/lp/services/mailman/doc/staging.txt (+2/-3)
lib/lp/services/mailman/doc/subscriptions.txt (+11/-7)
lib/lp/services/mailman/testing/helpers.py (+1/-1)
lib/lp/services/mailman/testing/logwatcher.py (+2/-2)
To merge this branch: bzr merge lp:~barry/launchpad/435604-mailman
Reviewer Review Type Date Requested Status
Brad Crittenden (community) release-critical Approve
Paul Hummer (community) Approve
Review via email: mp+12380@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :
Download full text (6.5 KiB)

 reviewer rockstar

= Summary =

Bug 435604 describes a critical problem we discovered while rolling out LP 3.0
on forster, the Mailman (and other) server. The problem is that with the move
to buildout for a lot of our packages, the sys.path that the Mailman binary
uses was not correct. The binary uses the system Python, not bin/py so none
of the buildout eggs were visible to it.

== Proposed fix ==

Extend the path hacking done in Mailman's mm_cfg.py to pick up the necessary
buildout eggs. Do this by importing _pythonpath.

== Pre-implementation notes ==

Discussion during cowboy rollout with gary, spm, and stub.

== Implementation details ==

Along the way, I updated all the MailmanLayer tests. These should all pass
now, with the usual caveat that these tests are flaky. That's why we don't
run them as part of our normal test suite.

== Tests ==

To test the specific critical fix in this branch, run:

% bin/test -vv --layer=MailmanLayer -t basic-integration

To see that all the tests have been updated for LP 3.0, run:

% bin/test -vv --layer=MailmanLayer

You may get spurious failures from the above. Your choices are to run each
test in lib/lp/services/mailman/doc separately, or/and do 'make clean && make'
between each run.

== Demo and Q/A ==

No demo or Q/A. Once we cherry pick this onto forster, if the mailing lists
work, we're gold.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/services/mailman/doc/logging.txt
  lib/lp/services/mailman/doc/subscriptions.txt
  lib/canonical/launchpad/mailman/monkeypatches/__init__.py
  lib/lp/services/mailman/testing/logwatcher.py
  lib/lp/services/mailman/doc/postings.txt
  lib/lp/services/mailman/doc/bounces.txt
  lib/lp/services/mailman/doc/basic-integration.txt
  lib/lp/services/mailman/doc/decorations.txt
  lib/lp/services/mailman/doc/messages.txt
  lib/lp/services/mailman/doc/recovery.txt
  lib/lp/services/mailman/doc/reactivate-lists.txt
  lib/lp/services/mailman/doc/create-lists.txt
  lib/canonical/launchpad/mailman/monkeypatches/mm_cfg.py.in
  lib/lp/services/mailman/doc/contact-address.txt
  lib/lp/services/mailman/doc/staging.txt
  lib/lp/services/mailman/testing/helpers.py
  lib/lp/services/mailman/doc/modify-lists.txt

== Pyflakes Doctest notices ==

lib/lp/services/mailman/doc/logging.txt
    70: undefined name 'error_watcher'
    71: undefined name 'error_watcher'
    74: undefined name 'error_watcher'
    75: undefined name 'error_watcher'

lib/lp/services/mailman/doc/postings.txt
    14: undefined name 'smtpd'
    35: undefined name 'vette_watcher'
    36: undefined name 'smtpd'
    73: undefined name 'vette_watcher'
    74: undefined name 'smtpd'
    88: undefined name 'smtpd'
    89: undefined name 'smtpd'
    99: undefined name 'vette_watcher'
    103: undefined name 'smtpd_watcher'
    104: undefined name 'smtpd'
    156: undefined name 'Browser'
    168: undefined name 'smtpd_watcher'
    169: undefined name 'smtpd'
    212: undefined name 'vette_watcher'
    213: undefined name 'smtpd'
    244: undefined name 'smtpd'
    259: undefine...

Read more...

Revision history for this message
Paul Hummer (rockstar) :
review: Approve
Revision history for this message
Brad Crittenden (bac) wrote :

Hi Barry,

Thanks for the fix. Not to beat a dead horse, but in the future try to keep RC candidate branches as tiny as possible.

Thanks also for hanging around last night as the release happened to ensure everything worked.

review: Approve (release-critical)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/mailman/monkeypatches/__init__.py'
--- lib/canonical/launchpad/mailman/monkeypatches/__init__.py 2009-07-17 00:26:05 +0000
+++ lib/canonical/launchpad/mailman/monkeypatches/__init__.py 2009-09-24 20:25:22 +0000
@@ -38,7 +38,8 @@
38 # will get appended to Mailman's sys.path.38 # will get appended to Mailman's sys.path.
39 import canonical39 import canonical
40 from canonical.launchpad.mailman.config import configure_siteowner40 from canonical.launchpad.mailman.config import configure_siteowner
41 launchpad_top = os.path.dirname(os.path.dirname(canonical.__file__))41 launchpad_top = os.path.dirname(
42 os.path.dirname(os.path.dirname(canonical.__file__)))
42 # Read the email footer template for all Launchpad messages.43 # Read the email footer template for all Launchpad messages.
43 from canonical.launchpad.helpers import get_email_template44 from canonical.launchpad.helpers import get_email_template
44 footer = get_email_template('mailinglist-footer.txt')45 footer = get_email_template('mailinglist-footer.txt')
@@ -46,85 +47,39 @@
46 host, port = as_host_port(config.mailman.smtp)47 host, port = as_host_port(config.mailman.smtp)
47 owner_address, owner_password = configure_siteowner(48 owner_address, owner_password = configure_siteowner(
48 config.mailman.build_site_list_owner)49 config.mailman.build_site_list_owner)
49 config_path = os.path.join(mailman_path, 'Mailman', 'mm_cfg.py')50 config_path_in = os.path.join(os.path.dirname(__file__), 'mm_cfg.py.in')
50 config_file = open(config_path, 'w')51 config_file_in = open(config_path_in)
51 try:52 try:
52 print >> config_file, """\53 config_template = config_file_in.read()
53# Automatically generated by runlaunchpad.py54 finally:
5455 config_file_in.close()
55# Set up Mailman's sys.path to pick up the top of Launchpad's tree56 config_path_out = os.path.join(mailman_path, 'Mailman', 'mm_cfg.py')
56import sys57 config_file_out = open(config_path_out, 'w')
57sys.path.insert(0, '%(launchpad_top)s')58 try:
5859 print >> config_file_out, config_template % dict(
59# Pick up Launchpad static overrides. This will also pick up the standard60 launchpad_top=launchpad_top,
60# Mailman.Defaults.* variables.61 smtp_host=host,
61from canonical.launchpad.mailman.monkeypatches.defaults import *62 smtp_port=port,
6263 xmlrpc_url=config.mailman.xmlrpc_url,
63# Our dynamic overrides of all the static defaults.64 xmlrpc_sleeptime=config.mailman.xmlrpc_runner_sleep,
64SMTPHOST = '%(smtp_host)s'65 xmlrpc_subscription_batch_size
65SMTPPORT = %(smtp_port)d66 =config.mailman.subscription_batch_size,
6667 site_list_owner=owner_address,
67# Configuration options for the XMLRPCRunner.68 list_help_header=config.mailman.list_help_header,
68XMLRPC_URL = '%(xmlrpc_url)s'69 list_subscription_headers
69XMLRPC_SLEEPTIME = %(xmlrpc_sleeptime)s70 =config.mailman.list_subscription_headers,
70XMLRPC_SUBSCRIPTION_BATCH_SIZE = %(xmlrpc_subscription_batch_size)s71 archive_url_template=config.mailman.archive_url_template,
71LAUNCHPAD_SHARED_SECRET = '%(shared_secret)s'72 list_owner_header_template
7273 =config.mailman.list_owner_header_template,
73# RFC 2369 header information.74 footer=footer,
74LIST_HELP_HEADER = '%(list_help_header)s'75 var_dir=config.mailman.build_var_dir,
75LIST_SUBSCRIPTION_HEADERS = '%(list_subscription_headers)s'76 shared_secret=config.mailman.shared_secret,
76LIST_ARCHIVE_HEADER_TEMPLATE = '%(archive_url_template)s'77 soft_max_size=config.mailman.soft_max_size,
77LIST_OWNER_HEADER_TEMPLATE = '%(list_owner_header_template)s'78 hard_max_size=config.mailman.hard_max_size,
7879 register_bounces_every=config.mailman.register_bounces_every,
79# Soft and hard maximum message size limits. Anything below the soft limit is80 )
80# allowed directly through. Between the soft and hard limits, the message is81 finally:
81# held for approval. Above the hard limit, the message is logged and82 config_file_out.close()
82# discarded. Note that the normal Mailman size thresholds are ignored.
83LAUNCHPAD_SOFT_MAX_SIZE = %(soft_max_size)d
84LAUNCHPAD_HARD_MAX_SIZE = %(hard_max_size)d
85
86SITE_LIST_OWNER = '%(site_list_owner)s'
87
88DEFAULT_MSG_FOOTER = '''_______________________________________________
89%(footer)s'''
90
91# Set up MHonArc archiving.
92PUBLIC_EXTERNAL_ARCHIVER = '/usr/bin/mhonarc \
93-add \
94-dbfile %(var_dir)s/archives/private/%%(listname)s.mbox/mhonarc.db \
95-outdir %(var_dir)s/mhonarc/%%(listname)s \
96-definevar ML-NAME=%%(listname)s \
97-rcfile %(var_dir)s/data/lp-mhonarc-common.mrc \
98-stderr %(var_dir)s/logs/mhonarc \
99-stdout %(var_dir)s/logs/mhonarc \
100-spammode \
101-umask 022'
102PRIVATE_EXTERNAL_ARCHIVER = PUBLIC_EXTERNAL_ARCHIVER
103
104# How often do we run the bounce processor? For production, the default 15
105# minutes is fine, for testing we want to run it more often.
106REGISTER_BOUNCES_EVERY = %(register_bounces_every)d
107""" % dict(
108 launchpad_top=launchpad_top,
109 smtp_host=host,
110 smtp_port=port,
111 xmlrpc_url=config.mailman.xmlrpc_url,
112 xmlrpc_sleeptime=config.mailman.xmlrpc_runner_sleep,
113 xmlrpc_subscription_batch_size=config.mailman.subscription_batch_size,
114 site_list_owner=owner_address,
115 list_help_header=config.mailman.list_help_header,
116 list_subscription_headers=config.mailman.list_subscription_headers,
117 archive_url_template=config.mailman.archive_url_template,
118 list_owner_header_template=config.mailman.list_owner_header_template,
119 footer=footer,
120 var_dir=config.mailman.build_var_dir,
121 shared_secret=config.mailman.shared_secret,
122 soft_max_size=config.mailman.soft_max_size,
123 hard_max_size=config.mailman.hard_max_size,
124 register_bounces_every=config.mailman.register_bounces_every,
125 )
126 finally:
127 config_file.close()
128 # Mailman's qrunner system requires runner modules to live in the83 # Mailman's qrunner system requires runner modules to live in the
129 # Mailman.Queue package. Set things up so that there's a hook module in84 # Mailman.Queue package. Set things up so that there's a hook module in
130 # there for the XMLRPCRunner.85 # there for the XMLRPCRunner.
13186
=== added file 'lib/canonical/launchpad/mailman/monkeypatches/mm_cfg.py.in'
--- lib/canonical/launchpad/mailman/monkeypatches/mm_cfg.py.in 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/mailman/monkeypatches/mm_cfg.py.in 2009-09-24 20:25:22 +0000
@@ -0,0 +1,65 @@
1# Automatically generated by runlaunchpad.py
2
3# Initialize sys.path so that the Mailman processes, which use the standard
4# system Python instead of buildout's bin/py, can find all the necessary
5# packages and modules. Some of these are in sourcecode and some are in
6# buildout eggs.
7#
8# This is a two-step process. First, we hack sys.path in order to find a
9# directory containing _pythonpath. Then the _pythonpath module does all the
10# subsequent sys.path hacking necessary.
11
12# Set up Mailman's sys.path to pick up the top of Launchpad's tree. This only
13# gets us a sys.path
14import sys
15sys.path.insert(0, '%(launchpad_top)s')
16import _pythonpath
17
18# Pick up Launchpad static overrides. This will also pick up the standard
19# Mailman.Defaults.* variables.
20from canonical.launchpad.mailman.monkeypatches.defaults import *
21
22# Our dynamic overrides of all the static defaults.
23SMTPHOST = '%(smtp_host)s'
24SMTPPORT = %(smtp_port)d
25
26# Configuration options for the XMLRPCRunner.
27XMLRPC_URL = '%(xmlrpc_url)s'
28XMLRPC_SLEEPTIME = %(xmlrpc_sleeptime)s
29XMLRPC_SUBSCRIPTION_BATCH_SIZE = %(xmlrpc_subscription_batch_size)s
30LAUNCHPAD_SHARED_SECRET = '%(shared_secret)s'
31
32# RFC 2369 header information.
33LIST_HELP_HEADER = '%(list_help_header)s'
34LIST_SUBSCRIPTION_HEADERS = '%(list_subscription_headers)s'
35LIST_ARCHIVE_HEADER_TEMPLATE = '%(archive_url_template)s'
36LIST_OWNER_HEADER_TEMPLATE = '%(list_owner_header_template)s'
37
38# Soft and hard maximum message size limits. Anything below the soft limit is
39# allowed directly through. Between the soft and hard limits, the message is
40# held for approval. Above the hard limit, the message is logged and
41# discarded. Note that the normal Mailman size thresholds are ignored.
42LAUNCHPAD_SOFT_MAX_SIZE = %(soft_max_size)d
43LAUNCHPAD_HARD_MAX_SIZE = %(hard_max_size)d
44
45SITE_LIST_OWNER = '%(site_list_owner)s'
46
47DEFAULT_MSG_FOOTER = '''_______________________________________________
48%(footer)s'''
49
50# Set up MHonArc archiving.
51PUBLIC_EXTERNAL_ARCHIVER = '/usr/bin/mhonarc \
52-add \
53-dbfile %(var_dir)s/archives/private/%%(listname)s.mbox/mhonarc.db \
54-outdir %(var_dir)s/mhonarc/%%(listname)s \
55-definevar ML-NAME=%%(listname)s \
56-rcfile %(var_dir)s/data/lp-mhonarc-common.mrc \
57-stderr %(var_dir)s/logs/mhonarc \
58-stdout %(var_dir)s/logs/mhonarc \
59-spammode \
60-umask 022'
61PRIVATE_EXTERNAL_ARCHIVER = PUBLIC_EXTERNAL_ARCHIVER
62
63# How often do we run the bounce processor? For production, the default 15
64# minutes is fine, for testing we want to run it more often.
65REGISTER_BOUNCES_EVERY = %(register_bounces_every)d
066
=== modified file 'lib/lp/services/mailman/doc/basic-integration.txt'
--- lib/lp/services/mailman/doc/basic-integration.txt 2009-06-23 17:51:24 +0000
+++ lib/lp/services/mailman/doc/basic-integration.txt 2009-09-24 20:25:22 +0000
@@ -29,3 +29,92 @@
2929
30 >>> withlist('withlist_2.can_import', '-q')30 >>> withlist('withlist_2.can_import', '-q')
31 9931 99
32
33
34Binaries
35========
36
37Mailman contains a number of binary wrappers for integration between the MTA
38and Mailman. Mailman's 'post' command accepts standard input from the MTA and
39drops the resulting message in Mailman's incoming queue.
40
41 # See `subscriptions.txt`_ for more details.
42 >>> from lp.services.mailman.testing import helpers
43 >>> alpha = helpers.create_list('alpha')
44 >>> helpers.subscribe('Anne', 'alpha')
45
46 # Ignore the list creation notification message.
47 >>> smtpd.reset()
48
49 >>> sample_message = """\
50 ... From: anne.person@example.com
51 ... To: alpha@lists.launchpad.dev
52 ... Subject: A test message
53 ... Message-ID: <aardvark>
54 ...
55 ... This is a test message.
56 ... """
57
58 # The path to the binary we're testing is relative to the top of the
59 # Launchpad source tree.
60 >>> import canonical
61 >>> launchpad_top = os.path.dirname(
62 ... os.path.dirname(os.path.dirname(canonical.__file__)))
63 >>> binary = os.path.join(launchpad_top,
64 ... 'lib/mailman/mail/mailman')
65
66 >>> mailman = subprocess.Popen(
67 ... (binary, 'post', 'alpha'),
68 ... stdin=subprocess.PIPE,
69 ... stdout=subprocess.PIPE,
70 ... stderr=subprocess.PIPE)
71
72 >>> stdout, stderr = mailman.communicate(sample_message)
73 >>> mailman.returncode
74 0
75 >>> print stdout
76 <BLANKLINE>
77 >>> print stderr
78 <BLANKLINE>
79
80The message was delivered to Anne and the archiver.
81
82 >>> smtpd_watcher.wait_for_mbox_delivery('aardvark')
83 >>> messages = list(smtpd)
84 >>> len(messages)
85 2
86
87 >>> for message in messages:
88 ... if message['x-rcptto'] == 'anne.person@example.com':
89 ... break
90
91 >>> print message.as_string()
92 From: anne.person@example.com
93 To: alpha@lists.launchpad.dev
94 Message-ID: <aardvark>
95 Subject: [Alpha] A test message
96 X-BeenThere: alpha@lists.launchpad.dev
97 X-Mailman-Version: ...
98 Precedence: list
99 List-Id: <alpha.lists.launchpad.dev>
100 List-Help: <http://help.launchpad.dev/ListHelp>
101 List-Subscribe: <http://launchpad.dev/~alpha>
102 List-Unsubscribe: <http://launchpad.dev/~alpha>
103 List-Post: <mailto:alpha@lists.launchpad.dev>
104 List-Archive: <http://lists.launchpad.dev/alpha>
105 List-Owner: <http://launchpad.dev/~alpha>
106 MIME-Version: 1.0
107 Content-Type: text/plain; charset="us-ascii"
108 Content-Transfer-Encoding: 7bit
109 Sender: alpha-bounces+anne.person=example.com@lists.launchpad.dev
110 Errors-To: alpha-bounces+anne.person=example.com@lists.launchpad.dev
111 X-Peer: ...
112 X-MailFrom: alpha-bounces+anne.person=example.com@lists.launchpad.dev
113 X-RcptTo: anne.person@example.com
114 <BLANKLINE>
115 This is a test message.
116 _______________________________________________
117 Mailing list: http://launchpad.dev/~alpha
118 Post to : alpha@lists.launchpad.dev
119 Unsubscribe : http://launchpad.dev/~alpha
120 More help : http://help.launchpad.dev/ListHelp
32121
=== modified file 'lib/lp/services/mailman/doc/bounces.txt'
--- lib/lp/services/mailman/doc/bounces.txt 2009-06-03 16:03:04 +0000
+++ lib/lp/services/mailman/doc/bounces.txt 2009-09-24 20:25:22 +0000
@@ -1,3 +1,4 @@
1=================
1Bounce processing2Bounce processing
2=================3=================
34
45
=== modified file 'lib/lp/services/mailman/doc/contact-address.txt'
--- lib/lp/services/mailman/doc/contact-address.txt 2009-06-26 21:58:35 +0000
+++ lib/lp/services/mailman/doc/contact-address.txt 2009-09-24 20:25:22 +0000
@@ -28,7 +28,7 @@
28 >>> browser.getControl('Change').click()28 >>> browser.getControl('Change').click()
2929
30 >>> browser.getLink('Change details').click()30 >>> browser.getLink('Change details').click()
31 >>> browser.getLink('Change contact address').click()31 >>> browser.getLink('Set contact address').click()
32 >>> control = browser.getControl(name='field.contact_method')32 >>> control = browser.getControl(name='field.contact_method')
33 >>> [strip_label(label) for label in control.displayValue]33 >>> [strip_label(label) for label in control.displayValue]
34 ['The Launchpad mailing list for this team...]34 ['The Launchpad mailing list for this team...]
@@ -237,7 +237,7 @@
237 >>> browser.getControl('Change').click()237 >>> browser.getControl('Change').click()
238238
239 >>> browser.getLink('Change details').click()239 >>> browser.getLink('Change details').click()
240 >>> browser.getLink('Change contact address').click()240 >>> browser.getLink('Set contact address').click()
241 >>> control = browser.getControl(name='field.contact_method')241 >>> control = browser.getControl(name='field.contact_method')
242 >>> [strip_label(label) for label in control.displayValue]242 >>> [strip_label(label) for label in control.displayValue]
243 ['Each member individually']243 ['Each member individually']
244244
=== modified file 'lib/lp/services/mailman/doc/create-lists.txt'
--- lib/lp/services/mailman/doc/create-lists.txt 2009-06-23 17:51:24 +0000
+++ lib/lp/services/mailman/doc/create-lists.txt 2009-09-24 20:25:22 +0000
@@ -21,7 +21,7 @@
21To request a mailing list, No Privileges Person navigates to the 'Configure21To request a mailing list, No Privileges Person navigates to the 'Configure
22mailing list' page and registers their application for a mailing list.22mailing list' page and registers their application for a mailing list.
2323
24 >>> browser.getLink('Configure mailing list').click()24 >>> browser.getLink('Create a mailing list').click()
25 >>> browser.getControl('Apply for Mailing List').click()25 >>> browser.getControl('Apply for Mailing List').click()
2626
27At this point, Mailman has still not created the mailing list.27At this point, Mailman has still not created the mailing list.
@@ -56,7 +56,7 @@
56 >>> browser.getControl(name='field.subscriptionpolicy').displayValue = [56 >>> browser.getControl(name='field.subscriptionpolicy').displayValue = [
57 ... 'Open Team']57 ... 'Open Team']
58 >>> browser.getControl('Create').click()58 >>> browser.getControl('Create').click()
59 >>> browser.getLink('Configure mailing list').click()59 >>> browser.getLink('Create a mailing list').click()
60 >>> browser.getControl('Apply for Mailing List').click()60 >>> browser.getControl('Apply for Mailing List').click()
6161
62This time, however, the mailing list expert declines the application for a new62This time, however, the mailing list expert declines the application for a new
6363
=== modified file 'lib/lp/services/mailman/doc/decorations.txt'
--- lib/lp/services/mailman/doc/decorations.txt 2009-06-23 19:33:57 +0000
+++ lib/lp/services/mailman/doc/decorations.txt 2009-09-24 20:25:22 +0000
@@ -117,8 +117,7 @@
117 >>> browser = Browser('no-priv@canonical.com:test')117 >>> browser = Browser('no-priv@canonical.com:test')
118 >>> browser.open('http://launchpad.dev:8085/~itest-one')118 >>> browser.open('http://launchpad.dev:8085/~itest-one')
119 >>> browser.getLink(id='mailing-list-archive')119 >>> browser.getLink(id='mailing-list-archive')
120 <Link text='Mailing list archive'120 <Link text='View archive' url='http://lists.launchpad.dev/itest-one'>
121 url='http://lists.launchpad.dev/itest-one'>
122 >>> print message['list-archive']121 >>> print message['list-archive']
123 <http://lists.launchpad.dev/itest-one>122 <http://lists.launchpad.dev/itest-one>
124123
125124
=== modified file 'lib/lp/services/mailman/doc/logging.txt'
--- lib/lp/services/mailman/doc/logging.txt 2009-04-17 10:32:16 +0000
+++ lib/lp/services/mailman/doc/logging.txt 2009-09-24 20:25:22 +0000
@@ -1,9 +1,12 @@
1= Logging and OOPS reporting =1==========================
2Logging and OOPS reporting
3==========================
24
3The Mailman XMLRPCRunner logs errors and reports OOPSes.5The Mailman XMLRPCRunner logs errors and reports OOPSes.
46
57
6= MailmanErrorUtility =8MailmanErrorUtility
9===================
710
8The MailmanErrorUtility is a ErrorReportingUtility that is configured11The MailmanErrorUtility is a ErrorReportingUtility that is configured
9in the [mailman] section of config.12in the [mailman] section of config.
@@ -13,21 +16,22 @@
13 ... MailmanErrorUtility)16 ... MailmanErrorUtility)
1417
15 >>> error_utility = MailmanErrorUtility()18 >>> error_utility = MailmanErrorUtility()
16 >>> error_utility._default_config_section19 >>> print error_utility._default_config_section
17 'mailman'20 mailman
1821
19 >>> error_utility.prefix22 >>> print error_utility.prefix
20 'TMMX'23 TMMX
21 >>> config.mailman.oops_prefix24 >>> print config.mailman.oops_prefix
22 'TMMX'25 TMMX
2326
24 >>> error_utility.error_dir27 >>> print error_utility.error_dir
25 '/var/tmp/mailman-xmlrpc.test'28 /var/tmp/mailman-xmlrpc.test
26 >>> config.mailman.error_dir29 >>> print config.mailman.error_dir
27 '/var/tmp/mailman-xmlrpc.test'30 /var/tmp/mailman-xmlrpc.test
2831
2932
30= Error logging =33Error logging
34=============
3135
32The log_exception() function is used by XMLRPCRunner to report known36The log_exception() function is used by XMLRPCRunner to report known
33exceptions. It reports the OOPS.37exceptions. It reports the OOPS.
@@ -44,12 +48,12 @@
4448
45 >>> test_log_exception()49 >>> test_log_exception()
46 >>> oops = error_utility.getLastOopsReport()50 >>> oops = error_utility.getLastOopsReport()
47 >>> oops.id51 >>> print oops.id
48 'OOPS-...TMMX...'52 OOPS-...TMMX...
49 >>> oops.type53 >>> print oops.type
50 'AssertionError'54 AssertionError
51 >>> oops.value55 >>> print oops.value
52 'There is an OOPS in progress.'56 There is an OOPS in progress.
53 >>> print oops.tb_text57 >>> print oops.tb_text
54 Traceback (most recent call last):58 Traceback (most recent call last):
55 ...59 ...
@@ -77,4 +81,3 @@
77the _create() method. The create-lists-txt test was run to generate a81the _create() method. The create-lists-txt test was run to generate a
78"NameError: global name 'defect' is not defined". The error was written82"NameError: global name 'defect' is not defined". The error was written
79to the xmlrpc log, and an OOPS was reported.83to the xmlrpc log, and an OOPS was reported.
80
8184
=== modified file 'lib/lp/services/mailman/doc/messages.txt'
--- lib/lp/services/mailman/doc/messages.txt 2009-04-17 10:32:16 +0000
+++ lib/lp/services/mailman/doc/messages.txt 2009-09-24 20:25:22 +0000
@@ -1,4 +1,6 @@
1= Messages =1========
2Messages
3========
24
3Launchpad uses a custom set of message templates that it installs5Launchpad uses a custom set of message templates that it installs
4in mailman/templates/site. The installation conforms to the standard6in mailman/templates/site. The installation conforms to the standard
57
=== modified file 'lib/lp/services/mailman/doc/modify-lists.txt'
--- lib/lp/services/mailman/doc/modify-lists.txt 2009-06-03 16:03:04 +0000
+++ lib/lp/services/mailman/doc/modify-lists.txt 2009-09-24 20:25:22 +0000
@@ -1,4 +1,6 @@
1= Modifying lists =1===============
2Modifying lists
3===============
24
3A team administrator can make changes to the mailing list, although currently5A team administrator can make changes to the mailing list, although currently
4the only thing they can change is the list's welcome message.6the only thing they can change is the list's welcome message.
@@ -13,7 +15,7 @@
13 >>> browser.getControl(name='field.subscriptionpolicy').displayValue = [15 >>> browser.getControl(name='field.subscriptionpolicy').displayValue = [
14 ... 'Open Team']16 ... 'Open Team']
15 >>> browser.getControl('Create').click()17 >>> browser.getControl('Create').click()
16 >>> browser.getLink('Configure mailing list').click()18 >>> browser.getLink('Create a mailing list').click()
17 >>> browser.getControl('Apply for Mailing List').click()19 >>> browser.getControl('Apply for Mailing List').click()
1820
19Before approving the creation of the mailing list, let's set the welcome21Before approving the creation of the mailing list, let's set the welcome
@@ -26,7 +28,7 @@
26 >>> from canonical.launchpad.interfaces import (28 >>> from canonical.launchpad.interfaces import (
27 ... ILaunchpadCelebrities, IMailingListSet, MailingListStatus)29 ... ILaunchpadCelebrities, IMailingListSet, MailingListStatus)
2830
29 >>> login('foo.bar@canonical.com')31 >>> login('admin@canonical.com')
30 >>> list_set = getUtility(IMailingListSet)32 >>> list_set = getUtility(IMailingListSet)
31 >>> list_one = list_set.get('itest-one')33 >>> list_one = list_set.get('itest-one')
32 >>> list_one.welcome_message = 'Greetings team members!'34 >>> list_one.welcome_message = 'Greetings team members!'
3335
=== modified file 'lib/lp/services/mailman/doc/postings.txt'
--- lib/lp/services/mailman/doc/postings.txt 2009-06-26 21:42:20 +0000
+++ lib/lp/services/mailman/doc/postings.txt 2009-09-24 20:25:22 +0000
@@ -1,3 +1,4 @@
1=======
1Posting2Posting
2=======3=======
34
@@ -14,7 +15,7 @@
1415
1516
16Non-member postings17Non-member postings
17-------------------18===================
1819
19Postings from addresses that are not registered and verified with Launchpad20Postings from addresses that are not registered and verified with Launchpad
20are summarily discarded.21are summarily discarded.
@@ -37,7 +38,7 @@
3738
3839
39Non-validated member postings40Non-validated member postings
40-----------------------------41=============================
4142
42Similarly, an address that has been registered with Launchpad but not verified43Similarly, an address that has been registered with Launchpad but not verified
43will also be summarily discarded. Anne registers with Launchpad but does not44will also be summarily discarded. Anne registers with Launchpad but does not
@@ -48,7 +49,7 @@
48 >>> from canonical.launchpad.ftests import login, logout49 >>> from canonical.launchpad.ftests import login, logout
49 >>> from canonical.launchpad.interfaces import EmailAddressStatus50 >>> from canonical.launchpad.interfaces import EmailAddressStatus
5051
51 >>> login('foo.bar@canonical.com')52 >>> login('admin@canonical.com')
52 >>> anne = factory.makePersonByName('Anne')53 >>> anne = factory.makePersonByName('Anne')
53 >>> alternative_email = get_alternative_email(anne)54 >>> alternative_email = get_alternative_email(anne)
5455
@@ -75,7 +76,7 @@
7576
7677
77Unsubscribed Launchpad members78Unsubscribed Launchpad members
78------------------------------79==============================
7980
80Anne's preferred email address is still validated, and she posts a message81Anne's preferred email address is still validated, and she posts a message
81from that address. However, because she has not yet joined the team or82from that address. However, because she has not yet joined the team or
@@ -336,7 +337,7 @@
336337
337338
338Unsubscribed team member339Unsubscribed team member
339------------------------340========================
340341
341Once Anne joins the team, she may post to it, though until she subscribes to342Once Anne joins the team, she may post to it, though until she subscribes to
342the mailing list, she will not get a copy of the message.343the mailing list, she will not get a copy of the message.
@@ -356,7 +357,7 @@
356357
357 >>> from zope.component import getUtility358 >>> from zope.component import getUtility
358 >>> from canonical.launchpad.interfaces import IPersonSet359 >>> from canonical.launchpad.interfaces import IPersonSet
359 >>> login('foo.bar@canonical.com')360 >>> login('admin@canonical.com')
360 >>> team_one = getUtility(IPersonSet).getByName('itest-one')361 >>> team_one = getUtility(IPersonSet).getByName('itest-one')
361 >>> anne.join(team_one)362 >>> anne.join(team_one)
362 >>> transaction.commit()363 >>> transaction.commit()
@@ -407,7 +408,7 @@
407408
408409
409Verified and registered member postings410Verified and registered member postings
410---------------------------------------411=======================================
411412
412Anne now subscribes to the team's mailing list. She gets a copy of each413Anne now subscribes to the team's mailing list. She gets a copy of each
413message posted to the mailing list.414message posted to the mailing list.
@@ -503,7 +504,7 @@
503504
504However, once she validates this address, she can post from it.505However, once she validates this address, she can post from it.
505506
506 >>> login('foo.bar@canonical.com')507 >>> login('admin@canonical.com')
507 >>> alternative_email.status = EmailAddressStatus.VALIDATED508 >>> alternative_email.status = EmailAddressStatus.VALIDATED
508 >>> logout()509 >>> logout()
509 >>> transaction.commit()510 >>> transaction.commit()
@@ -570,11 +571,11 @@
570571
571572
572Leaving and re-joining the team573Leaving and re-joining the team
573-------------------------------574===============================
574575
575Anne now leaves the team, but she does not unsubscribe from the mailing list.576Anne now leaves the team, but she does not unsubscribe from the mailing list.
576577
577 >>> login('foo.bar@canonical.com')578 >>> login('admin@canonical.com')
578 >>> team_one = getUtility(IPersonSet).getByName('itest-one')579 >>> team_one = getUtility(IPersonSet).getByName('itest-one')
579 >>> anne.leave(team_one)580 >>> anne.leave(team_one)
580 >>> logout()581 >>> logout()
@@ -619,7 +620,7 @@
619Anne re-joins the team and once again can post to the mailing list. She does620Anne re-joins the team and once again can post to the mailing list. She does
620not need to re-subscribe to the mailing list.621not need to re-subscribe to the mailing list.
621622
622 >>> login('foo.bar@canonical.com')623 >>> login('admin@canonical.com')
623 >>> anne.join(team_one)624 >>> anne.join(team_one)
624 >>> logout()625 >>> logout()
625 >>> transaction.commit()626 >>> transaction.commit()
@@ -654,12 +655,12 @@
654655
655656
656First post moderation657First post moderation
657---------------------658=====================
658659
659Normally, Launchpad members who are not subscribed to the mailing list will660Normally, Launchpad members who are not subscribed to the mailing list will
660have their posts held for moderation.661have their posts held for moderation.
661662
662 >>> login('foo.bar@canonical.com')663 >>> login('admin@canonical.com')
663 >>> bart = factory.makePersonByName('Bart')664 >>> bart = factory.makePersonByName('Bart')
664 >>> transaction.commit()665 >>> transaction.commit()
665 >>> bart.personal_standing666 >>> bart.personal_standing
@@ -694,7 +695,7 @@
694list.695list.
695696
696 >>> from canonical.launchpad.interfaces import PersonalStanding697 >>> from canonical.launchpad.interfaces import PersonalStanding
697 >>> login('foo.bar@canonical.com')698 >>> login('admin@canonical.com')
698 >>> bart.personal_standing = PersonalStanding.GOOD699 >>> bart.personal_standing = PersonalStanding.GOOD
699 >>> transaction.commit()700 >>> transaction.commit()
700701
@@ -809,7 +810,7 @@
809810
810811
811Preventing archiver forgeries812Preventing archiver forgeries
812-----------------------------813=============================
813814
814We archive messages by sending them to a special address owned by the Mail815We archive messages by sending them to a special address owned by the Mail
815Archive <http://www.mail-archive.com>. This address becomes a recipient of816Archive <http://www.mail-archive.com>. This address becomes a recipient of
@@ -834,7 +835,7 @@
834835
835836
836Large messages837Large messages
837--------------838==============
838839
839Only messages which are less than about 40k in size are allowed straight840Only messages which are less than about 40k in size are allowed straight
840through on the mailing list. A message bigger than that will be held for841through on the mailing list. A message bigger than that will be held for
841842
=== modified file 'lib/lp/services/mailman/doc/reactivate-lists.txt'
--- lib/lp/services/mailman/doc/reactivate-lists.txt 2009-05-06 15:13:39 +0000
+++ lib/lp/services/mailman/doc/reactivate-lists.txt 2009-09-24 20:25:22 +0000
@@ -1,4 +1,6 @@
1= Re-activating mailing lists =1===========================
2Re-activating mailing lists
3===========================
24
3A team administrator may re-activate a previously deactivated mailing list5A team administrator may re-activate a previously deactivated mailing list
4without the need for approval from a Launchpad administrator. When the6without the need for approval from a Launchpad administrator. When the
@@ -48,7 +50,7 @@
4850
49Now the team owner reactivates the list.51Now the team owner reactivates the list.
5052
51 >>> browser.getLink('Configure mailing list').click()53 >>> browser.getLink('Create a mailing list').click()
52 >>> browser.getControl('Reactivate this Mailing List').click()54 >>> browser.getControl('Reactivate this Mailing List').click()
53 >>> xmlrpc_watcher.wait_for_reactivation('itest-one')55 >>> xmlrpc_watcher.wait_for_reactivation('itest-one')
5456
5557
=== modified file 'lib/lp/services/mailman/doc/recovery.txt'
--- lib/lp/services/mailman/doc/recovery.txt 2009-06-03 16:03:04 +0000
+++ lib/lp/services/mailman/doc/recovery.txt 2009-09-24 20:25:22 +0000
@@ -1,4 +1,6 @@
1= Error recovery =1==============
2Error recovery
3==============
24
3Under various conditions, reporting status from Mailman to Launchpad can5Under various conditions, reporting status from Mailman to Launchpad can
4fail. For example, if during construction, the reportStatus() call has6fail. For example, if during construction, the reportStatus() call has
@@ -18,14 +20,15 @@
18 >>> browser.getControl(name='field.subscriptionpolicy').displayValue = [20 >>> browser.getControl(name='field.subscriptionpolicy').displayValue = [
19 ... 'Open Team']21 ... 'Open Team']
20 >>> browser.getControl('Create').click()22 >>> browser.getControl('Create').click()
21 >>> browser.getLink('Configure mailing list').click()23 >>> browser.getLink('Create a mailing list').click()
22 >>> browser.getControl('Apply for Mailing List').click()24 >>> browser.getControl('Apply for Mailing List').click()
2325
24 >>> from lp.services.mailman.testing import helpers26 >>> from lp.services.mailman.testing import helpers
25 >>> list_one = helpers.review_list('itest-one')27 >>> list_one = helpers.review_list('itest-one')
2628
2729
28== Lost construction ==30Lost construction
31=================
2932
30The mailing list is now active, but let's say that the status reporting33The mailing list is now active, but let's say that the status reporting
31failed. The mailing list would be in the CONSTRUCTING state. We34failed. The mailing list would be in the CONSTRUCTING state. We
@@ -37,7 +40,7 @@
37 >>> from canonical.launchpad.interfaces import (40 >>> from canonical.launchpad.interfaces import (
38 ... IMailingListSet, MailingListStatus)41 ... IMailingListSet, MailingListStatus)
39 >>> from canonical.launchpad.ftests import login, logout42 >>> from canonical.launchpad.ftests import login, logout
40 >>> login('foo.bar@canonical.com')43 >>> login('admin@canonical.com')
41 >>> mailing_list_set = getUtility(IMailingListSet)44 >>> mailing_list_set = getUtility(IMailingListSet)
42 >>> itest_one = removeSecurityProxy(mailing_list_set.get('itest-one'))45 >>> itest_one = removeSecurityProxy(mailing_list_set.get('itest-one'))
43 >>> itest_one.status = MailingListStatus.CONSTRUCTING46 >>> itest_one.status = MailingListStatus.CONSTRUCTING
@@ -84,7 +87,8 @@
84 <DBItem MailingListStatus.ACTIVE, (5) Active>87 <DBItem MailingListStatus.ACTIVE, (5) Active>
8588
8689
87== Lost updates ==90Lost updates
91============
8892
89Another situation can occur if the team owner updates the mailing list,93Another situation can occur if the team owner updates the mailing list,
90say by giving it a welcome message.94say by giving it a welcome message.
9195
=== modified file 'lib/lp/services/mailman/doc/staging.txt'
--- lib/lp/services/mailman/doc/staging.txt 2009-06-30 17:37:35 +0000
+++ lib/lp/services/mailman/doc/staging.txt 2009-09-24 20:25:22 +0000
@@ -20,8 +20,7 @@
2020
21 >>> from canonical.launchpad.ftests import login, logout21 >>> from canonical.launchpad.ftests import login, logout
2222
23 >>> login('foo.bar@canonical.com')23 >>> login('admin@canonical.com')
24 >>> from zope.component import getUtility
2524
26 >>> owner = factory.makePerson()25 >>> owner = factory.makePerson()
27 >>> team_one = factory.makeTeam(owner=owner, name='staging-one')26 >>> team_one = factory.makeTeam(owner=owner, name='staging-one')
@@ -127,7 +126,7 @@
127 >>> from canonical.launchpad.interfaces import (126 >>> from canonical.launchpad.interfaces import (
128 ... EmailAddressStatus, IEmailAddressSet, IPersonSet)127 ... EmailAddressStatus, IEmailAddressSet, IPersonSet)
129 >>> from zope.component import getUtility128 >>> from zope.component import getUtility
130 >>> login('foo.bar@canonical.com')129 >>> login('admin@canonical.com')
131 >>> team = getUtility(IPersonSet).getByName('staging-two')130 >>> team = getUtility(IPersonSet).getByName('staging-two')
132 >>> email = getUtility(IEmailAddressSet).new(131 >>> email = getUtility(IEmailAddressSet).new(
133 ... 'contact@example.com', team, EmailAddressStatus.VALIDATED)132 ... 'contact@example.com', team, EmailAddressStatus.VALIDATED)
134133
=== modified file 'lib/lp/services/mailman/doc/subscriptions.txt'
--- lib/lp/services/mailman/doc/subscriptions.txt 2009-06-03 16:03:04 +0000
+++ lib/lp/services/mailman/doc/subscriptions.txt 2009-09-24 20:25:22 +0000
@@ -1,4 +1,6 @@
1= Mailing list subscriptions =1==========================
2Mailing list subscriptions
3==========================
24
3Both direct and indirect members of a team may subscribe to a team's mailing5Both direct and indirect members of a team may subscribe to a team's mailing
4list. First, create the mailing list, which will send a notification messages6list. First, create the mailing list, which will send a notification messages
@@ -57,7 +59,7 @@
57 >>> from lp.registry.interfaces.person import IPersonSet59 >>> from lp.registry.interfaces.person import IPersonSet
58 >>> from zope.component import getUtility60 >>> from zope.component import getUtility
5961
60 >>> login('foo.bar@canonical.com')62 >>> login('admin@canonical.com')
61 >>> person_set = getUtility(IPersonSet)63 >>> person_set = getUtility(IPersonSet)
6264
63 >>> anne = person_set.getByName('anne')65 >>> anne = person_set.getByName('anne')
@@ -88,7 +90,7 @@
8890
89Bart now leaves the team. His subscription is removed.91Bart now leaves the team. His subscription is removed.
9092
91 >>> login('foo.bar@canonical.com')93 >>> login('admin@canonical.com')
92 >>> bart = person_set.getByName('bart')94 >>> bart = person_set.getByName('bart')
93 >>> team_one = person_set.getByName('itest-one')95 >>> team_one = person_set.getByName('itest-one')
94 >>> bart.leave(team_one)96 >>> bart.leave(team_one)
@@ -97,7 +99,8 @@
97 >>> helpers.ensure_nonmembership('itest-one', bart)99 >>> helpers.ensure_nonmembership('itest-one', bart)
98100
99101
100== Alternative addresses ==102Alternative addresses
103=====================
101104
102Now Anne decides to register another email address with Launchpad, however105Now Anne decides to register another email address with Launchpad, however
103before she can validate this address, subscriptions for the list are106before she can validate this address, subscriptions for the list are
@@ -105,7 +108,7 @@
105the membership list, either as an enabled or disabled address.108the membership list, either as an enabled or disabled address.
106109
107 >>> from canonical.launchpad.interfaces import IEmailAddressSet110 >>> from canonical.launchpad.interfaces import IEmailAddressSet
108 >>> login('foo.bar@canonical.com')111 >>> login('admin@canonical.com')
109 >>> email_set = getUtility(IEmailAddressSet)112 >>> email_set = getUtility(IEmailAddressSet)
110 >>> email = email_set.new(113 >>> email = email_set.new(
111 ... 'anne.x.person@example.net', anne, account=anne.account)114 ... 'anne.x.person@example.net', anne, account=anne.account)
@@ -145,7 +148,8 @@
145 No Privileges Person <no-priv@canonical.com> DISABLED148 No Privileges Person <no-priv@canonical.com> DISABLED
146149
147150
148== Case-preservation ==151Case-preservation
152=================
149153
150Mailing lists preserve the case of the subscribed email address,154Mailing lists preserve the case of the subscribed email address,
151although for subscription purposes, two email addresses that differs155although for subscription purposes, two email addresses that differs
@@ -154,7 +158,7 @@
154Emma joins Launchpad as normal, then subscribes a mixed-case alternative158Emma joins Launchpad as normal, then subscribes a mixed-case alternative
155email address.159email address.
156160
157 >>> login('foo.bar@canonical.com')161 >>> login('admin@canonical.com')
158 >>> emma = factory.makePersonByName('Emma')162 >>> emma = factory.makePersonByName('Emma')
159 >>> email = email_set.new(163 >>> email = email_set.new(
160 ... 'EmmaXPerson@example.org', emma, account=emma.account)164 ... 'EmmaXPerson@example.org', emma, account=emma.account)
161165
=== modified file 'lib/lp/services/mailman/testing/helpers.py'
--- lib/lp/services/mailman/testing/helpers.py 2009-06-25 04:06:00 +0000
+++ lib/lp/services/mailman/testing/helpers.py 2009-09-24 20:25:22 +0000
@@ -103,7 +103,7 @@
103 'Open Team']103 'Open Team']
104 browser.getControl('Create').click()104 browser.getControl('Create').click()
105 # Create the mailing list.105 # Create the mailing list.
106 browser.getLink('Configure mailing list').click()106 browser.getLink('Create a mailing list').click()
107 browser.getControl('Apply for Mailing List').click()107 browser.getControl('Apply for Mailing List').click()
108 mailing_list = review_list(team_name)108 mailing_list = review_list(team_name)
109 # pylint: disable-msg=F0401109 # pylint: disable-msg=F0401
110110
=== modified file 'lib/lp/services/mailman/testing/logwatcher.py'
--- lib/lp/services/mailman/testing/logwatcher.py 2009-07-17 00:26:05 +0000
+++ lib/lp/services/mailman/testing/logwatcher.py 2009-09-24 20:25:22 +0000
@@ -33,8 +33,8 @@
3333
34BREAK_ON_TIMEOUT = bool(os.getenv('BREAK_ON_TIMEOUT'))34BREAK_ON_TIMEOUT = bool(os.getenv('BREAK_ON_TIMEOUT'))
35LINES_TO_CAPTURE = 5035LINES_TO_CAPTURE = 50
36LOG_GROWTH_WAIT_INTERVAL = datetime.timedelta(seconds=20)36LOG_GROWTH_WAIT_INTERVAL = datetime.timedelta(seconds=10)
37FAILURE_CAPTURE_INTERVAL = datetime.timedelta(seconds=60)37FAILURE_CAPTURE_INTERVAL = datetime.timedelta(seconds=10)
38SECONDS_TO_SNOOZE = 0.138SECONDS_TO_SNOOZE = 0.1
39Empty = object()39Empty = object()
40NL = '\n'40NL = '\n'