Merge lp:~abentley/launchpad/celery-everywhere-8 into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 15155
Proposed branch: lp:~abentley/launchpad/celery-everywhere-8
Merge into: lp:launchpad
Prerequisite: lp:~abentley/launchpad/celery-everywhere-7
Diff against target: 257 lines (+113/-15)
4 files modified
lib/lp/registry/model/persontransferjob.py (+15/-2)
lib/lp/registry/tests/test_membership_notification_job.py (+44/-1)
lib/lp/registry/tests/test_person_merge_job.py (+52/-11)
lib/lp/services/job/model/job.py (+2/-1)
To merge this branch: bzr merge lp:~abentley/launchpad/celery-everywhere-8
Reviewer Review Type Date Requested Status
Deryck Hodge (community) Approve
Review via email: mp+103545@code.launchpad.net

Commit message

Support running PersonTransferJobs via Celery.

Description of the change

= Summary =
Support running PersonTransferJob via Celery.

== Pre-implementation notes ==
None

== LOC Rationale ==
Part of a resourced arc that will ultimately remove code.

== Implementation details ==
PersonTransferJob now provides makeDerived.
PersonTransferJobDerived now uses the EnumeratedSubclass metatype, so it can turn a PersonTransferJob into the appropriate subclass.
PersonTransferJobDerived.create now requests the job to run via Celery.
UniversalJobSource now supports PersonTransferJob.
PersonMergeJob and MembershipNotificationJob both provide configs.

== Tests ==
bin/test --layer=CeleryJobLayer -m 'membership_notification|person_merge'

== Demo and Q/A ==
None

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/tests/test_pofilestatsjob.py
  lib/lp/registry/tests/test_membership_notification_job.py
  lib/lp/translations/model/pofilestatsjob.py
  lib/lp/registry/model/persontransferjob.py
  lib/lp/registry/tests/test_person_merge_job.py
  lib/lp/services/job/model/job.py

