Merge lp:~michael.nelson/launchpad/delegate-to-buildfarmjob into lp:launchpad

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~michael.nelson/launchpad/delegate-to-buildfarmjob
Merge into: lp:launchpad
Diff against target: 718 lines (+215/-123)
15 files modified
lib/lp/buildmaster/doc/buildfarmjob.txt (+32/-9)
lib/lp/buildmaster/interfaces/buildfarmbranchjob.py (+1/-2)
lib/lp/buildmaster/interfaces/buildfarmjob.py (+15/-25)
lib/lp/buildmaster/model/buildfarmjob.py (+64/-37)
lib/lp/buildmaster/model/packagebuildfarmjob.py (+29/-3)
lib/lp/buildmaster/tests/test_buildqueue.py (+24/-17)
lib/lp/code/configure.zcml (+2/-1)
lib/lp/code/model/sourcepackagerecipebuild.py (+5/-4)
lib/lp/soyuz/configure.zcml (+2/-0)
lib/lp/soyuz/model/binarypackagebuild.py (+1/-3)
lib/lp/soyuz/model/buildpackagejob.py (+11/-5)
lib/lp/soyuz/tests/test_buildpackagejob.py (+13/-0)
lib/lp/translations/configure.zcml (+2/-0)
lib/lp/translations/model/translationtemplatesbuildjob.py (+8/-9)
lib/lp/translations/tests/test_translationtemplatesbuildjob.py (+6/-8)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/delegate-to-buildfarmjob
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+23553@code.launchpad.net

Commit message

Switch BuildFarmJob from an inherited mix-in to a delegated object in preparation for the build generalisation work.

Description of the change

This branch switches BuildFarmJob from an inherited mix-in to a delegated object, in preparation for the build generalisation work (where BuildFarmJob will become a concrete Storm class).

While at it, I used the opportunity to merge two interfaces specific to the build farm queue scheduling into existing interfaces (see bug 562252).

There is no demo url, this is just an implementation detail.

Test command:
bin/test -vvt test_translationtemplatesbuildjob -t test_buildqueue -t doc/buildfarmjob.txt

Pre-implementation:
This is part of the LEP https://dev.launchpad.net/LEP/GeneralBuildHistories which has been discussed with jml. The reason for the delegation rather than inheritance is that BuildFarmJob will become a concrete Storm class.

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Hi Michael,

This branch looks good.

Since _set_build_farm_job() is a method which is intended to be overridden please provide a docstring for it.

Also, our coding guidelines state the docstring must be a single line followed by a longer explanation if needed. It is quite Draconian and leads to some haiku-like challenges but please see if you can adhere to the rule.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/buildmaster/doc/buildfarmjob.txt'
2--- lib/lp/buildmaster/doc/buildfarmjob.txt 2010-01-14 09:55:06 +0000
3+++ lib/lp/buildmaster/doc/buildfarmjob.txt 2010-04-21 11:53:24 +0000
4@@ -1,20 +1,43 @@
5-= BuildFarmJob =
6+BuildFarmJob
7+============
8
9 >>> from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
10- >>> from lp.buildmaster.model.buildfarmjob import BuildFarmJob
11+ >>> from lp.buildmaster.model.buildfarmjob import (
12+ ... BuildFarmJob, BuildFarmJobDerived)
13
14 BuildFarmJob provides a basic implementation of IBuildFarmJob. The
15-specific build farm job classes should inherit from it.
16+specific build farm job classes will automatically delegate to
17+BuildFarmJob by inheriting BuildFarmJobDerived.
18
19 >>> buildfarmjob = BuildFarmJob()
20- >>> verifyObject(IBuildFarmJob, buildfarmjob)
21+ >>> verifyObject(IBuildFarmJob, buildfarmjob)
22 True
23
24-As a class, it also provides ISpecificBuildFarmJobClass so that
25-BuildQueue can find the instance of a specific build-farm job
26-implementation associated with a given Job instance.
27+The BuildFarmJob class does not (yet) do a lot itself, other than
28+providing default implementations for its methods.
29+
30+ >>> print buildfarmjob.getLogFileName()
31+ buildlog.txt
32+
33+ >>> buildfarmjob.getName()
34+ Traceback (most recent call last):
35+ ...
36+ NotImplementedError
37+
38+
39+BuildFarmJobDerived
40+===================
41+
42+BuildFarmJobDerived exists to encapsulate the functionality required
43+by classes delegating IBuildFarmJob.
44
45 >>> from lp.buildmaster.interfaces.buildfarmjob import (
46- ... ISpecificBuildFarmJobClass)
47- >>> verifyObject(ISpecificBuildFarmJobClass, BuildFarmJob)
48+ ... IBuildFarmJobDerived)
49+ >>> verifyObject(IBuildFarmJobDerived, BuildFarmJobDerived())
50 True
51+
52+This class also includes the getByJob() class method used to find the
53+instance of a specific build-farm job implementation associated with a
54+given Job instance which must be called in the context of a concrete
55+derived class. See lp.buildmaster.tests.test_buildqueue for examples.
56+
57
58=== modified file 'lib/lp/buildmaster/interfaces/buildfarmbranchjob.py'
59--- lib/lp/buildmaster/interfaces/buildfarmbranchjob.py 2010-03-17 05:58:22 +0000
60+++ lib/lp/buildmaster/interfaces/buildfarmbranchjob.py 2010-04-21 11:53:24 +0000
61@@ -8,11 +8,10 @@
62 'IBuildFarmBranchJob'
63 ]
64
65-from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
66 from lp.code.interfaces.branchjob import IBranchJob
67
68
69-class IBuildFarmBranchJob(IBuildFarmJob, IBranchJob):
70+class IBuildFarmBranchJob(IBranchJob):
71 """An `IBuildFarmJob` that's also an `IBranchJob`.
72
73 Use this interface for `IBuildFarmJob` implementations that do not
74
75=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
76--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-12 05:52:01 +0000
77+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2010-04-21 11:53:24 +0000
78@@ -9,8 +9,7 @@
79
80 __all__ = [
81 'IBuildFarmJob',
82- 'IBuildFarmCandidateJobSelection',
83- 'ISpecificBuildFarmJobClass',
84+ 'IBuildFarmJobDerived',
85 'BuildFarmJobType',
86 ]
87
88@@ -57,16 +56,6 @@
89 class IBuildFarmJob(Interface):
90 """Operations that jobs for the build farm must implement."""
91
92- def generateSlaveBuildCookie():
93- """Produce a cookie for the slave as a token of the job it's doing.
94-
95- The cookie need not be unique, but should be hard for a
96- compromised slave to guess.
97-
98- :return: a hard-to-guess ASCII string that can be reproduced
99- accurately based on this job's properties.
100- """
101-
102 def score():
103 """Calculate a job score appropriate for the job type in question."""
104
105@@ -101,11 +90,10 @@
106 "return None."))
107
108
109-class ISpecificBuildFarmJobClass(Interface):
110- """Class interface provided by `IBuildFarmJob` classes.
111+class IBuildFarmJobDerived(Interface):
112+ """Common functionality required by classes delegating IBuildFarmJob.
113
114- Used by the `BuildQueue` to find the specific build-farm job objects
115- it needs to dispatch to builders.
116+ An implementation of this class must setup the necessary delagation.
117 """
118
119 def getByJob(job):
120@@ -115,15 +103,6 @@
121 has an entry associated with `job`.
122 """
123
124-
125-class IBuildFarmCandidateJobSelection(Interface):
126- """Operations for refining candidate job selection (optional).
127-
128- Job type classes that do *not* need to refine candidate job selection may
129- be derived from `BuildFarmJob` which provides a base implementation of
130- this interface.
131- """
132-
133 def addCandidateSelectionCriteria(processor, virtualized):
134 """Provide a sub-query to refine the candidate job selection.
135
136@@ -159,3 +138,14 @@
137 :return: True if the candidate job should be dispatched
138 to a builder, False otherwise.
139 """
140+
141+ def generateSlaveBuildCookie():
142+ """Produce a cookie for the slave as a token of the job it's doing.
143+
144+ The cookie need not be unique, but should be hard for a
145+ compromised slave to guess.
146+
147+ :return: a hard-to-guess ASCII string that can be reproduced
148+ accurately based on this job's properties.
149+ """
150+
151
152=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
153--- lib/lp/buildmaster/model/buildfarmjob.py 2010-04-12 05:52:01 +0000
154+++ lib/lp/buildmaster/model/buildfarmjob.py 2010-04-21 11:53:24 +0000
155@@ -2,48 +2,31 @@
156 # GNU Affero General Public License version 3 (see the file LICENSE).
157
158 __metaclass__ = type
159-__all__ = ['BuildFarmJob']
160-
161+__all__ = [
162+ 'BuildFarmJob',
163+ 'BuildFarmJobDerived',
164+ ]
165+
166+
167+from lazr.delegates import delegates
168
169 import hashlib
170
171 from zope.component import getUtility
172-from zope.interface import classProvides, implements
173+from zope.interface import implements
174 from zope.security.proxy import removeSecurityProxy
175
176 from canonical.launchpad.webapp.interfaces import (
177 DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE)
178+
179 from lp.buildmaster.interfaces.buildfarmjob import (
180- IBuildFarmJob, IBuildFarmCandidateJobSelection,
181- ISpecificBuildFarmJobClass)
182+ IBuildFarmJob, IBuildFarmJobDerived)
183 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
184
185
186 class BuildFarmJob:
187- """Mix-in class for `IBuildFarmJob` implementations."""
188+ """A base implementation for `IBuildFarmJob` classes."""
189 implements(IBuildFarmJob)
190- classProvides(
191- IBuildFarmCandidateJobSelection, ISpecificBuildFarmJobClass)
192-
193- def generateSlaveBuildCookie(self):
194- """See `IBuildFarmJob`."""
195- buildqueue = getUtility(IBuildQueueSet).getByJob(self.job)
196-
197- if buildqueue.processor is None:
198- processor = '*'
199- else:
200- processor = repr(buildqueue.processor.id)
201-
202- contents = ';'.join([
203- repr(removeSecurityProxy(self.job).id),
204- self.job.date_created.isoformat(),
205- repr(buildqueue.id),
206- buildqueue.job_type.name,
207- processor,
208- self.getName(),
209- ])
210-
211- return hashlib.sha1(contents).hexdigest()
212
213 def score(self):
214 """See `IBuildFarmJob`."""
215@@ -83,22 +66,66 @@
216 """See `IBuildFarmJob`."""
217 return None
218
219- @staticmethod
220- def addCandidateSelectionCriteria(processor, virtualized):
221- """See `IBuildFarmCandidateJobSelection`."""
222- return ('')
223+
224+class BuildFarmJobDerived:
225+ """See `IBuildFarmJobDerived`."""
226+ implements(IBuildFarmJobDerived)
227+ delegates(IBuildFarmJob, context='_build_farm_job')
228+
229+ def __init__(self, *args, **kwargs):
230+ """Ensure the instance to which we delegate is set on creation."""
231+ self._set_build_farm_job()
232+ super(BuildFarmJobDerived, self).__init__(*args, **kwargs)
233+
234+ def __storm_loaded__(self):
235+ """Set the attribute for our IBuildFarmJob delegation.
236+
237+ This is needed here as __init__() is not called when a storm object
238+ is loaded from the database.
239+ """
240+ self._set_build_farm_job()
241+
242+ def _set_build_farm_job(self):
243+ """Set the build farm job to which we will delegate.
244+
245+ Sub-classes can override as required.
246+ """
247+ self._build_farm_job = BuildFarmJob()
248
249 @classmethod
250 def getByJob(cls, job):
251- """See `ISpecificBuildFarmJobClass`.
252- This base implementation should work for most build farm job
253- types, but some need to override it.
254- """
255+ """See `IBuildFarmJobDerived`."""
256 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
257 return store.find(cls, cls.job == job).one()
258
259 @staticmethod
260+ def addCandidateSelectionCriteria(processor, virtualized):
261+ """See `IBuildFarmJobDerived`."""
262+ return ('')
263+
264+ @staticmethod
265 def postprocessCandidate(job, logger):
266- """See `IBuildFarmCandidateJobSelection`."""
267+ """See `IBuildFarmJobDerived`."""
268 return True
269
270+ def generateSlaveBuildCookie(self):
271+ """See `IBuildFarmJobDerived`."""
272+ buildqueue = getUtility(IBuildQueueSet).getByJob(self.job)
273+
274+ if buildqueue.processor is None:
275+ processor = '*'
276+ else:
277+ processor = repr(buildqueue.processor.id)
278+
279+ contents = ';'.join([
280+ repr(removeSecurityProxy(self.job).id),
281+ self.job.date_created.isoformat(),
282+ repr(buildqueue.id),
283+ buildqueue.job_type.name,
284+ processor,
285+ self.getName(),
286+ ])
287+
288+ return hashlib.sha1(contents).hexdigest()
289+
290+
291
292=== modified file 'lib/lp/buildmaster/model/packagebuildfarmjob.py'
293--- lib/lp/buildmaster/model/packagebuildfarmjob.py 2010-03-25 14:38:25 +0000
294+++ lib/lp/buildmaster/model/packagebuildfarmjob.py 2010-04-21 11:53:24 +0000
295@@ -2,17 +2,32 @@
296 # GNU Affero General Public License version 3 (see the file LICENSE).
297
298 __metaclass__ = type
299-__all__ = ['PackageBuildFarmJob']
300+__all__ = [
301+ 'PackageBuildFarmJob',
302+ 'PackageBuildFarmJobDerived',
303+ ]
304
305
306 from canonical.database.constants import UTC_NOW
307
308 from lp.buildmaster.interfaces.buildbase import BuildStatus
309-from lp.buildmaster.model.buildfarmjob import BuildFarmJob
310+from lp.buildmaster.model.buildfarmjob import (
311+ BuildFarmJob, BuildFarmJobDerived)
312
313
314 class PackageBuildFarmJob(BuildFarmJob):
315- """Mix-in class for `IBuildFarmJob` implementations for package builds."""
316+ """An implementation of `IBuildFarmJob` for package builds."""
317+
318+ def __init__(self, build):
319+ """Store the build for this package build farm job.
320+
321+ XXX 2010-04-12 michael.nelson bug=536700
322+ The build param will no longer be necessary once BuildFarmJob is
323+ itself a concrete class. This class (PackageBuildFarmJob)
324+ will also be renamed PackageBuild and turned into a concrete class.
325+ """
326+ super(PackageBuildFarmJob, self).__init__()
327+ self.build = build
328
329 def getTitle(self):
330 """See `IBuildFarmJob`."""
331@@ -32,3 +47,14 @@
332 def jobAborted(self):
333 """See `IBuildFarmJob`."""
334 self.build.buildstate = BuildStatus.NEEDSBUILD
335+
336+
337+class PackageBuildFarmJobDerived(BuildFarmJobDerived):
338+ """Override the base delegate.
339+
340+ Ensure that we use a build farm job specific to packages.
341+ """
342+ def _set_build_farm_job(self):
343+ self._build_farm_job = PackageBuildFarmJob(self.build)
344+
345+
346
347=== modified file 'lib/lp/buildmaster/tests/test_buildqueue.py'
348--- lib/lp/buildmaster/tests/test_buildqueue.py 2010-04-12 08:29:02 +0000
349+++ lib/lp/buildmaster/tests/test_buildqueue.py 2010-04-21 11:53:24 +0000
350@@ -8,7 +8,8 @@
351 from pytz import utc
352 from unittest import TestLoader
353
354-from zope.component import getUtility
355+from zope import component
356+from zope.component import getGlobalSiteManager, getUtility
357 from zope.interface.verify import verifyObject
358
359 from canonical.launchpad.webapp.interfaces import (
360@@ -17,10 +18,11 @@
361
362 from lp.buildmaster.interfaces.buildbase import BuildStatus
363 from lp.buildmaster.interfaces.builder import IBuilderSet
364-from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
365+from lp.buildmaster.interfaces.buildfarmjob import (
366+ BuildFarmJobType, IBuildFarmJob)
367 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
368 from lp.buildmaster.model.builder import specific_job_classes
369-from lp.buildmaster.model.buildfarmjob import BuildFarmJob
370+from lp.buildmaster.model.buildfarmjob import BuildFarmJobDerived
371 from lp.buildmaster.model.buildqueue import BuildQueue, get_builder_data
372 from lp.services.job.model.job import Job
373 from lp.soyuz.interfaces.archive import ArchivePurpose
374@@ -885,9 +887,7 @@
375
376 def test_OtherTypeClasses(self):
377 """Other job type classes are picked up as well."""
378- from zope import component
379- from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
380- class FakeBranchBuild(BuildFarmJob):
381+ class FakeBranchBuild(BuildFarmJobDerived):
382 pass
383
384 _build, bq = find_job(self, 'gedit')
385@@ -896,17 +896,24 @@
386 self.assertTrue(
387 specific_job_classes().get(BuildFarmJobType.BRANCHBUILD) is None)
388
389- # Pretend that our `FakeBranchBuild` class implements the
390- # `IBuildFarmJob` interface.
391- component.provideUtility(
392- FakeBranchBuild, IBuildFarmJob, 'BRANCHBUILD')
393-
394- # Now we should see the `FakeBranchBuild` class "registered" in the
395- # `specific_job_classes` dictionary under the 'BRANCHBUILD' key.
396- self.assertEqual(
397- specific_job_classes()[BuildFarmJobType.BRANCHBUILD],
398- FakeBranchBuild)
399-
400+ try:
401+ # Pretend that our `FakeBranchBuild` class implements the
402+ # `IBuildFarmJob` interface.
403+ component.provideUtility(
404+ FakeBranchBuild, IBuildFarmJob, 'BRANCHBUILD')
405+
406+ # Now we should see the `FakeBranchBuild` class "registered"
407+ # in the `specific_job_classes` dictionary under the
408+ # 'BRANCHBUILD' key.
409+ self.assertEqual(
410+ specific_job_classes()[BuildFarmJobType.BRANCHBUILD],
411+ FakeBranchBuild)
412+ finally:
413+ # Just de-register the utility so we don't affect other
414+ # tests.
415+ site_manager = getGlobalSiteManager()
416+ site_manager.unregisterUtility(
417+ FakeBranchBuild, IBuildFarmJob, 'BRANCHBUILD')
418
419 class TestPlatformData(TestCaseWithFactory):
420 """Tests covering the processor/virtualized properties."""
421
422=== modified file 'lib/lp/code/configure.zcml'
423--- lib/lp/code/configure.zcml 2010-04-20 06:42:29 +0000
424+++ lib/lp/code/configure.zcml 2010-04-21 11:53:24 +0000
425@@ -31,7 +31,7 @@
426
427 <class class="lp.code.model.codereviewvote.CodeReviewVoteReference">
428 <allow interface="lp.code.interfaces.codereviewvote.ICodeReviewVoteReferencePublic"/>
429- <require
430+ <require
431 permission="launchpad.Edit"
432 interface="lp.code.interfaces.codereviewvote.ICodeReviewVoteReferenceEdit"/>
433 </class>
434@@ -988,6 +988,7 @@
435 <class
436 class="lp.code.model.sourcepackagerecipebuild.SourcePackageRecipeBuildJob">
437 <allow interface="lp.code.interfaces.sourcepackagerecipebuild.ISourcePackageRecipeBuildJob"/>
438+ <allow interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobDerived"/>
439 </class>
440
441 <securedutility
442
443=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
444--- lib/lp/code/model/sourcepackagerecipebuild.py 2010-04-19 03:36:27 +0000
445+++ lib/lp/code/model/sourcepackagerecipebuild.py 2010-04-21 11:53:24 +0000
446@@ -27,7 +27,8 @@
447 from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
448 from lp.buildmaster.model.buildbase import BuildBase
449 from lp.buildmaster.model.buildqueue import BuildQueue
450-from lp.buildmaster.model.packagebuildfarmjob import PackageBuildFarmJob
451+from lp.buildmaster.model.packagebuildfarmjob import (
452+ PackageBuildFarmJobDerived)
453 from lp.code.interfaces.sourcepackagerecipebuild import (
454 ISourcePackageRecipeBuildJob, ISourcePackageRecipeBuildJobSource,
455 ISourcePackageRecipeBuild, ISourcePackageRecipeBuildSource)
456@@ -198,7 +199,7 @@
457 return
458
459
460-class SourcePackageRecipeBuildJob(PackageBuildFarmJob, Storm):
461+class SourcePackageRecipeBuildJob(PackageBuildFarmJobDerived, Storm):
462 classProvides(ISourcePackageRecipeBuildJobSource)
463 implements(ISourcePackageRecipeBuildJob)
464
465@@ -217,15 +218,15 @@
466 virtualized = True
467
468 def __init__(self, build, job):
469- super(SourcePackageRecipeBuildJob, self).__init__()
470 self.build = build
471 self.job = job
472+ super(SourcePackageRecipeBuildJob, self).__init__()
473
474 @classmethod
475 def new(cls, build, job):
476 """See `ISourcePackageRecipeBuildJobSource`."""
477 specific_job = cls(build, job)
478- store = IMasterStore(SourcePackageRecipeBuildJob)
479+ store = IMasterStore(cls)
480 store.add(specific_job)
481 return specific_job
482
483
484=== modified file 'lib/lp/soyuz/configure.zcml'
485--- lib/lp/soyuz/configure.zcml 2010-04-12 11:37:48 +0000
486+++ lib/lp/soyuz/configure.zcml 2010-04-21 11:53:24 +0000
487@@ -848,6 +848,8 @@
488 class="lp.soyuz.model.buildpackagejob.BuildPackageJob">
489 <allow
490 interface="lp.soyuz.interfaces.buildpackagejob.IBuildPackageJob"/>
491+ <allow
492+ interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobDerived"/>
493 </class>
494 <!--
495 The registration below is used to discover all build farm job classes
496
497=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
498--- lib/lp/soyuz/model/binarypackagebuild.py 2010-04-09 15:46:09 +0000
499+++ lib/lp/soyuz/model/binarypackagebuild.py 2010-04-21 11:53:24 +0000
500@@ -313,9 +313,7 @@
501 store = Store.of(self)
502 job = Job()
503 store.add(job)
504- specific_job = BuildPackageJob()
505- specific_job.build = self.id
506- specific_job.job = job.id
507+ specific_job = BuildPackageJob(build=self, job=job)
508 store.add(specific_job)
509 return specific_job
510
511
512=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
513--- lib/lp/soyuz/model/buildpackagejob.py 2010-04-12 11:37:48 +0000
514+++ lib/lp/soyuz/model/buildpackagejob.py 2010-04-21 11:53:24 +0000
515@@ -12,13 +12,14 @@
516
517 from storm.locals import Int, Reference, Storm
518
519+from zope.component import getUtility
520 from zope.interface import implements
521-from zope.component import getUtility
522
523 from canonical.database.sqlbase import sqlvalues
524
525 from lp.buildmaster.interfaces.buildbase import BuildStatus
526-from lp.buildmaster.model.packagebuildfarmjob import PackageBuildFarmJob
527+from lp.buildmaster.model.packagebuildfarmjob import (
528+ PackageBuildFarmJobDerived)
529 from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
530 from lp.registry.interfaces.pocket import PackagePublishingPocket
531 from lp.soyuz.interfaces.archive import ArchivePurpose
532@@ -27,7 +28,7 @@
533 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
534
535
536-class BuildPackageJob(PackageBuildFarmJob, Storm):
537+class BuildPackageJob(PackageBuildFarmJobDerived, Storm):
538 """See `IBuildPackageJob`."""
539 implements(IBuildPackageJob)
540
541@@ -40,6 +41,11 @@
542 build_id = Int(name='build', allow_none=False)
543 build = Reference(build_id, 'BinaryPackageBuild.id')
544
545+ def __init__(self, build, job):
546+ """ Setup the IBuildFarmJob delegation when new items are created."""
547+ self.build, self.job = build, job
548+ super(BuildPackageJob, self).__init__()
549+
550 def score(self):
551 """See `IBuildPackageJob`."""
552 score_pocketname = {
553@@ -166,7 +172,7 @@
554
555 @staticmethod
556 def addCandidateSelectionCriteria(processor, virtualized):
557- """See `IBuildFarmCandidateJobSelection`."""
558+ """See `IBuildFarmJob`."""
559 # Avoiding circular import.
560 from lp.buildmaster.model.builder import Builder
561
562@@ -236,7 +242,7 @@
563
564 @staticmethod
565 def postprocessCandidate(job, logger):
566- """See `IBuildFarmCandidateJobSelection`."""
567+ """See `IBuildFarmJob`."""
568 # Mark build records targeted to old source versions as SUPERSEDED
569 # and build records target to SECURITY pocket as FAILEDTOBUILD.
570 # Builds in those situation should not be built because they will
571
572=== modified file 'lib/lp/soyuz/tests/test_buildpackagejob.py'
573--- lib/lp/soyuz/tests/test_buildpackagejob.py 2010-04-12 08:29:02 +0000
574+++ lib/lp/soyuz/tests/test_buildpackagejob.py 2010-04-21 11:53:24 +0000
575@@ -13,7 +13,10 @@
576
577 from lp.buildmaster.interfaces.buildbase import BuildStatus
578 from lp.buildmaster.interfaces.builder import IBuilderSet
579+from lp.buildmaster.interfaces.buildfarmjob import (
580+ IBuildFarmJob, IBuildFarmJobDerived)
581 from lp.soyuz.interfaces.archive import ArchivePurpose
582+from lp.soyuz.interfaces.buildpackagejob import IBuildPackageJob
583 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
584 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
585 from lp.soyuz.model.processor import ProcessorFamilySet
586@@ -225,3 +228,13 @@
587 # Test that BuildPackageJob returns the title of the build.
588 build, bq = find_job(self, 'gcc', '386')
589 self.assertEqual(bq.specific_job.getTitle(), build.title)
590+
591+ def test_providesInterfaces(self):
592+ # Ensure that a BuildPackageJob generates an appropriate cookie.
593+ build, bq = find_job(self, 'gcc', '386')
594+ build_farm_job = bq.specific_job
595+ self.assertProvides(build_farm_job, IBuildPackageJob)
596+ self.assertProvides(build_farm_job, IBuildFarmJob)
597+ self.assertProvides(build_farm_job, IBuildFarmJobDerived)
598+
599+
600
601=== modified file 'lib/lp/translations/configure.zcml'
602--- lib/lp/translations/configure.zcml 2010-02-17 11:19:42 +0000
603+++ lib/lp/translations/configure.zcml 2010-04-21 11:53:24 +0000
604@@ -579,6 +579,8 @@
605 interface="lp.code.interfaces.branchjob.IBranchJob"/>
606 <allow
607 interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJob"/>
608+ <allow
609+ interface="lp.buildmaster.interfaces.buildfarmjob.IBuildFarmJobDerived"/>
610 </class>
611 <securedutility
612 component="lp.translations.model.translationtemplatesbuildjob.TranslationTemplatesBuildJob"
613
614=== modified file 'lib/lp/translations/model/translationtemplatesbuildjob.py'
615--- lib/lp/translations/model/translationtemplatesbuildjob.py 2010-03-18 12:25:36 +0000
616+++ lib/lp/translations/model/translationtemplatesbuildjob.py 2010-04-21 11:53:24 +0000
617@@ -18,10 +18,9 @@
618 from canonical.launchpad.webapp.interfaces import (
619 DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE, MASTER_FLAVOR)
620
621-from lp.buildmaster.interfaces.buildfarmjob import (
622- BuildFarmJobType, ISpecificBuildFarmJobClass)
623-from lp.buildmaster.model.buildfarmjob import BuildFarmJob
624+from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
625 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
626+from lp.buildmaster.model.buildfarmjob import BuildFarmJobDerived
627 from lp.buildmaster.model.buildqueue import BuildQueue
628 from lp.code.interfaces.branchjob import IRosettaUploadJobSource
629 from lp.buildmaster.interfaces.buildfarmbranchjob import IBuildFarmBranchJob
630@@ -31,17 +30,15 @@
631 from lp.translations.pottery.detect_intltool import is_intltool_structure
632
633
634-class TranslationTemplatesBuildJob(BranchJobDerived, BuildFarmJob):
635+class TranslationTemplatesBuildJob(BuildFarmJobDerived, BranchJobDerived):
636 """An `IBuildFarmJob` implementation that generates templates.
637
638 Implementation-wise, this is actually a `BranchJob`.
639 """
640 implements(IBuildFarmBranchJob)
641-
642 class_job_type = BranchJobType.TRANSLATION_TEMPLATES_BUILD
643
644- classProvides(
645- ISpecificBuildFarmJobClass, ITranslationTemplatesBuildJobSource)
646+ classProvides(ITranslationTemplatesBuildJobSource)
647
648 duration_estimate = timedelta(seconds=10)
649
650@@ -110,7 +107,6 @@
651 branch, BranchJobType.TRANSLATION_TEMPLATES_BUILD, metadata)
652 store.add(branch_job)
653 specific_job = TranslationTemplatesBuildJob(branch_job)
654-
655 duration_estimate = cls.duration_estimate
656 build_queue_entry = BuildQueue(
657 estimated_duration=duration_estimate,
658@@ -133,7 +129,10 @@
659
660 @classmethod
661 def getByJob(cls, job):
662- """See `ISpecificBuildFarmJobClass`."""
663+ """See `IBuildFarmJobDerived`.
664+
665+ Overridden here to search via a BranchJob, rather than a Job.
666+ """
667 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
668 branch_job = store.find(BranchJob, BranchJob.job == job).one()
669 if branch_job is None:
670
671=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuildjob.py'
672--- lib/lp/translations/tests/test_translationtemplatesbuildjob.py 2010-03-06 00:21:57 +0000
673+++ lib/lp/translations/tests/test_translationtemplatesbuildjob.py 2010-04-21 11:53:24 +0000
674@@ -19,7 +19,7 @@
675 from lp.testing import TestCaseWithFactory
676
677 from lp.buildmaster.interfaces.buildfarmjob import (
678- IBuildFarmJob, ISpecificBuildFarmJobClass)
679+ IBuildFarmJob, IBuildFarmJobDerived)
680 from lp.buildmaster.interfaces.buildfarmjobbehavior import (
681 IBuildFarmJobBehavior)
682 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
683@@ -54,14 +54,12 @@
684 self.specific_job = self.jobset.create(self.branch)
685
686 def test_new_TranslationTemplatesBuildJob(self):
687- # TranslationTemplateBuildJob implements IBuildFarmJob and
688- # IBranchJob.
689+ # TranslationTemplateBuildJob implements IBuildFarmJob,
690+ # IBuildFarmJobDerived, and IBranchJob.
691 verifyObject(IBranchJob, self.specific_job)
692+ verifyObject(IBuildFarmJobDerived, self.specific_job)
693 verifyObject(IBuildFarmJob, self.specific_job)
694
695- # The class also implements ISpecificBuildFarmJobClass.
696- verifyObject(ISpecificBuildFarmJobClass, TranslationTemplatesBuildJob)
697-
698 # Each of these jobs knows the branch it will operate on.
699 self.assertEqual(self.branch, self.specific_job.branch)
700
701@@ -91,7 +89,7 @@
702 self.assertNotEqual(self.specific_job.getName(), other_job.getName())
703
704 def test_getTitle(self):
705- other_job = self.jobset.create(self.branch)
706+ self.jobset.create(self.branch)
707 self.assertEqual(
708 '%s translation templates build' % self.branch.bzr_identity,
709 self.specific_job.getTitle())
710@@ -191,7 +189,7 @@
711 # branch, generatesTemplates returns False.
712 branch = self._makeTranslationBranch()
713 self.assertFalse(self.jobsource.generatesTemplates(branch))
714-
715+
716 def test_branch_not_used(self):
717 # We don't generate templates branches not attached to series.
718 branch = self._makeTranslationBranch(fake_pottery_compatible=True)