← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/opt-in-zopeless-immediate-mail into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/opt-in-zopeless-immediate-mail into lp:launchpad with lp:~cjwatson/launchpad/more-pop-notifications as a prerequisite.

Commit message:
Disable Zopeless immediate mail delivery, except for BaseMailer, job OOPS/error notifications, and a few other places that still need it.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #29744 in Launchpad itself: "In Zopeless, mails should be sent only when the transaction is committed"
  https://bugs.launchpad.net/launchpad/+bug/29744

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/opt-in-zopeless-immediate-mail/+merge/270383

Disable Zopeless immediate mail delivery, except for BaseMailer, job OOPS/error notifications, and a few other places that still need it.

This is a step towards being able to disable it across the board so that we can avoid ever having situations where operation/mail/operation/mail sequences send duplicate mail if a non-first operation fails and has to be retried.  There are still several things that need to be fixed, notably archiveuploader; but this branch at least means that it's disabled for tests by default, which will simplify the process of fixing archiveuploader.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/opt-in-zopeless-immediate-mail into lp:launchpad.
=== modified file 'daemons/buildd-manager.tac'
--- daemons/buildd-manager.tac	2011-12-29 05:29:36 +0000
+++ daemons/buildd-manager.tac	2015-09-08 11:52:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # Twisted Application Configuration file.
@@ -6,20 +6,15 @@
 
 from twisted.application import service
 from twisted.scripts.twistd import ServerOptions
-from twisted.web import server
 
+from lp.buildmaster.manager import BuilddManager
 from lp.services.config import dbconfig
 from lp.services.daemons import readyservice
 from lp.services.scripts import execute_zcml_for_scripts
-from lp.buildmaster.manager import BuilddManager
-from lp.services.mail.sendmail import set_immediate_mail_delivery
 from lp.services.twistedsupport.loggingsupport import RotatableFileLogObserver
 
 execute_zcml_for_scripts()
 dbconfig.override(dbuser='buildd_manager', isolation_level='read_committed')
-# XXX wgrant 2011-09-24 bug=29744: initZopeless used to do this.
-# Should be removed from callsites verified to not need it.
-set_immediate_mail_delivery(True)
 
 options = ServerOptions()
 options.parseOptions()
@@ -34,4 +29,3 @@
 # Service for scanning buildd slaves.
 service = BuilddManager()
 service.setServiceParent(application)
-

=== modified file 'lib/lp/bugs/scripts/bugnotification.py'
--- lib/lp/bugs/scripts/bugnotification.py	2015-08-06 12:09:28 +0000
+++ lib/lp/bugs/scripts/bugnotification.py	2015-09-08 11:52:24 +0000
@@ -35,7 +35,10 @@
 from lp.services.database.constants import UTC_NOW
 from lp.services.mail.helpers import get_email_template
 from lp.services.mail.mailwrapper import MailWrapper
-from lp.services.mail.sendmail import sendmail
+from lp.services.mail.sendmail import (
+    immediate_mail_delivery,
+    sendmail,
+    )
 from lp.services.scripts.base import LaunchpadCronScript
 from lp.services.scripts.logger import log
 from lp.services.webapp import canonical_url
@@ -339,7 +342,7 @@
 
 
 class SendBugNotifications(LaunchpadCronScript):
-    def main(self):
+    def send_notifications(self):
         notifications_sent = False
         bug_notification_set = getUtility(IBugNotificationSet)
         deferred_notifications = \
@@ -392,3 +395,9 @@
 
         if not notifications_sent:
             self.logger.debug("No notifications are pending to be sent.")
+
+    def main(self):
+        # XXX cjwatson 2015-09-05 bug=29744: SMTP exception handling here
+        # currently relies on this.
+        with immediate_mail_delivery(True):
+            self.send_notifications()

=== modified file 'lib/lp/bugs/scripts/tests/test_bugnotification.py'
--- lib/lp/bugs/scripts/tests/test_bugnotification.py	2015-08-06 12:09:28 +0000
+++ lib/lp/bugs/scripts/tests/test_bugnotification.py	2015-09-08 11:52:24 +0000
@@ -1368,7 +1368,7 @@
                 "send-bug-notifications", config.malone.bugnotification_dbuser,
                 ["-q"])
             script.txn = self.layer.txn