To post a comment you must log in.
Revision history for this message
Deryck Hodge (deryck) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/model/persontransferjob.py'
2--- lib/lp/registry/model/persontransferjob.py 2012-01-27 11:12:53 +0000
3+++ lib/lp/registry/model/persontransferjob.py 2012-04-26 16:21:20 +0000
4@@ -52,7 +52,10 @@
5 IStore,
6 )
7 from lp.services.database.stormbase import StormBase
8-from lp.services.job.model.job import Job
9+from lp.services.job.model.job import (
10+ EnumeratedSubclass,
11+ Job,
12+ )
13 from lp.services.job.runner import BaseRunnableJob
14 from lp.services.mail.helpers import (
15 get_contact_email_addresses,
16@@ -115,6 +118,9 @@
17 # but the DB representation is unicode.
18 self._json_data = json_data.decode('utf-8')
19
20+ def makeDerived(self):
21+ return PersonTransferJobDerived.makeSubclass(self)
22+
23
24 class PersonTransferJobDerived(BaseRunnableJob):
25 """Intermediate class for deriving from PersonTransferJob.
26@@ -126,6 +132,7 @@
27 the run() method.
28 """
29
30+ __metaclass__ = EnumeratedSubclass
31 delegates(IPersonTransferJob)
32 classProvides(IPersonTransferJobSource)
33
34@@ -146,7 +153,9 @@
35 major_person=major_person,
36 job_type=cls.class_job_type,
37 metadata=metadata)
38- return cls(job)
39+ derived = cls(job)
40+ derived.celeryRunOnCommit()
41+ return derived
42
43 @classmethod
44 def iterReady(cls):
45@@ -176,6 +185,8 @@
46
47 class_job_type = PersonTransferJobType.MEMBERSHIP_NOTIFICATION
48
49+ config = config.IMembershipNotificationJobSource
50+
51 @classmethod
52 def create(cls, member, team, reviewer, old_status, new_status,
53 last_change_comment=None):
54@@ -342,6 +353,8 @@
55
56 class_job_type = PersonTransferJobType.MERGE
57
58+ config = config.IPersonMergeJobSource
59+
60 @classmethod
61 def create(cls, from_person, to_person, reviewer=None, delete=False):
62 """See `IPersonMergeJobSource`."""
63
64=== modified file 'lib/lp/registry/tests/test_membership_notification_job.py'
65--- lib/lp/registry/tests/test_membership_notification_job.py 2012-01-01 02:58:52 +0000
66+++ lib/lp/registry/tests/test_membership_notification_job.py 2012-04-26 16:21:20 +0000
67@@ -19,14 +19,22 @@
68 TeamMembershipStatus,
69 )
70 from lp.registry.model.persontransferjob import MembershipNotificationJob
71+from lp.services.features.testing import FeatureFixture
72 from lp.services.job.interfaces.job import JobStatus
73+from lp.services.job.tests import (
74+ block_on_job,
75+ pop_remote_notifications,
76+ )
77 from lp.testing import (
78 login_person,
79 person_logged_in,
80 run_script,
81 TestCaseWithFactory,
82 )
83-from lp.testing.layers import DatabaseFunctionalLayer
84+from lp.testing.layers import (
85+ CeleryJobLayer,
86+ DatabaseFunctionalLayer,
87+ )
88 from lp.testing.sampledata import ADMIN_EMAIL
89
90
91@@ -110,3 +118,38 @@
92 self.assertEqual(0, exit_code)
93 self.assertTrue(job_repr in err, err)
94 self.assertTrue("MembershipNotificationJob sent email" in err, err)
95+
96+
97+class TestViaCelery(TestCaseWithFactory):
98+
99+ layer = CeleryJobLayer
100+
101+ def test_smoke_admining_team(self):
102+ # Smoke test, primarily for DB permissions needed by queries to work
103+ # with admining users and teams
104+ # Check the oopses in /var/tmp/lperr.test if the assertions fail.
105+ self.useFixture(FeatureFixture({
106+ 'jobs.celery.enabled_classes': 'MembershipNotificationJob'
107+ }))
108+ team = self.factory.makeTeam(name='a-team')
109+ with person_logged_in(team.teamowner):
110+ # This implicitly creates a job, but it is not the job under test.
111+ admining_team = self.factory.makeTeam()
112+ team.addMember(
113+ admining_team, team.teamowner, force_team_add=True)
114+ membership = getUtility(ITeamMembershipSet).getByPersonAndTeam(
115+ admining_team, team)
116+ membership.setStatus(
117+ TeamMembershipStatus.ADMIN, team.teamowner)
118+ person = self.factory.makePerson(name='murdock')
119+ with block_on_job(self):
120+ transaction.commit()
121+ pop_remote_notifications()
122+ job = getUtility(IMembershipNotificationJobSource).create(
123+ person, team, team.teamowner,
124+ TeamMembershipStatus.APPROVED, TeamMembershipStatus.ADMIN)
125+ with block_on_job(self):
126+ transaction.commit()
127+ self.assertEqual(JobStatus.COMPLETED, job.status)
128+ (notification,) = pop_remote_notifications()
129+ self.assertIn('murdock made admin by', notification['Subject'])
130
131=== modified file 'lib/lp/registry/tests/test_person_merge_job.py'
132--- lib/lp/registry/tests/test_person_merge_job.py 2012-01-01 02:58:52 +0000
133+++ lib/lp/registry/tests/test_person_merge_job.py 2012-04-26 16:21:20 +0000
134@@ -1,4 +1,4 @@
135-# Copyright 2011 Canonical Ltd. This software is licensed under the
136+# Copyright 2012 Canonical Ltd. This software is licensed under the
137 # GNU Affero General Public License version 3 (see the file LICENSE).
138
139 """Tests of `PersonMergeJob`."""
140@@ -20,9 +20,11 @@
141 IMasterObject,
142 IStore,
143 )
144+from lp.services.features.testing import FeatureFixture
145 from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
146 from lp.services.job.interfaces.job import JobStatus
147 from lp.services.job.model.job import Job
148+from lp.services.job.tests import block_on_job
149 from lp.services.log.logger import BufferLogger
150 from lp.services.mail.sendmail import format_address_for_person
151 from lp.services.scripts import log
152@@ -31,7 +33,33 @@
153 run_script,
154 TestCaseWithFactory,
155 )
156-from lp.testing.layers import DatabaseFunctionalLayer
157+from lp.testing.layers import (
158+ CeleryJobLayer,
159+ DatabaseFunctionalLayer,
160+ )
161+
162+
163+def create_job(factory):
164+ """Create a PersonMergeJob for testing purposes.
165+
166+ :param factory: A LaunchpadObjectFactory.
167+ """
168+ from_person = factory.makePerson(name='void')
169+ to_person = factory.makePerson(name='gestalt')
170+ return getUtility(IPersonMergeJobSource).create(
171+ from_person=from_person, to_person=to_person)
172+
173+
174+def transfer_email(job):
175+ """Reassign email address using the people specified in the job.
176+
177+ IPersonSet.merge() does not (yet) promise to do this.
178+ """
179+ from_email = IMasterObject(job.from_person.preferredemail)
180+ removeSecurityProxy(from_email).personID = job.to_person.id
181+ removeSecurityProxy(from_email).accountID = job.to_person.accountID
182+ removeSecurityProxy(from_email).status = EmailAddressStatus.NEW
183+ IStore(from_email).flush()
184
185
186 class TestPersonMergeJob(TestCaseWithFactory):
187@@ -40,11 +68,10 @@
188
189 def setUp(self):
190 super(TestPersonMergeJob, self).setUp()
191- self.from_person = self.factory.makePerson(name='void')
192- self.to_person = self.factory.makePerson(name='gestalt')
193 self.job_source = getUtility(IPersonMergeJobSource)
194- self.job = self.job_source.create(
195- from_person=self.from_person, to_person=self.to_person)
196+ self.job = create_job(self.factory)
197+ self.from_person = self.job.from_person
198+ self.to_person = self.job.to_person
199
200 def test_interface(self):
201 # PersonMergeJob implements IPersonMergeJob.
202@@ -90,11 +117,7 @@
203 def transfer_email(self):
204 # Reassign from_person's email address over to to_person because
205 # IPersonSet.merge() does not (yet) promise to do that.
206- from_email = IMasterObject(self.from_person.preferredemail)
207- removeSecurityProxy(from_email).personID = self.to_person.id
208- removeSecurityProxy(from_email).accountID = self.to_person.accountID
209- removeSecurityProxy(from_email).status = EmailAddressStatus.NEW
210- IStore(from_email).flush()
211+ transfer_email(self.job)
212
213 def test_run(self):
214 # When run it merges from_person into to_person.
215@@ -179,3 +202,21 @@
216 self.assertEqual([self.job], self.find())
217 else:
218 self.assertEqual([], self.find())
219+
220+
221+class TestViaCelery(TestCaseWithFactory):
222+ """Test that PersonMergeJob runs under Celery."""
223+
224+ layer = CeleryJobLayer
225+
226+ def test_run(self):
227+ # When run it merges from_person into to_person.
228+ self.useFixture(FeatureFixture({
229+ 'jobs.celery.enabled_classes': 'PersonMergeJob',
230+ }))
231+ job = create_job(self.factory)
232+ transfer_email(job)
233+ from_person = job.from_person
234+ with block_on_job(self):
235+ transaction.commit()
236+ self.assertEqual(job.to_person, from_person.merged)
237
238=== modified file 'lib/lp/services/job/model/job.py'
239--- lib/lp/services/job/model/job.py 2012-04-24 20:57:27 +0000
240+++ lib/lp/services/job/model/job.py 2012-04-26 16:21:20 +0000
241@@ -276,6 +276,7 @@
242 from lp.code.model.branchmergeproposaljob import (
243 BranchMergeProposalJob,
244 )
245+ from lp.registry.model.persontransferjob import PersonTransferJob
246 from lp.soyuz.model.distributionjob import DistributionJob
247 from lp.translations.model.pofilestatsjob import POFileStatsJob
248 from lp.translations.model.translationsharingjob import (
249@@ -286,7 +287,7 @@
250
251 for baseclass in [
252 ApportJob, BranchJob, BranchMergeProposalJob, DistributionJob,
253- POFileStatsJob, TranslationSharingJob,
254+ PersonTransferJob, POFileStatsJob, TranslationSharingJob,
255 ]:
256 derived, base_class, store = cls._getDerived(job_id, baseclass)
257 if derived is not None: