launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #19271
[Merge] lp:~cjwatson/launchpad/async-branch-notifications into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/async-branch-notifications into lp:launchpad.
Commit message:
Send branch/repository modification notifications from jobs rather than directly from the webapp.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/async-branch-notifications/+merge/269788
Send branch/repository modification notifications from jobs rather than directly from the webapp.
The motivation for this is to allow us to restrict BaseMailer to permissive security policies in the near future and thus get rid of a pile of security proxy problems; for instance this fixes an unreported bug whereby a branch subscription from a private team will cause modifications to that branch by a user who can't access that private team to OOPS.
Most of this is straightforward job handling. The one really hairy bit is dealing with the upcoming mailer calling convention in pagetests. Anticipating that this will be useful in a couple of other places shortly, I added a "permissive_security_policy" context manager which deals with the bits we need.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/async-branch-notifications into lp:launchpad.
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2015-06-13 01:45:19 +0000
+++ lib/lp/code/configure.zcml 2015-09-01 17:16:20 +0000
@@ -585,6 +585,10 @@
<allow interface="lp.code.interfaces.branchjob.IReclaimBranchSpaceJob"/>
<allow interface="lp.code.interfaces.branchjob.IBranchJob"/>
</class>
+ <class class="lp.code.model.branchjob.BranchModifiedMailJob">
+ <allow interface="lp.code.interfaces.branchjob.IBranchModifiedMailJob"/>
+ <allow interface="lp.code.interfaces.branchjob.IBranchJob"/>
+ </class>
<securedutility
component="lp.code.model.branchjob.RevisionMailJob"
provides="lp.code.interfaces.branchjob.IRevisionMailJobSource">
@@ -605,6 +609,11 @@
provides="lp.code.interfaces.branchjob.IReclaimBranchSpaceJobSource">
<allow interface="lp.code.interfaces.branchjob.IReclaimBranchSpaceJobSource"/>
</securedutility>
+ <securedutility
+ component="lp.code.model.branchjob.BranchModifiedMailJob"
+ provides="lp.code.interfaces.branchjob.IBranchModifiedMailJobSource">
+ <allow interface="lp.code.interfaces.branchjob.IBranchModifiedMailJobSource"/>
+ </securedutility>
<!-- CodeImport -->
@@ -961,6 +970,11 @@
provides="lp.code.interfaces.gitjob.IReclaimGitRepositorySpaceJobSource">
<allow interface="lp.code.interfaces.gitjob.IReclaimGitRepositorySpaceJobSource" />
</securedutility>
+ <securedutility
+ component="lp.code.model.gitjob.GitRepositoryModifiedMailJob"
+ provides="lp.code.interfaces.gitjob.IGitRepositoryModifiedMailJobSource">
+ <allow interface="lp.code.interfaces.gitjob.IGitRepositoryModifiedMailJobSource" />
+ </securedutility>
<class class="lp.code.model.gitjob.GitRefScanJob">
<allow interface="lp.code.interfaces.gitjob.IGitJob" />
<allow interface="lp.code.interfaces.gitjob.IGitRefScanJob" />
@@ -969,6 +983,10 @@
<allow interface="lp.code.interfaces.gitjob.IGitJob" />
<allow interface="lp.code.interfaces.gitjob.IReclaimGitRepositorySpaceJob" />
</class>
+ <class class="lp.code.model.gitjob.GitRepositoryModifiedMailJob">
+ <allow interface="lp.code.interfaces.gitjob.IGitJob" />
+ <allow interface="lp.code.interfaces.gitjob.IGitRepositoryModifiedMailJob" />
+ </class>
<lp:help-folder folder="help" name="+help-code" />
=== modified file 'lib/lp/code/doc/branch-notifications.txt'
--- lib/lp/code/doc/branch-notifications.txt 2015-04-16 04:07:59 +0000
+++ lib/lp/code/doc/branch-notifications.txt 2015-09-01 17:16:20 +0000
@@ -421,6 +421,14 @@
>>> from zope.event import notify
>>> from lazr.lifecycle.event import ObjectModifiedEvent
>>> from lazr.lifecycle.snapshot import Snapshot
+ >>> from lp.code.interfaces.branchjob import IBranchModifiedMailJobSource
+ >>> from lp.services.job.runner import JobRunner
+ >>> from lp.testing.dbuser import dbuser
+
+ >>> def run_modified_mail_jobs():
+ ... job_source = getUtility(IBranchModifiedMailJobSource)
+ ... with dbuser("branchscanner"):
+ ... JobRunner.fromReady(job_source).runAll()
>>> login('test@xxxxxxxxxxxxx')
>>> before_modification = Snapshot(branch, providing=IBranch)
@@ -433,6 +441,7 @@
>>> notify(ObjectModifiedEvent(branch,
... before_modification,
... ['whiteboard']))
+ >>> run_modified_mail_jobs()
>>> notifications = pop_notifications()
>>> len(notifications)
@@ -482,6 +491,7 @@
>>> updated_fields = ['name','title','summary','url','whiteboard','lifecycle_status']
>>> notify(ObjectModifiedEvent(
... branch, before_modification, updated_fields))
+ >>> run_modified_mail_jobs()
>>> notifications = pop_notifications()
>>> len(notifications)
@@ -521,6 +531,7 @@
>>> updated_fields = ['whiteboard']
>>> notify(ObjectModifiedEvent(
... branch, before_modification, updated_fields))
+ >>> run_modified_mail_jobs()
>>> notifications = pop_notifications()
>>> len(notifications)
@@ -563,6 +574,7 @@
>>> updated_fields = ['whiteboard']
>>> notify(ObjectModifiedEvent(
... branch, before_modification, updated_fields))
+ >>> run_modified_mail_jobs()
>>> notifications = pop_notifications()
>>> len(notifications)
=== modified file 'lib/lp/code/interfaces/branchjob.py'
--- lib/lp/code/interfaces/branchjob.py 2013-07-04 07:18:21 +0000
+++ lib/lp/code/interfaces/branchjob.py 2015-09-01 17:16:20 +0000
@@ -9,6 +9,8 @@
__all__ = [
'IBranchJob',
+ 'IBranchModifiedMailJob',
+ 'IBranchModifiedMailJobSource',
'IBranchScanJob',
'IBranchScanJobSource',
'IBranchUpgradeJob',
@@ -184,3 +186,18 @@
:param branch_id: The id of the branch to remove from disk.
"""
+
+
+class IBranchModifiedMailJob(IRunnableJob):
+ """A Job to send email about branch modifications."""
+
+
+class IBranchModifiedMailJobSource(IJobSource):
+
+ def create(branch, user, branch_delta):
+ """Send email about branch modifications.
+
+ :param branch: The `IBranch` that was modified.
+ :param user: The `IPerson` who modified the branch.
+ :param branch_delta: An `IBranchDelta` describing the changes.
+ """
=== modified file 'lib/lp/code/interfaces/gitjob.py'
--- lib/lp/code/interfaces/gitjob.py 2015-05-19 11:29:24 +0000
+++ lib/lp/code/interfaces/gitjob.py 2015-09-01 17:16:20 +0000
@@ -9,6 +9,8 @@
'IGitJob',
'IGitRefScanJob',
'IGitRefScanJobSource',
+ 'IGitRepositoryModifiedMailJob',
+ 'IGitRepositoryModifiedMailJobSource',
'IReclaimGitRepositorySpaceJob',
'IReclaimGitRepositorySpaceJobSource',
]
@@ -75,3 +77,19 @@
:param repository_path: The storage path of the repository to remove
from storage.
"""
+
+
+class IGitRepositoryModifiedMailJob(IRunnableJob):
+ """A Job to send email about repository modifications."""
+
+
+class IGitRepositoryModifiedMailJobSource(IJobSource):
+
+ def create(repository, user, repository_delta):
+ """Send email about repository modifications.
+
+ :param repository: The `IGitRepository` that was modified.
+ :param user: The `IPerson` who modified the repository.
+ :param repository_delta: An `IGitRepositoryDelta` describing the
+ changes.
+ """
=== modified file 'lib/lp/code/mail/branch.py'
--- lib/lp/code/mail/branch.py 2015-08-23 22:53:55 +0000
+++ lib/lp/code/mail/branch.py 2015-09-01 17:16:20 +0000
@@ -5,7 +5,7 @@
__metaclass__ = type
-from zope.security.proxy import removeSecurityProxy
+from zope.component import getUtility
from lp.code.adapters.branch import BranchDelta
from lp.code.adapters.gitrepository import GitRepositoryDelta
@@ -15,35 +15,37 @@
CodeReviewNotificationLevel,
)
from lp.code.interfaces.branch import IBranch
+from lp.code.interfaces.branchjob import IBranchModifiedMailJobSource
+from lp.code.interfaces.gitjob import IGitRepositoryModifiedMailJobSource
from lp.code.interfaces.gitref import IGitRef
from lp.registry.interfaces.person import IPerson
from lp.registry.interfaces.product import IProduct
+from lp.services.config import config
from lp.services.mail import basemailer
from lp.services.mail.basemailer import BaseMailer
-from lp.services.mail.sendmail import format_address
+from lp.services.mail.sendmail import format_address_for_person
from lp.services.webapp import canonical_url
def send_branch_modified_notifications(branch, event):
- """Notify the related people that a branch has been modifed."""
+ """Notify the related people that a branch has been modified."""
user = IPerson(event.user)
branch_delta = BranchDelta.construct(
event.object_before_modification, branch, user)
if branch_delta is None:
return
- mailer = BranchMailer.forBranchModified(branch, user, branch_delta)
- mailer.sendAll()
+ getUtility(IBranchModifiedMailJobSource).create(branch, user, branch_delta)
def send_git_repository_modified_notifications(repository, event):
- """Notify the related people that a Git repository has been modifed."""
+ """Notify the related people that a Git repository has been modified."""
user = IPerson(event.user)
repository_delta = GitRepositoryDelta.construct(
event.object_before_modification, repository, user)
if repository_delta is None:
return
- mailer = BranchMailer.forBranchModified(repository, user, repository_delta)
- mailer.sendAll()
+ getUtility(IGitRepositoryModifiedMailJobSource).create(
+ repository, user, repository_delta)
class RecipientReason(basemailer.RecipientReason):
@@ -164,9 +166,10 @@
delta=None, contents=None, diff=None, message_id=None,
revno=None, revision_id=None, notification_type=None,
**kwargs):
- BaseMailer.__init__(self, subject, template_name, recipients,
- from_address, delta, message_id,
- notification_type)
+ super(BranchMailer, self).__init__(
+ subject, template_name, recipients, from_address,
+ message_id=message_id, notification_type=notification_type)
+ self.delta_text = delta
self.contents = contents
self.diff = diff
if diff is None:
@@ -193,7 +196,7 @@
actual_recipients = {}
# If the person editing the branch isn't in the team of the owner
# then notify the branch owner of the changes as well.
- if not user.inTeam(branch.owner):
+ if user is not None and not user.inTeam(branch.owner):
# Existing rationales are kept.
recipients.add(branch.owner, None, None)
for recipient in recipients:
@@ -208,8 +211,10 @@
actual_recipients[recipient] = \
RecipientReason.forBranchSubscriber(
subscription, branch, recipient, rationale)
- from_address = format_address(
- user.displayname, removeSecurityProxy(user).preferredemail.email)
+ if user is not None:
+ from_address = format_address_for_person(user)
+ else:
+ from_address = config.canonical.noreply_from_address
return cls(
'[Branch %(unique_name)s]', 'branch-modified.txt',
actual_recipients, from_address, delta=delta,
@@ -283,7 +288,8 @@
params['diff'] = self.contents or ''
if not self._includeDiff(email):
params['diff'] += self._explainNotPresentDiff(email)
- params.setdefault('delta', '')
+ params['delta'] = (
+ self.delta_text if self.delta_text is not None else '')
params.update(self.extra_template_params)
return params
@@ -331,8 +337,3 @@
ctrl.addAttachment(
self.diff, content_type='text/x-diff', inline=True,
filename='revision-diff.txt', charset='utf-8')
-
- @staticmethod
- def _format_user_address(user):
- naked_email = removeSecurityProxy(user).preferredemail.email
- return format_address(user.displayname, naked_email)
=== modified file 'lib/lp/code/mail/branchmergeproposal.py'
--- lib/lp/code/mail/branchmergeproposal.py 2015-08-23 22:53:55 +0000
+++ lib/lp/code/mail/branchmergeproposal.py 2015-09-01 17:16:20 +0000
@@ -10,7 +10,10 @@
from lp.code.mail.branch import BranchMailer
from lp.services.config import config
from lp.services.mail.basemailer import BaseMailer
-from lp.services.mail.sendmail import get_msgid
+from lp.services.mail.sendmail import (
+ format_address_for_person,
+ get_msgid,
+ )
from lp.services.webapp import canonical_url
@@ -23,13 +26,13 @@
direct_email=False):
BranchMailer.__init__(
self, subject, template_name, recipients, from_address,
- message_id=message_id, notification_type='code-review')
+ delta=delta, message_id=message_id,
+ notification_type='code-review')
self.merge_proposal = merge_proposal
if requested_reviews is None:
requested_reviews = []
self.requested_reviews = requested_reviews
self.preview_diff = preview_diff
- self.delta_text = delta
self.template_params = self._generateTemplateParams()
self.direct_email = direct_email
@@ -51,7 +54,7 @@
assert from_user.preferredemail is not None, (
'The sender must have an email address.')
- from_address = cls._format_user_address(from_user)
+ from_address = format_address_for_person(from_user)
return cls(
'%(proposal_title)s',
@@ -73,7 +76,7 @@
if from_user is not None:
assert from_user.preferredemail is not None, (
'The sender must have an email address.')
- from_address = cls._format_user_address(from_user)
+ from_address = format_address_for_person(from_user)
else:
from_address = config.canonical.noreply_from_address
return cls(
@@ -85,7 +88,7 @@
@classmethod
def forReviewRequest(cls, reason, merge_proposal, from_user):
"""Return a mailer for a request to review a BranchMergeProposal."""
- from_address = cls._format_user_address(from_user)
+ from_address = format_address_for_person(from_user)
recipients = {reason.subscriber: reason}
return cls(
'%(proposal_title)s',
@@ -114,7 +117,7 @@
continue
if vote.reviewer.hide_email_addresses:
continue
- to_addrs.append(self._format_user_address(vote.reviewer))
+ to_addrs.append(format_address_for_person(vote.reviewer))
return to_addrs
def _getHeaders(self, email, recipient):
=== modified file 'lib/lp/code/mail/tests/test_branchmergeproposal.py'
--- lib/lp/code/mail/tests/test_branchmergeproposal.py 2014-11-14 22:31:05 +0000
+++ lib/lp/code/mail/tests/test_branchmergeproposal.py 2015-09-01 17:16:20 +0000
@@ -32,6 +32,7 @@
from lp.code.model.diff import PreviewDiff
from lp.code.subscribers.branchmergeproposal import merge_proposal_modified
from lp.services.database.interfaces import IStore
+from lp.services.mail.sendmail import format_address_for_person
from lp.services.webapp import canonical_url
from lp.testing import (
person_logged_in,
@@ -135,7 +136,7 @@
'Message-Id': '<foobar-example-com>'},
ctrl.headers)
self.assertEqual('Baz Qux <baz.qux@xxxxxxxxxxx>', ctrl.from_addr)
- reviewer_id = mailer._format_user_address(reviewer)
+ reviewer_id = format_address_for_person(reviewer)
self.assertEqual(set([reviewer_id, bmp.address]), set(ctrl.to_addrs))
mailer.sendAll()
@@ -284,7 +285,7 @@
ctrl = mailer.generateEmail(bmp.registrant.preferredemail.email,
bmp.registrant)
reviewer = request.recipient
- reviewer_id = mailer._format_user_address(reviewer)
+ reviewer_id = format_address_for_person(reviewer)
self.assertEqual(set([reviewer_id, bmp.address]), set(ctrl.to_addrs))
def test_to_addrs_excludes_team_reviewers(self):
@@ -297,7 +298,7 @@
ctrl = mailer.generateEmail(subscriber.preferredemail.email,
subscriber)
reviewer = bmp.target_branch.owner
- reviewer_id = mailer._format_user_address(reviewer)
+ reviewer_id = format_address_for_person(reviewer)
self.assertEqual(set([reviewer_id, bmp.address]), set(ctrl.to_addrs))
def test_to_addrs_excludes_people_with_hidden_addresses(self):
@@ -557,7 +558,7 @@
request, request.merge_proposal, requester)
ctrl = mailer.generateEmail(request.recipient.preferredemail.email,
request.recipient)
- recipient_addr = mailer._format_user_address(request.recipient)
+ recipient_addr = format_address_for_person(request.recipient)
self.assertEqual([recipient_addr], ctrl.to_addrs)
def test_forReviewRequestMessageId(self):
=== modified file 'lib/lp/code/model/branchjob.py'
--- lib/lp/code/model/branchjob.py 2015-07-09 20:06:17 +0000
+++ lib/lp/code/model/branchjob.py 2015-09-01 17:16:20 +0000
@@ -3,6 +3,7 @@
__all__ = [
'BranchJob',
+ 'BranchModifiedMailJob',
'BranchScanJob',
'BranchJobDerived',
'BranchJobType',
@@ -67,6 +68,8 @@
)
from lp.code.interfaces.branchjob import (
IBranchJob,
+ IBranchModifiedMailJob,
+ IBranchModifiedMailJobSource,
IBranchScanJob,
IBranchScanJobSource,
IBranchUpgradeJob,
@@ -93,6 +96,7 @@
get_rw_server,
)
from lp.codehosting.vfs.branchfs import get_real_branch_path
+from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.productseries import IProductSeriesSet
from lp.scripts.helpers import TransactionFreeOperation
from lp.services.config import config
@@ -117,6 +121,7 @@
BaseRunnableJobSource,
)
from lp.services.mail.sendmail import format_address_for_person
+from lp.services.utils import text_delta
from lp.services.webapp import canonical_url
from lp.translations.interfaces.translationimportqueue import (
ITranslationImportQueue,
@@ -184,6 +189,12 @@
This job scans a branch for new revisions.
""")
+ BRANCH_MODIFIED_MAIL = DBItem(8, """
+ Branch modified mail
+
+ This job runs against a branch to send emails about modifications.
+ """)
+
@implementer(IBranchJob)
class BranchJob(SQLBase):
@@ -990,3 +1001,44 @@
branch_path = get_real_branch_path(self.branch_id)
if os.path.exists(branch_path):
shutil.rmtree(branch_path)
+
+
+@implementer(IBranchModifiedMailJob)
+@provider(IBranchModifiedMailJobSource)
+class BranchModifiedMailJob(BranchJobDerived):
+ """A Job that sends email about branch modifications."""
+
+ class_job_type = BranchJobType.BRANCH_MODIFIED_MAIL
+
+ config = config.IBranchModifiedMailJobSource
+
+ @classmethod
+ def create(cls, branch, user, branch_delta):
+ """See `IBranchModifiedMailJobSource`."""
+ metadata = {
+ 'user': user.id,
+ 'branch_delta': text_delta(
+ branch_delta, branch_delta.delta_values,
+ branch_delta.new_values, branch_delta.interface),
+ }
+ branch_job = BranchJob(branch, cls.class_job_type, metadata)
+ job = cls(branch_job)
+ job.celeryRunOnCommit()
+ return job
+
+ @property
+ def user(self):
+ return getUtility(IPersonSet).get(self.metadata['user'])
+
+ @property
+ def branch_delta(self):
+ return self.metadata['branch_delta']
+
+ def getMailer(self):
+ """Return a `BranchMailer` for this job."""
+ return BranchMailer.forBranchModified(
+ self.branch, self.user, self.branch_delta)
+
+ def run(self):
+ """See `IBranchModifiedMailJob`."""
+ self.getMailer().sendAll()
=== modified file 'lib/lp/code/model/gitjob.py'
--- lib/lp/code/model/gitjob.py 2015-08-10 12:51:52 +0000
+++ lib/lp/code/model/gitjob.py 2015-09-01 17:16:20 +0000
@@ -7,6 +7,7 @@
'GitJob',
'GitJobType',
'GitRefScanJob',
+ 'GitRepositoryModifiedMailJob',
'ReclaimGitRepositorySpaceJob',
]
@@ -36,9 +37,13 @@
IGitJob,
IGitRefScanJob,
IGitRefScanJobSource,
+ IGitRepositoryModifiedMailJob,
+ IGitRepositoryModifiedMailJobSource,
IReclaimGitRepositorySpaceJob,
IReclaimGitRepositorySpaceJobSource,
)
+from lp.code.mail.branch import BranchMailer
+from lp.registry.interfaces.person import IPersonSet
from lp.services.config import config
from lp.services.database.enumcol import EnumCol
from lp.services.database.interfaces import (
@@ -59,6 +64,7 @@
from lp.services.job.runner import BaseRunnableJob
from lp.services.mail.sendmail import format_address_for_person
from lp.services.scripts import log
+from lp.services.utils import text_delta
from lp.services.webhooks.interfaces import IWebhookSet
@@ -78,6 +84,13 @@
database from storage.
""")
+ REPOSITORY_MODIFIED_MAIL = DBItem(2, """
+ Repository modified mail
+
+ This job runs against a repository to send emails about
+ modifications.
+ """)
+
@implementer(IGitJob)
class GitJob(StormBase):
@@ -281,3 +294,44 @@
def run(self):
getUtility(IGitHostingClient).delete(self.repository_path, logger=log)
+
+
+@implementer(IGitRepositoryModifiedMailJob)
+@provider(IGitRepositoryModifiedMailJobSource)
+class GitRepositoryModifiedMailJob(GitJobDerived):
+ """A Job that sends email about repository modifications."""
+
+ class_job_type = GitJobType.REPOSITORY_MODIFIED_MAIL
+
+ config = config.IGitRepositoryModifiedMailJobSource
+
+ @classmethod
+ def create(cls, repository, user, repository_delta):
+ """See `IGitRepositoryModifiedMailJobSource`."""
+ metadata = {
+ "user": user.id,
+ "repository_delta": text_delta(
+ repository_delta, repository_delta.delta_values,
+ repository_delta.new_values, repository_delta.interface),
+ }
+ git_job = GitJob(repository, cls.class_job_type, metadata)
+ job = cls(git_job)
+ job.celeryRunOnCommit()
+ return job
+
+ @property
+ def user(self):
+ return getUtility(IPersonSet).get(self.metadata["user"])
+
+ @property
+ def repository_delta(self):
+ return self.metadata["repository_delta"]
+
+ def getMailer(self):
+ """Return a `BranchMailer` for this job."""
+ return BranchMailer.forBranchModified(
+ self.repository, self.user, self.repository_delta)
+
+ def run(self):
+ """See `IGitRepositoryModifiedMailJob`."""
+ self.getMailer().sendAll()
=== modified file 'lib/lp/code/model/tests/test_gitrepository.py'
--- lib/lp/code/model/tests/test_gitrepository.py 2015-08-06 12:03:36 +0000
+++ lib/lp/code/model/tests/test_gitrepository.py 2015-09-01 17:16:20 +0000
@@ -56,7 +56,10 @@
)
from lp.code.interfaces.defaultgit import ICanHasDefaultGitRepository
from lp.code.interfaces.githosting import IGitHostingClient
-from lp.code.interfaces.gitjob import IGitRefScanJobSource
+from lp.code.interfaces.gitjob import (
+ IGitRefScanJobSource,
+ IGitRepositoryModifiedMailJobSource,
+ )
from lp.code.interfaces.gitlookup import IGitLookup
from lp.code.interfaces.gitnamespace import (
IGitNamespacePolicy,
@@ -746,7 +749,9 @@
notify(ObjectModifiedEvent(
repository, repository_before_modification, ["name"],
user=repository.owner))
- transaction.commit()
+ with dbuser("branchscanner"):
+ JobRunner.fromReady(
+ getUtility(IGitRepositoryModifiedMailJobSource)).runAll()
self.assertEqual(1, len(stub.test_emails))
message = email.message_from_string(stub.test_emails[0][2])
body = message.get_payload(decode=True)
=== modified file 'lib/lp/code/stories/branches/xx-branch-edit.txt'
--- lib/lp/code/stories/branches/xx-branch-edit.txt 2013-09-27 04:13:23 +0000
+++ lib/lp/code/stories/branches/xx-branch-edit.txt 2015-09-01 17:16:20 +0000
@@ -63,6 +63,16 @@
Emails go out to the all the subscribers. Now there are no subscribers
so there whould be no emails.
+ >>> from zope.component import getUtility
+ >>> from lp.code.interfaces.branchjob import IBranchModifiedMailJobSource
+ >>> from lp.services.job.runner import JobRunner
+
+ >>> def run_modified_mail_jobs():
+ ... with permissive_security_policy("branchscanner"):
+ ... job_source = getUtility(IBranchModifiedMailJobSource)
+ ... JobRunner.fromReady(job_source).runAll()
+
+ >>> run_modified_mail_jobs()
>>> len(stub.test_emails)
0
@@ -214,7 +224,6 @@
The whiteboard is only visible and editable on import branches, and is
editable by any user.
- >>> from zope.component import getUtility
>>> from lp.registry.interfaces.person import IPersonSet
>>> from lp.code.enums import (
... BranchSubscriptionNotificationLevel, BranchSubscriptionDiffSize,
@@ -261,6 +270,7 @@
The subscribers of the branch are notified that someone else has
modified the details of the branch.
+ >>> run_modified_mail_jobs()
>>> len(stub.test_emails)
1
=== modified file 'lib/lp/services/config/schema-lazr.conf'
--- lib/lp/services/config/schema-lazr.conf 2015-08-28 08:38:54 +0000
+++ lib/lp/services/config/schema-lazr.conf 2015-09-01 17:16:20 +0000
@@ -1741,7 +1741,9 @@
# dbuser, the crontab_group, and the module that the job source class
# can be loaded from.
job_sources:
+ IBranchModifiedMailJobSource,
ICommercialExpiredJobSource,
+ IGitRepositoryModifiedMailJobSource,
IMembershipNotificationJobSource,
IPersonDeactivateJobSource,
IPersonMergeJobSource,
@@ -1758,6 +1760,11 @@
dbuser: merge-proposal-jobs
runner_class: TwistedJobRunner
+[IBranchModifiedMailJobSource]
+module: lp.code.interfaces.branchjob
+dbuser: branchscanner
+crontab_group: MAIN
+
[IBranchScanJobSource]
module: lp.code.interfaces.branchjob
dbuser: branchscanner
@@ -1780,6 +1787,11 @@
module: lp.code.interfaces.gitjob
dbuser: branchscanner
+[IGitRepositoryModifiedMailJobSource]
+module: lp.code.interfaces.gitjob
+dbuser: branchscanner
+crontab_group: MAIN
+
[IInitializeDistroSeriesJobSource]
module: lp.soyuz.interfaces.distributionjob
dbuser: initializedistroseries
=== modified file 'lib/lp/testing/pages.py'
--- lib/lp/testing/pages.py 2014-11-27 07:48:25 +0000
+++ lib/lp/testing/pages.py 2015-09-01 17:16:20 +0000
@@ -5,6 +5,7 @@
__metaclass__ = type
+from contextlib import contextmanager
from datetime import datetime
import doctest
from itertools import chain
@@ -39,6 +40,7 @@
SimpleCookie,
)
from zope.component import getUtility
+from zope.security.management import setSecurityPolicy
from zope.security.proxy import removeSecurityProxy
from zope.session.interfaces import ISession
from zope.testbrowser.testing import Browser
@@ -52,6 +54,7 @@
OAUTH_REALM,
)
from lp.services.webapp import canonical_url
+from lp.services.webapp.authorization import LaunchpadPermissiveSecurityPolicy
from lp.services.webapp.interfaces import OAuthPermission
from lp.services.webapp.servers import LaunchpadTestRequest
from lp.services.webapp.url import urlsplit
@@ -61,7 +64,9 @@
login,
login_person,
logout,
+ person_logged_in,
)
+from lp.testing.dbuser import dbuser
from lp.testing.factory import LaunchpadObjectFactory
from lp.testing.layers import PageTestLayer
from lp.testing.systemdocs import (
@@ -754,6 +759,26 @@
return setupBrowser(auth='Basic re@xxxxxx:test')
+@contextmanager
+def permissive_security_policy(dbuser_name=None):
+ """Context manager to run code with a permissive security policy.
+
+ This is just enough to run code such as `BaseMailer` that normally
+ expects to be called only from environments that use a permissive
+ security policy, such as jobs or scripts.
+ """
+ try:
+ old_policy = setSecurityPolicy(LaunchpadPermissiveSecurityPolicy)
+ if dbuser_name is not None:
+ dbuser_context = dbuser(dbuser_name)
+ else:
+ dbuser_context = contextmanager([None].__iter__)
+ with person_logged_in(ANONYMOUS), dbuser_context:
+ yield
+ finally:
+ setSecurityPolicy(old_policy)
+
+
def setUpGlobs(test):
test.globs['transaction'] = transaction
test.globs['http'] = UnstickyCookieHTTPCaller()
@@ -794,6 +819,7 @@
test.globs['login_person'] = login_person
test.globs['logout'] = logout
test.globs['parse_relationship_section'] = parse_relationship_section
+ test.globs['permissive_security_policy'] = permissive_security_policy
test.globs['pretty'] = pprint.PrettyPrinter(width=1).pformat
test.globs['print_action_links'] = print_action_links
test.globs['print_errors'] = print_errors
Follow ups