-            script.main()
+            script.send_notifications()
 
         self.assertEqual(1, len(self.oopses))
         self.assertIn(

=== modified file 'lib/lp/bugs/tests/bugs-emailinterface.txt'
--- lib/lp/bugs/tests/bugs-emailinterface.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/bugs/tests/bugs-emailinterface.txt	2015-09-08 11:52:24 +0000
@@ -81,8 +81,8 @@
 
     >>> def process_email(raw_mail):
     ...     msg = construct_email(raw_mail)
-    ...     handler.process(msg, msg['To'],
-    ...         )
+    ...     handler.process(msg, msg['To'])
+    ...     transaction.commit()
 
     >>> process_email(submit_mail)
 
@@ -454,9 +454,7 @@
     ...     signature = None
     >>> msg = email.message_from_string(
     ...     comment_mail, _class=MockUnsignedMessage)
-    >>> handler.process(
-    ...     msg, msg['To'],
-    ...     )
+    >>> handler.process(msg, msg['To'])
     True
     >>> transaction.commit()
 
@@ -514,9 +512,7 @@
     ...     return construct_email(edit_mail)
 
     >>> def submit_command_email(msg):
-    ...     handler.process(
-    ...         msg, msg['To'],
-    ...         )
+    ...     handler.process(msg, msg['To'])
     ...     transaction.commit()
 
     >>> def submit_commands(bug, *commands):
@@ -1805,10 +1801,9 @@
     >>> msg = signed_message_from_string(submit_mail)
     >>> import email.utils
     >>> msg['Message-Id'] = email.utils.make_msgid()
-    >>> handler.process(
-    ...     msg, msg['To'],
-    ...     )
+    >>> handler.process(msg, msg['To'])
     True
+    >>> transaction.commit()
     >>> print_latest_email()
     Subject: Submit Request Failure
     To: test@xxxxxxxxxxxxx

=== modified file 'lib/lp/code/mail/codehandler.py'
--- lib/lp/code/mail/codehandler.py	2015-07-08 16:05:11 +0000
+++ lib/lp/code/mail/codehandler.py	2015-09-08 11:52:24 +0000
@@ -316,11 +316,11 @@
                 message, context.vote, context.vote_tags, mail)
 
         except IncomingEmailError as error:
+            transaction.abort()
             send_process_error_notification(
                 str(user.preferredemail.email),
                 'Submit Request Failure',
                 error.message, mail, error.failing_command)
-            transaction.abort()
         return True
 
     @staticmethod

=== modified file 'lib/lp/services/job/celeryjob.py'
--- lib/lp/services/job/celeryjob.py	2015-08-03 07:06:45 +0000
+++ lib/lp/services/job/celeryjob.py	2015-09-08 11:52:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2012-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Celery-specific Job code.
@@ -34,7 +34,6 @@
     install_feature_controller,
     make_script_feature_controller,
     )
-from lp.services.mail.sendmail import set_immediate_mail_delivery
 from lp.services.job.model.job import (
     Job,
     UniversalJobSource,
@@ -139,7 +138,6 @@
         return
     transaction.abort()
     scripts.execute_zcml_for_scripts(use_web_security=False)
-    set_immediate_mail_delivery(True)
     needs_zcml = False
 
 

=== modified file 'lib/lp/services/job/runner.py'
--- lib/lp/services/job/runner.py	2015-08-04 00:19:36 +0000
+++ lib/lp/services/job/runner.py	2015-09-08 11:52:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Facilities for running Jobs."""
@@ -72,8 +72,8 @@
     IRunnableJob,
     )
 from lp.services.mail.sendmail import (
+    immediate_mail_delivery,
     MailController,
-    set_immediate_mail_delivery,
     )
 from lp.services.twistedsupport import run_reactor
 from lp.services.webapp import errorlog
@@ -177,7 +177,9 @@
         """Report this oops."""
         ctrl = self.getOopsMailController(oops['id'])
         if ctrl is not None:
-            ctrl.send()
+            # XXX cjwatson 2015-09-06 bug=29744: Can this be removed?
+            with immediate_mail_delivery(True):
+                ctrl.send()
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
@@ -187,7 +189,9 @@
         """See `IRunnableJob`."""
         ctrl = self.getUserErrorMailController(e)
         if ctrl is not None:
-            ctrl.send()
+            # XXX cjwatson 2015-09-06 bug=29744: Can this be removed?
+            with immediate_mail_delivery(True):
+                ctrl.send()
 
     def makeOopsReport(self, oops_config, info):
         """Generate an OOPS report using the given OOPS configuration."""
@@ -406,9 +410,6 @@
         scripts.execute_zcml_for_scripts(use_web_security=False)
         signal(SIGHUP, handler)
         dbconfig.override(dbuser=cls.dbuser, isolation_level='read_committed')
-        # XXX wgrant 2011-09-24 bug=29744: initZopeless used to do this.
-        # Should be removed from callsites verified to not need it.
-        set_immediate_mail_delivery(True)
 
     @staticmethod
     def __exit__(exc_type, exc_val, exc_tb):

=== modified file 'lib/lp/services/mail/basemailer.py'
--- lib/lp/services/mail/basemailer.py	2015-08-25 14:05:24 +0000
+++ lib/lp/services/mail/basemailer.py	2015-09-08 11:52:24 +0000
@@ -20,6 +20,7 @@
 from lp.services.mail.sendmail import (
     append_footer,
     format_address,
+    immediate_mail_delivery,
     MailController,
     )
 from lp.services.utils import text_delta
@@ -204,8 +205,12 @@
 
     def sendAll(self):
         """Send notifications to all recipients."""
-        for email, recipient in sorted(self._recipients.getRecipientPersons()):
-            self.sendOne(email, recipient)
+        # XXX cjwatson 2015-09-05 bug=29744: We currently depend on this to
+        # handle SMTPExceptions in the correct place.
+        with immediate_mail_delivery(True):
+            for email, recipient in sorted(
+                    self._recipients.getRecipientPersons()):
+                self.sendOne(email, recipient)
 
 
 class RecipientReason:

=== modified file 'lib/lp/services/mail/sendmail.py'
--- lib/lp/services/mail/sendmail.py	2015-07-21 09:04:01 +0000
+++ lib/lp/services/mail/sendmail.py	2015-09-08 11:52:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """The One True Way to send mail from the Launchpad application.
@@ -19,6 +19,7 @@
     'format_address',
     'format_address_for_person',
     'get_msgid',
+    'immediate_mail_delivery',
     'MailController',
     'sendmail',
     'set_immediate_mail_delivery',
@@ -29,6 +30,7 @@
 
 
 from binascii import b2a_qp
+from contextlib import contextmanager
 from email import charset
 from email.encoders import encode_base64
 from email.header import Header
@@ -178,6 +180,16 @@
     _immediate_mail_delivery = enabled
 
 
+@contextmanager
+def immediate_mail_delivery(enabled):
+    """Context manager to temporarily change immediate mail delivery."""
+    global _immediate_mail_delivery
+    previous = _immediate_mail_delivery
+    set_immediate_mail_delivery(enabled)
+    yield
+    set_immediate_mail_delivery(previous)
+
+
 def simple_sendmail(from_addr, to_addrs, subject, body, headers=None,
                     bulk=True):
     """Send an email from from_addr to to_addrs with the subject and body

=== modified file 'lib/lp/services/mail/tests/incomingmail.txt'
--- lib/lp/services/mail/tests/incomingmail.txt	2015-09-08 11:52:24 +0000
+++ lib/lp/services/mail/tests/incomingmail.txt	2015-09-08 11:52:24 +0000
@@ -45,16 +45,20 @@
 
 Now we send a few test mails to foo.com, bar.com, and baz.com:
 
+    >>> import transaction
     >>> from lp.services.mail.tests.helpers import read_test_message
     >>> from lp.services.mail.sendmail import sendmail as original_sendmail
     >>> from lp.testing.dbuser import switch_dbuser
 
 For these examples, we don't want the Precedence header added. Domains
 are treated without regard to case: for incoming mail, foo.com and
-FOO.COM are treated equivalently.
+FOO.COM are treated equivalently. We also commit to ensure that mail is
+sent immediately.
 
     >>> def sendmail(msg, to_addrs=None):
-    ...     return original_sendmail(msg, to_addrs=to_addrs, bulk=False)
+    ...     msgid = original_sendmail(msg, to_addrs=to_addrs, bulk=False)
+    ...     transaction.commit()
+    ...     return msgid
 
     >>> switch_dbuser('launchpad')
     >>> msgids = {'foo.com': [], 'bar.com': [], 'baz.com': []}

=== modified file 'lib/lp/services/mail/tests/test_incoming.py'
--- lib/lp/services/mail/tests/test_incoming.py	2015-07-08 16:05:11 +0000
+++ lib/lp/services/mail/tests/test_incoming.py	2015-09-08 11:52:24 +0000
@@ -75,6 +75,7 @@
             email_address, 'to@xxxxxxxxxxx', 'subject', invalid_body,
             bulk=False)
         ctrl.send()
+        transaction.commit()
         handleMail()
         self.assertEqual([], self.oopses)
         [notification] = pop_notifications()
@@ -101,6 +102,7 @@
             email_address, 'to@xxxxxxxxxxx', 'subject', fat_body,
             bulk=False)
         ctrl.send()
+        transaction.commit()
         handleMail()
         self.assertEqual([], self.oopses)
         [notification] = pop_notifications()

=== modified file 'lib/lp/services/scripts/base.py'
--- lib/lp/services/scripts/base.py	2014-08-29 01:34:04 +0000
+++ lib/lp/services/scripts/base.py	2015-09-08 11:52:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -44,7 +44,6 @@
     install_feature_controller,
     make_script_feature_controller,
     )
-from lp.services.mail.sendmail import set_immediate_mail_delivery
 from lp.services.scripts.interfaces.scriptactivity import IScriptActivitySet
 from lp.services.scripts.logger import OopsHandler
 from lp.services.webapp.errorlog import globalErrorUtility
@@ -307,10 +306,6 @@
         self._init_zca(use_web_security=use_web_security)
         self._init_db(isolation=isolation)
 
-        # XXX wgrant 2011-09-24 bug=29744: initZopeless used to do this.
-        # Should be called directly by scripts that actually need it.
-        set_immediate_mail_delivery(True)
-
         date_started = datetime.datetime.now(UTC)
         profiler = None
         if self.options.profile:

=== modified file 'lib/lp/testing/layers.py'
--- lib/lp/testing/layers.py	2013-08-14 11:15:51 +0000
+++ lib/lp/testing/layers.py	2015-09-08 11:52:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Layers used by Launchpad tests.
@@ -1498,10 +1498,6 @@
     @profiled
     def testSetUp(cls):
         dbconfig.override(isolation_level='read_committed')
-        # XXX wgrant 2011-09-24 bug=29744: initZopeless used to do this.
-        # Tests that still need it should eventually set this directly,
-        # so the whole layer is not polluted.
-        set_immediate_mail_delivery(True)
 
         # Connect Storm
         reconnect_stores()

=== modified file 'scripts/mlist-import.py'
--- scripts/mlist-import.py	2013-01-07 02:40:55 +0000
+++ scripts/mlist-import.py	2015-09-08 11:52:24 +0000
@@ -24,6 +24,7 @@
 
 from lp.registry.scripts.mlistimport import Importer
 from lp.services.config import config
+from lp.services.mail.sendmail import set_immediate_mail_delivery
 from lp.services.scripts.base import LaunchpadScript
 
 
@@ -56,6 +57,12 @@
 
     def main(self):
         """See `LaunchpadScript`."""
+        # XXX cjwatson 2015-09-06 bug=29744: We need immediate mail delivery
+        # for the current implementation of the --notifications switch, and
+        # because the tests expect notifications to end up in
+        # LayerProcessController.smtp_controller.
+        set_immediate_mail_delivery(True)
+
         team_name = None
         if len(self.args) == 0:
             self.parser.error('Missing team name')


Follow ups