Merge lp:~al-maisan/launchpad/refactoring-479079 into lp:launchpad/db-devel

Proposed by Muharem Hrnjadovic
Status: Merged
Approved by: Julian Edwards
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~al-maisan/launchpad/refactoring-479079
Merge into: lp:launchpad/db-devel
Diff against target: 3133 lines (+994/-560)
35 files modified
database/sampledata/current-dev.sql (+36/-2)
database/sampledata/current.sql (+36/-2)
database/schema/comments.sql (+3/-4)
database/schema/patch-2207-11-0.sql (+87/-0)
database/schema/security.cfg (+8/-0)
lib/lp/buildmaster/buildergroup.py (+41/-31)
lib/lp/buildmaster/interfaces/buildfarmjob.py (+71/-0)
lib/lp/buildmaster/master.py (+4/-2)
lib/lp/buildmaster/model/buildfarmjob.py (+40/-0)
lib/lp/buildmaster/tests/queuebuilder.txt (+6/-5)
lib/lp/buildmaster/tests/test_manager.py (+14/-10)
lib/lp/registry/model/sourcepackage.py (+3/-1)
lib/lp/services/job/tests/test_job.py (+22/-5)
lib/lp/soyuz/browser/build.py (+2/-2)
lib/lp/soyuz/browser/builder.py (+3/-3)
lib/lp/soyuz/browser/tests/builder-views.txt (+9/-5)
lib/lp/soyuz/configure.zcml (+7/-1)
lib/lp/soyuz/doc/build-estimated-dispatch-time.txt (+6/-5)
lib/lp/soyuz/doc/buildd-dispatching.txt (+20/-35)
lib/lp/soyuz/doc/buildd-scoring.txt (+19/-11)
lib/lp/soyuz/doc/buildd-slavescanner.txt (+94/-78)
lib/lp/soyuz/doc/builder.txt (+3/-1)
lib/lp/soyuz/doc/buildqueue.txt (+16/-36)
lib/lp/soyuz/interfaces/build.py (+7/-0)
lib/lp/soyuz/interfaces/buildpackagejob.py (+34/-0)
lib/lp/soyuz/interfaces/buildqueue.py (+27/-44)
lib/lp/soyuz/model/build.py (+47/-8)
lib/lp/soyuz/model/builder.py (+51/-41)
lib/lp/soyuz/model/buildpackagejob.py (+168/-0)
lib/lp/soyuz/model/buildqueue.py (+72/-199)
lib/lp/soyuz/stories/soyuz/xx-build-record.txt (+5/-8)
lib/lp/soyuz/templates/build-index.pt (+2/-2)
lib/lp/soyuz/templates/builder-index.pt (+10/-3)
lib/lp/soyuz/templates/builds-list.pt (+2/-2)
lib/lp/soyuz/tests/test_builder.py (+19/-14)
To merge this branch: bzr merge lp:~al-maisan/launchpad/refactoring-479079
Reviewer Review Type Date Requested Status
Julian Edwards (community) code Approve
Review via email: mp+14829@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :

Hello there!

This is a follow-up branch to the schema (and sample data) changes required
for the Soyuz buildd generalisation. It refactors the code in the python
domain to cope with the schema changes.

For more details on the Soyuz buildd generalisation
please see: https://dev.launchpad.net/Soyuz/Specs/BuilddGeneralisation.

Pre-implementation call, assistance and intermediate reviews by Julian E.

Tests to run:

    bin/test -vv -t build

No pertinent lint errors or warnings.

This branch depends on the schema patch branch (lp:~al-maisan/launchpad/builddsc-478919). Just in case the diff includes the schema patch: the *real* diff is here: http://pastebin.ubuntu.com/317945/

Revision history for this message
Julian Edwards (julian-edwards) wrote :
Download full text (117.6 KiB)

Phew! What a change, thanks for staying late and getting this done Muharem.
There's a few things to fix; you're already aware of some that I mentioned on IRC. More inline!

> === modified file 'lib/lp/buildmaster/buildergroup.py'
> --- lib/lp/buildmaster/buildergroup.py 2009-08-16 12:38:12 +0000
> +++ lib/lp/buildmaster/buildergroup.py 2009-11-13 16:42:17 +0000
> @@ -130,8 +130,9 @@
> try:
> build = getUtility(IBuildSet).getByBuildID(int(build_id))
> queue_item = getUtility(IBuildQueueSet).get(int(queue_item_id))
> - # Also check it build and buildqueue are properly related.
> - if queue_item.build.id != build.id:
> + queued_build = getUtility(IBuildSet).getByQueueEntry(queue_item)
> + # Also check whether build and buildqueue are properly related.
> + if queued_build.id != build.id:
> raise BuildJobMismatch('Job build entry mismatch')
>
> except (SQLObjectNotFound, NotFoundError, BuildJobMismatch), reason:
> @@ -159,9 +160,10 @@
>
> Invoke getFileFromSlave method with 'buildlog' identifier.
> """
> + build = getUtility(IBuildSet).getByQueueEntry(queueItem)
> return queueItem.builder.transferSlaveFileToLibrarian(
> 'buildlog', queueItem.getLogFileName(),
> - queueItem.build.archive.private)
> + build.archive.private)
>
> def updateBuild(self, queueItem):
> """Verify the current build job status.
> @@ -199,7 +201,7 @@
> "Unknown status code (%s) returned from status() probe."
> % builder_status)
> queueItem.builder = None
> - queueItem.buildstart = None
> + queueItem.setDateStarted(None)
> self.commit()
> return
>
> @@ -261,17 +263,18 @@
>
> Store Buildlog, datebuilt, duration, dependencies.
> """
> - queueItem.build.buildlog = self.getLogFromSlave(queueItem)
> - queueItem.build.builder = queueItem.builder
> - queueItem.build.dependencies = dependencies
> + build = getUtility(IBuildSet).getByQueueEntry(queueItem)
> + build.buildlog = self.getLogFromSlave(queueItem)
> + build.builder = queueItem.builder
> + build.dependencies = dependencies
> # XXX cprov 20060615 bug=120584: Currently buildduration includes
> # the scanner latency, it should really be asking the slave for
> # the duration spent building locally.
> - queueItem.build.datebuilt = UTC_NOW
> + build.datebuilt = UTC_NOW
> # We need dynamic datetime.now() instance to be able to perform
> # the time operations for duration.
> RIGHT_NOW = datetime.datetime.now(pytz.timezone('UTC'))
> - queueItem.build.buildduration = RIGHT_NOW - queueItem.buildstart
> + build.buildduration = RIGHT_NOW - queueItem.job.date_started

Since you added buildqueue.setDateStarted, it makes sense to have a property
buildqueue.date_started so it's consistent.

> def buildStatus_OK(self, queueItem, librarian, buildid,
> @@ -287,7 +290,7 @@
> self.logger....

review: Needs Fixing (code)
Revision history for this message
Muharem Hrnjadovic (al-maisan) wrote :
Download full text (121.4 KiB)

Julian Edwards wrote:
> Review: Needs Fixing code
> Phew! What a change, thanks for staying late and getting this done
> Muharem. There's a few things to fix; you're already aware of some
> that I mentioned on IRC. More inline!

Hello Julian,

thanks for reviewing this monster branch :P
Please see my responses below as well as the enclosed incremental diff.

>> === modified file 'lib/lp/buildmaster/buildergroup.py'
>> --- lib/lp/buildmaster/buildergroup.py 2009-08-16 12:38:12 +0000
>> +++ lib/lp/buildmaster/buildergroup.py 2009-11-13 16:42:17 +0000
>> @@ -130,8 +130,9 @@
>> try:
>> build = getUtility(IBuildSet).getByBuildID(int(build_id))
>> queue_item = getUtility(IBuildQueueSet).get(int(queue_item_id))
>> - # Also check it build and buildqueue are properly related.
>> - if queue_item.build.id != build.id:
>> + queued_build = getUtility(IBuildSet).getByQueueEntry(queue_item)
>> + # Also check whether build and buildqueue are properly related.
>> + if queued_build.id != build.id:
>> raise BuildJobMismatch('Job build entry mismatch')
>>
>> except (SQLObjectNotFound, NotFoundError, BuildJobMismatch), reason:
>> @@ -159,9 +160,10 @@
>>
>> Invoke getFileFromSlave method with 'buildlog' identifier.
>> """
>> + build = getUtility(IBuildSet).getByQueueEntry(queueItem)
>> return queueItem.builder.transferSlaveFileToLibrarian(
>> 'buildlog', queueItem.getLogFileName(),
>> - queueItem.build.archive.private)
>> + build.archive.private)
>>
>> def updateBuild(self, queueItem):
>> """Verify the current build job status.
>> @@ -199,7 +201,7 @@
>> "Unknown status code (%s) returned from status() probe."
>> % builder_status)
>> queueItem.builder = None
>> - queueItem.buildstart = None
>> + queueItem.setDateStarted(None)
>> self.commit()
>> return
>>
>> @@ -261,17 +263,18 @@
>>
>> Store Buildlog, datebuilt, duration, dependencies.
>> """
>> - queueItem.build.buildlog = self.getLogFromSlave(queueItem)
>> - queueItem.build.builder = queueItem.builder
>> - queueItem.build.dependencies = dependencies
>> + build = getUtility(IBuildSet).getByQueueEntry(queueItem)
>> + build.buildlog = self.getLogFromSlave(queueItem)
>> + build.builder = queueItem.builder
>> + build.dependencies = dependencies
>> # XXX cprov 20060615 bug=120584: Currently buildduration includes
>> # the scanner latency, it should really be asking the slave for
>> # the duration spent building locally.
>> - queueItem.build.datebuilt = UTC_NOW
>> + build.datebuilt = UTC_NOW
>> # We need dynamic datetime.now() instance to be able to perform
>> # the time operations for duration.
>> RIGHT_NOW = datetime.datetime.now(pytz.timezone('UTC'))
>> - queueItem.build.buildduration = RIGHT_NOW - queueItem.buildstart
>> + build.buildduration = RIGHT_NOW - queueItem.jo...

1=== modified file 'lib/lp/buildmaster/buildergroup.py'
2--- lib/lp/buildmaster/buildergroup.py 2009-11-13 09:00:59 +0000
3+++ lib/lp/buildmaster/buildergroup.py 2009-11-13 19:12:49 +0000
4@@ -274,7 +274,7 @@
5 # We need dynamic datetime.now() instance to be able to perform
6 # the time operations for duration.
7 RIGHT_NOW = datetime.datetime.now(pytz.timezone('UTC'))
8- build.buildduration = RIGHT_NOW - queueItem.job.date_started
9+ build.buildduration = RIGHT_NOW - queueItem.date_started
10
11
12 def buildStatus_OK(self, queueItem, librarian, buildid,
13
14=== added directory 'lib/lp/buildmaster/interfaces'
15=== renamed file 'lib/lp/soyuz/interfaces/soyuzjob.py' => 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
16--- lib/lp/soyuz/interfaces/soyuzjob.py 2009-11-12 15:53:23 +0000
17+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2009-11-13 19:42:13 +0000
18@@ -8,15 +8,15 @@
19 __metaclass__ = type
20
21 __all__ = [
22- 'ISoyuzJob',
23- 'SoyuzJobType',
24+ 'IBuildfarmJob',
25+ 'BuildfarmJobType',
26 ]
27
28 from zope.interface import Interface
29 from lazr.enum import DBEnumeratedType, DBItem
30
31
32-class SoyuzJobType(DBEnumeratedType):
33+class BuildfarmJobType(DBEnumeratedType):
34 """Soyuz build farm job type.
35
36 An enumeration with the types of jobs that may be run on the Soyuz build
37@@ -48,7 +48,7 @@
38 """)
39
40
41-class ISoyuzJob(Interface):
42+class IBuildfarmJob(Interface):
43 """Operations that Soyuz build farm jobs must implement."""
44
45 def score():
46
47=== added directory 'lib/lp/buildmaster/model'
48=== renamed file 'lib/lp/soyuz/model/soyuzjob.py' => 'lib/lp/buildmaster/model/buildfarmjob.py'
49--- lib/lp/soyuz/model/soyuzjob.py 2009-11-12 15:53:23 +0000
50+++ lib/lp/buildmaster/model/buildfarmjob.py 2009-11-13 19:48:03 +0000
51@@ -2,39 +2,39 @@
52 # GNU Affero General Public License version 3 (see the file LICENSE).
53
54 __metaclass__ = type
55-__all__ = ['SoyuzJob']
56+__all__ = ['BuildfarmJob']
57
58
59 from zope.interface import implements
60
61-from lp.soyuz.interfaces.soyuzjob import ISoyuzJob
62-
63-
64-class SoyuzJob:
65- """Mix-in class for `ISoyuzJob` implementations."""
66- implements(ISoyuzJob)
67+from lp.buildmaster.interfaces.buildfarmjob import IBuildfarmJob
68+
69+
70+class BuildfarmJob:
71+ """Mix-in class for `IBuildfarmJob` implementations."""
72+ implements(IBuildfarmJob)
73
74 def score(self):
75- """See `ISoyuzJob`."""
76+ """See `IBuildfarmJob`."""
77 raise NotImplementedError
78
79 def getLogFileName(self):
80- """See `ISoyuzJob`."""
81+ """See `IBuildfarmJob`."""
82 raise NotImplementedError
83
84 def getName(self):
85- """See `ISoyuzJob`."""
86+ """See `IBuildfarmJob`."""
87 raise NotImplementedError
88
89 def jobStarted(self):
90- """See `ISoyuzJob`."""
91+ """See `IBuildfarmJob`."""
92 pass
93
94 def jobReset(self):
95- """See `ISoyuzJob`."""
96+ """See `IBuildfarmJob`."""
97 pass
98
99 def jobAborted(self):
100- """See `ISoyuzJob`."""
101+ """See `IBuildfarmJob`."""
102 pass
103
104
105=== modified file 'lib/lp/buildmaster/tests/test_manager.py'
106--- lib/lp/buildmaster/tests/test_manager.py 2009-11-12 13:54:36 +0000
107+++ lib/lp/buildmaster/tests/test_manager.py 2009-11-13 19:11:59 +0000
108@@ -494,7 +494,7 @@
109
110 self.assertTrue(job is not None)
111 self.assertEqual(job.builder, builder)
112- self.assertTrue(job.job.date_started is not None)
113+ self.assertTrue(job.date_started is not None)
114 build = getUtility(IBuildSet).getByQueueEntry(job)
115 self.assertEqual(build.buildstate, BuildStatus.BUILDING)
116 self.assertEqual(job.logtail, logtail)
117@@ -618,7 +618,7 @@
118
119 job = getUtility(IBuildQueueSet).get(job.id)
120 self.assertTrue(job.builder is None)
121- self.assertTrue(job.job.date_started is None)
122+ self.assertTrue(job.date_started is None)
123 build = getUtility(IBuildSet).getByQueueEntry(job)
124 self.assertEqual(build.buildstate, BuildStatus.NEEDSBUILD)
125
126@@ -710,7 +710,7 @@
127
128 self.assertEqual('BUILDING', build.buildstate.name)
129 self.assertNotEqual(None, job.builder)
130- self.assertNotEqual(None, job.job.date_started)
131+ self.assertNotEqual(None, job.date_started)
132 self.assertNotEqual(None, job.logtail)
133
134 transaction.commit()
135@@ -723,7 +723,7 @@
136 build = getUtility(IBuildSet).getByQueueEntry(job)
137 self.assertEqual('NEEDSBUILD', build.buildstate.name)
138 self.assertEqual(None, job.builder)
139- self.assertEqual(None, job.job.date_started)
140+ self.assertEqual(None, job.date_started)
141 self.assertEqual(None, job.logtail)
142
143 def testResetDispatchResult(self):
144
145=== modified file 'lib/lp/soyuz/browser/builder.py'
146--- lib/lp/soyuz/browser/builder.py 2009-11-12 10:38:46 +0000
147+++ lib/lp/soyuz/browser/builder.py 2009-11-13 19:13:14 +0000
148@@ -237,11 +237,11 @@
149 def current_build_duration(self):
150 """Return the delta representing the duration of the current job."""
151 if (self.context.currentjob is None or
152- self.context.currentjob.job.date_started is None):
153+ self.context.currentjob.date_started is None):
154 return None
155 else:
156 UTC = pytz.timezone('UTC')
157- date_started = self.context.currentjob.job.date_started
158+ date_started = self.context.currentjob.date_started
159 return datetime.datetime.now(UTC) - date_started
160
161 @property
162
163=== modified file 'lib/lp/soyuz/browser/tests/builder-views.txt'
164--- lib/lp/soyuz/browser/tests/builder-views.txt 2009-11-12 10:38:46 +0000
165+++ lib/lp/soyuz/browser/tests/builder-views.txt 2009-11-13 19:22:15 +0000
166@@ -215,7 +215,7 @@
167
168 >>> import datetime
169 >>> import pytz
170- >>> private_job.job.date_started = (
171+ >>> private_job.setDateStarted(
172 ... datetime.datetime.now(pytz.UTC) - datetime.timedelta(10))
173 >>> print admin_view.current_build_duration
174 10 days...
175
176=== modified file 'lib/lp/soyuz/configure.zcml'
177--- lib/lp/soyuz/configure.zcml 2009-11-13 16:37:05 +0000
178+++ lib/lp/soyuz/configure.zcml 2009-11-13 19:56:58 +0000
179@@ -560,7 +560,7 @@
180
181 <require
182 permission="zope.Public"
183- set_attributes="lastscore builder logtail"/>
184+ set_attributes="lastscore builder logtail date_started"/>
185 </class>
186
187 <!-- BuildQueueSet -->
188
189=== modified file 'lib/lp/soyuz/doc/build-estimated-dispatch-time.txt'
190--- lib/lp/soyuz/doc/build-estimated-dispatch-time.txt 2009-11-13 07:04:53 +0000
191+++ lib/lp/soyuz/doc/build-estimated-dispatch-time.txt 2009-11-13 19:14:25 +0000
192@@ -76,9 +76,9 @@
193
194 >>> from zope.security.proxy import removeSecurityProxy
195 >>> cur_bqueue.lastscore = 1111
196- >>> removeSecurityProxy(cur_bqueue.job).date_started = (
197+ >>> cur_bqueue.setDateStarted(
198 ... datetime(2008, 4, 1, 10, 45, 39, tzinfo=UTC))
199- >>> print cur_bqueue.job.date_started
200+ >>> print cur_bqueue.date_started
201 2008-04-01 10:45:39+00:00
202
203 Please note that the "estimated build duration" is an internal property
204
205=== modified file 'lib/lp/soyuz/doc/buildd-dispatching.txt'
206--- lib/lp/soyuz/doc/buildd-dispatching.txt 2009-11-13 09:14:30 +0000
207+++ lib/lp/soyuz/doc/buildd-dispatching.txt 2009-11-13 19:14:44 +0000
208@@ -140,7 +140,7 @@
209 'NEEDSBUILD'
210 >>> job.builder is None
211 True
212- >>> job.job.date_started is None
213+ >>> job.date_started is None
214 True
215 >>> build.is_virtualized
216 False
217@@ -204,7 +204,7 @@
218 3
219 >>> ppa_job.builder == None
220 True
221- >>> ppa_job.job.date_started == None
222+ >>> ppa_job.date_started == None
223 True
224
225 The build job's archive requires virtualized builds.
226@@ -317,7 +317,7 @@
227 4
228 >>> print sec_job.builder
229 None
230- >>> print sec_job.job.date_started
231+ >>> print sec_job.date_started
232 None
233 >>> sec_build.is_virtualized
234 False
235
236=== modified file 'lib/lp/soyuz/doc/buildd-slavescanner.txt'
237--- lib/lp/soyuz/doc/buildd-slavescanner.txt 2009-11-13 16:37:05 +0000
238+++ lib/lp/soyuz/doc/buildd-slavescanner.txt 2009-11-13 19:15:23 +0000
239@@ -593,7 +593,7 @@
240
241 >>> bqItem11.builder is None
242 True
243- >>> bqItem11.job.date_started is None
244+ >>> bqItem11.date_started is None
245 True
246 >>> bqItem11.lastscore
247 0
248@@ -962,7 +962,7 @@
249 >>> a_builder.dispatchBuildCandidate(candidate)
250 ensurepresent called, url=...
251 ensurepresent called,
252- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
253+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
254 OkSlave BUILDING
255 Archives:
256 deb http://ftpmaster.internal/ubuntu hoary main
257@@ -990,7 +990,7 @@
258 >>> a_builder.dispatchBuildCandidate(candidate)
259 ensurepresent called, url=...
260 ensurepresent called,
261- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
262+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
263 OkSlave BUILDING
264 Archives:
265 deb http://ftpmaster.internal/ubuntu hoary main
266@@ -1042,7 +1042,7 @@
267 >>> a_builder.dispatchBuildCandidate(candidate)
268 ensurepresent called, url=...
269 ensurepresent called,
270- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
271+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
272 OkSlave BUILDING
273 Archives:
274 deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
275@@ -1134,7 +1134,7 @@
276 >>> a_builder.dispatchBuildCandidate(candidate)
277 ensurepresent called, url=...
278 ensurepresent called,
279- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
280+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
281 OkSlave BUILDING
282 Archives:
283 deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
284@@ -1191,7 +1191,7 @@
285 >>> a_builder.dispatchBuildCandidate(candidate)
286 ensurepresent called, url=...
287 ensurepresent called,
288- url=http://private-ppa.launchpad.dev/cprov/ppa/ubuntu/pool/main/m/mozilla-firefox/firefox-0.9.2.orig.tar.gz
289+ url=http://private-ppa.launchpad.dev/cprov/ppa/ubuntu/pool/main/m/mozilla-firefox/firefox_0.9.2.orig.tar.gz
290 URL authorisation with buildd/secret
291 OkSlave BUILDING
292 Archives:
293@@ -1311,7 +1311,7 @@
294 >>> a_builder.dispatchBuildCandidate(candidate)
295 ensurepresent called, url=...
296 ensurepresent called,
297- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
298+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
299 OkSlave BUILDING
300 Archives:
301 deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
302@@ -1340,7 +1340,7 @@
303 >>> a_builder.dispatchBuildCandidate(candidate)
304 ensurepresent called, url=...
305 ensurepresent called,
306- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
307+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
308 OkSlave BUILDING
309 Archives:
310 deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
311@@ -1410,7 +1410,7 @@
312 >>> a_builder.dispatchBuildCandidate(bqItem3)
313 ensurepresent called, url=...
314 ensurepresent called,
315- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
316+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
317 OkSlave BUILDING
318 Archives:
319 deb http://ftpmaster.internal/ubuntu hoary main
320@@ -1432,7 +1432,7 @@
321 >>> a_builder.dispatchBuildCandidate(bqItem3)
322 ensurepresent called, url=...
323 ensurepresent called,
324- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
325+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
326 OkSlave BUILDING
327 Archives:
328 deb http://ftpmaster.internal/ubuntu hoary main
329@@ -1455,7 +1455,7 @@
330 >>> a_builder.dispatchBuildCandidate(bqItem3)
331 ensurepresent called, url=...
332 ensurepresent called,
333- url=http://localhost:58000/3/firefox-0.9.2.orig.tar.gz
334+ url=http://localhost:58000/3/firefox_0.9.2.orig.tar.gz
335 OkSlave BUILDING
336 Archives:
337 deb http://ftpmaster.internal/ubuntu hoary main restricted universe multiverse
338
339=== modified file 'lib/lp/soyuz/doc/buildqueue.txt'
340--- lib/lp/soyuz/doc/buildqueue.txt 2009-11-12 13:09:37 +0000
341+++ lib/lp/soyuz/doc/buildqueue.txt 2009-11-13 19:15:43 +0000
342@@ -45,7 +45,7 @@
343 >>> bq.job.date_created
344 datetime.datetime(2005, 6, 15, 9, 14, 12, 820778, tzinfo=<UTC>)
345
346- >>> bq.job.date_started
347+ >>> bq.date_started
348 datetime.datetime(2005, 6, 15, 9, 20, 12, 820778, tzinfo=<UTC>)
349
350 Check Builder foreign key, which indicated which builder 'is processing'
351@@ -110,7 +110,7 @@
352
353 >>> print job.builder.name
354 bob
355- >>> job.job.date_started is not None
356+ >>> job.date_started is not None
357 True
358 >>> print job.logtail
359 Dummy sampledata entry, not processing
360@@ -130,7 +130,7 @@
361
362 >>> print job.builder
363 None
364- >>> print job.job.date_started
365+ >>> print job.date_started
366 None
367 >>> print job.logtail
368 None
369@@ -149,7 +149,7 @@
370
371 >>> print job.builder.name
372 bob
373- >>> job.job.date_started is not None
374+ >>> job.date_started is not None
375 True
376 >>> print build.buildstate.name
377 BUILDING
378
379=== modified file 'lib/lp/soyuz/interfaces/buildpackagejob.py'
380--- lib/lp/soyuz/interfaces/buildpackagejob.py 2009-11-13 10:26:22 +0000
381+++ lib/lp/soyuz/interfaces/buildpackagejob.py 2009-11-13 19:47:45 +0000
382@@ -15,20 +15,20 @@
383
384 from canonical.launchpad import _
385 from lazr.restful.fields import Reference
386+from lp.buildmaster.interfaces.buildfarmjob import IBuildfarmJob
387 from lp.services.job.interfaces.job import IJob
388 from lp.soyuz.interfaces.build import IBuild
389-from lp.soyuz.interfaces.soyuzjob import ISoyuzJob
390-
391-
392-class IBuildPackageJob(ISoyuzJob):
393+
394+
395+class IBuildPackageJob(IBuildfarmJob):
396 """A read-only interface for build package jobs."""
397 id = Int(title=_('ID'), required=True, readonly=True)
398
399 job = Reference(
400- IJob, title=_("General build job data"), required=True, readonly=True,
401- description=_("General data about this build job."))
402+ IJob, title=_("Job"), required=True, readonly=True,
403+ description=_("Data common to all job types."))
404
405 build = Reference(
406- IBuild, title=_("Associated build record"),
407+ IBuild, title=_("Build"),
408 required=True,readonly=True,
409- description=_("The build record associated with this job."))
410+ description=_("Build record associated with this job."))
411
412=== modified file 'lib/lp/soyuz/interfaces/buildqueue.py'
413--- lib/lp/soyuz/interfaces/buildqueue.py 2009-11-13 08:25:22 +0000
414+++ lib/lp/soyuz/interfaces/buildqueue.py 2009-11-13 19:53:36 +0000
415@@ -13,11 +13,14 @@
416 ]
417
418 from zope.interface import Interface, Attribute
419-from zope.schema import Choice, Object
420+from zope.schema import Choice, Datetime
421+
422+from lazr.restful.fields import Reference
423
424 from canonical.launchpad import _
425+from lp.buildmaster.interfaces.buildfarmjob import (
426+ IBuildfarmJob, BuildfarmJobType)
427 from lp.services.job.interfaces.job import IJob
428-from lp.soyuz.interfaces.soyuzjob import SoyuzJobType
429
430
431 class IBuildQueue(Interface):
432@@ -40,12 +43,12 @@
433 lastscore = Attribute("Last score to be computed for this job")
434 manual = Attribute("Whether or not the job was manually scored")
435
436- job = Object(
437- title=_("Generic job data"), schema=IJob, required=True,
438- description=_("The generic data (time stamps etc.) about this job."))
439+ job = Reference(
440+ IJob, title=_("Job"), required=True, readonly=True,
441+ description=_("Data common to all job types."))
442
443 job_type = Choice(
444- title=_('Job type'), required=True, vocabulary=SoyuzJobType,
445+ title=_('Job type'), required=True, vocabulary=BuildfarmJobType,
446 description=_("The type of this job."))
447
448 def manualScore(value):
449@@ -91,12 +94,17 @@
450 Clean the builder for another jobs.
451 """
452
453- specific_job = Attribute(
454- "Object with data and behaviour specific to the job type at hand.")
455+ specific_job = Reference(
456+ IBuildfarmJob, title=_("Job"),
457+ description=_("Data and operations common to all build farm jobs."))
458
459 def setDateStarted(timestamp):
460 """Sets the date started property to the given value."""
461
462+ date_started = Datetime(
463+ title=_('Start time'),
464+ description=_('Time when the job started.'))
465+
466
467 class IBuildQueueSet(Interface):
468 """Launchpad Auto Build queue set handler and auxiliary methods."""
469
470=== modified file 'lib/lp/soyuz/model/build.py'
471--- lib/lp/soyuz/model/build.py 2009-11-13 10:25:11 +0000
472+++ lib/lp/soyuz/model/build.py 2009-11-13 19:58:20 +0000
473@@ -45,6 +45,7 @@
474 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
475 from canonical.launchpad.webapp.tales import DurationFormatterAPI
476 from lp.archivepublisher.utils import get_ppa_reference
477+from lp.buildmaster.interfaces.buildfarmjob import BuildfarmJobType
478 from lp.registry.interfaces.pocket import PackagePublishingPocket
479 from lp.services.job.model.job import Job
480 from lp.soyuz.adapters.archivedependencies import get_components_for_building
481@@ -53,7 +54,6 @@
482 BuildStatus, BuildSetStatus, CannotBeRescored, IBuild, IBuildSet)
483 from lp.soyuz.interfaces.builder import IBuilderSet
484 from lp.soyuz.interfaces.publishing import active_publishing_status
485-from lp.soyuz.interfaces.soyuzjob import SoyuzJobType
486 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
487 from lp.soyuz.model.builder import Builder
488 from lp.soyuz.model.buildpackagejob import BuildPackageJob
489@@ -630,7 +630,7 @@
490 store.add(specific_job)
491 queue_entry = BuildQueue()
492 queue_entry.job = job.id
493- queue_entry.job_type = SoyuzJobType.PACKAGEBUILD
494+ queue_entry.job_type = BuildfarmJobType.PACKAGEBUILD
495 store.add(queue_entry)
496 return queue_entry
497
498
499=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
500--- lib/lp/soyuz/model/buildpackagejob.py 2009-11-12 15:53:23 +0000
501+++ lib/lp/soyuz/model/buildpackagejob.py 2009-11-13 20:04:38 +0000
502@@ -14,6 +14,7 @@
503
504 from canonical.database.constants import UTC_NOW
505 from canonical.launchpad.interfaces import SourcePackageUrgency
506+from lp.buildmaster.interfaces.buildfarmjob import IBuildfarmJob
507 from lp.registry.interfaces.pocket import PackagePublishingPocket
508 from lp.soyuz.interfaces.archive import ArchivePurpose
509 from lp.soyuz.interfaces.build import BuildStatus
510@@ -22,7 +23,7 @@
511
512 class BuildPackageJob(Storm):
513 """See `IBuildPackageJob`."""
514- implements(IBuildPackageJob)
515+ implements(IBuildfarmJob, IBuildPackageJob)
516 __storm_table__ = 'buildpackagejob'
517 id = Int(primary=True)
518
519@@ -33,7 +34,7 @@
520 build = Reference(build_id, 'Build.id')
521
522 def score(self):
523- """See `ISoyuzJob`."""
524+ """See `IBuildPackageJob`."""
525 score_pocketname = {
526 PackagePublishingPocket.BACKPORTS: 0,
527 PackagePublishingPocket.RELEASE: 1500,
528@@ -120,7 +121,7 @@
529 return score
530
531 def getLogFileName(self):
532- """See `ISoyuzJob`."""
533+ """See `IBuildPackageJob`."""
534 sourcename = self.build.sourcepackagerelease.name
535 version = self.build.sourcepackagerelease.version
536 # we rely on previous storage of current buildstate
537@@ -143,22 +144,22 @@
538 ))
539
540 def getName(self):
541- """See `ISoyuzJob`."""
542+ """See `IBuildPackageJob`."""
543 return self.build.sourcepackagerelease.name
544
545 def jobStarted(self):
546- """See `ISoyuzJob`."""
547+ """See `IBuildPackageJob`."""
548 self.build.buildstate = BuildStatus.BUILDING
549 # The build started, set the start time if not set already.
550 if self.build.date_first_dispatched is None:
551 self.build.date_first_dispatched = UTC_NOW
552
553 def jobReset(self):
554- """See `ISoyuzJob`."""
555+ """See `IBuildPackageJob`."""
556 self.build.buildstate = BuildStatus.NEEDSBUILD
557
558 def jobAborted(self):
559- """See `ISoyuzJob`."""
560+ """See `IBuildPackageJob`."""
561 # XXX, al-maisan, Thu, 12 Nov 2009 16:38:52 +0100
562 # The setting below was "inherited" from the previous code. We
563 # need to investigate whether and why this is really needed and
564
565=== modified file 'lib/lp/soyuz/model/buildqueue.py'
566--- lib/lp/soyuz/model/buildqueue.py 2009-11-13 08:49:14 +0000
567+++ lib/lp/soyuz/model/buildqueue.py 2009-11-13 20:00:40 +0000
568@@ -23,11 +23,11 @@
569 from canonical.database.enumcol import EnumCol
570 from canonical.database.sqlbase import SQLBase, sqlvalues
571 from canonical.launchpad.webapp.interfaces import NotFoundError
572+from lp.buildmaster.interfaces.buildfarmjob import BuildfarmJobType
573 from lp.services.job.interfaces.job import JobStatus
574 from lp.services.job.model.job import Job
575 from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
576 from lp.soyuz.interfaces.buildqueue import IBuildQueue, IBuildQueueSet
577-from lp.soyuz.interfaces.soyuzjob import SoyuzJobType
578 from lp.soyuz.model.buildpackagejob import BuildPackageJob
579 from canonical.launchpad.webapp.interfaces import (
580 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
581@@ -40,8 +40,8 @@
582
583 job = ForeignKey(dbName='job', foreignKey='Job', notNull=True)
584 job_type = EnumCol(
585- enum=SoyuzJobType, notNull=True, default=SoyuzJobType.PACKAGEBUILD,
586- dbName='job_type')
587+ enum=BuildfarmJobType, notNull=True,
588+ default=BuildfarmJobType.PACKAGEBUILD, dbName='job_type')
589 builder = ForeignKey(dbName='builder', foreignKey='Builder', default=None)
590 logtail = StringCol(dbName='logtail', default=None)
591 lastscore = IntCol(dbName='lastscore', default=0)
592@@ -55,6 +55,11 @@
593 BuildPackageJob, BuildPackageJob.job == self.job)
594 return result_set.one()
595
596+ @property
597+ def date_started(self):
598+ """See `IBuildQueue`."""
599+ return self.job.date_started
600+
601 def manualScore(self, value):
602 """See `IBuildQueue`."""
603 self.lastscore = value
604@@ -71,14 +76,14 @@
605 "%s (%d) MANUALLY RESCORED" % (name, self.lastscore))
606 return
607
608- # Allow the `ISoyuzJob` instance with the data/logic specific to the
609- # job at hand to calculate the score as appropriate.
610+ # Allow the `IBuildfarmJob` instance with the data/logic specific to
611+ # the job at hand to calculate the score as appropriate.
612 self.lastscore = self.specific_job.score()
613
614 def getLogFileName(self):
615 """See `IBuildQueue`."""
616- # Allow the `ISoyuzJob` instance with the data/logic specific to the
617- # job at hand to calculate the log file name as appropriate.
618+ # Allow the `IBuildfarmJob` instance with the data/logic specific to
619+ # the job at hand to calculate the log file name as appropriate.
620 return self.specific_job.getLogFileName()
621
622 def markAsBuilding(self, builder):
623@@ -189,7 +194,7 @@
624 BuildPackageJob.build = build.id AND
625 BuildQueue.builder IS NULL
626 """ % sqlvalues(
627- arch_ids, BuildStatus.NEEDSBUILD, SoyuzJobType.PACKAGEBUILD)
628+ arch_ids, BuildStatus.NEEDSBUILD, BuildfarmJobType.PACKAGEBUILD)
629
630 candidates = BuildQueue.select(
631 query, clauseTables=['Build', 'BuildPackageJob'],
Revision history for this message
Julian Edwards (julian-edwards) wrote :

Nice work Muharem. Just a couple of things as I mentioned on IRC:
 - you forgot to bzr add the __init__.py files in the new modules
 - s/Buildfarm/BuildFarm/

And then r=me! Land away on db-devel.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/sampledata/current-dev.sql'
2--- database/sampledata/current-dev.sql 2009-11-09 13:01:13 +0000
3+++ database/sampledata/current-dev.sql 2009-11-13 20:43:23 +0000
4@@ -745,6 +745,15 @@
5
6
7
8+
9+
10+
11+
12+
13+
14+
15+
16+
17 ALTER TABLE account DISABLE TRIGGER ALL;
18
19 INSERT INTO account (id, date_created, creation_rationale, status, date_status_set, displayname, openid_identifier, status_comment, old_openid_identifier) VALUES (1, '2005-06-06 08:59:51.591618', 8, 20, '2005-06-06 08:59:51.591618', 'Mark Shuttleworth', 'mark_oid', NULL, '123/mark');
20@@ -1695,10 +1704,19 @@
21 ALTER TABLE builder ENABLE TRIGGER ALL;
22
23
24+ALTER TABLE buildpackagejob DISABLE TRIGGER ALL;
25+
26+INSERT INTO buildpackagejob (id, job, build) VALUES (1, 1, 8);
27+INSERT INTO buildpackagejob (id, job, build) VALUES (2, 2, 11);
28+
29+
30+ALTER TABLE buildpackagejob ENABLE TRIGGER ALL;
31+
32+
33 ALTER TABLE buildqueue DISABLE TRIGGER ALL;
34
35-INSERT INTO buildqueue (id, build, builder, logtail, created, buildstart, lastscore, manual) VALUES (1, 8, 1, 'Dummy sampledata entry, not processing', '2005-06-15 09:14:12.820778', '2005-06-15 09:20:12.820778', 1, false);
36-INSERT INTO buildqueue (id, build, builder, logtail, created, buildstart, lastscore, manual) VALUES (2, 11, NULL, NULL, '2005-06-15 10:14:12.820778', NULL, 10, false);
37+INSERT INTO buildqueue (id, builder, logtail, lastscore, manual, job, job_type) VALUES (1, 1, 'Dummy sampledata entry, not processing', 1, false, 1, 1);
38+INSERT INTO buildqueue (id, builder, logtail, lastscore, manual, job, job_type) VALUES (2, NULL, NULL, 10, false, 2, 1);
39
40
41 ALTER TABLE buildqueue ENABLE TRIGGER ALL;
42@@ -2781,6 +2799,8 @@
43
44 ALTER TABLE job DISABLE TRIGGER ALL;
45
46+INSERT INTO job (id, requester, reason, status, progress, last_report_seen, next_report_due, attempt_count, max_retries, log, scheduled_start, lease_expires, date_created, date_started, date_finished) VALUES (1, NULL, NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, '2005-06-15 09:14:12.820778', '2005-06-15 09:20:12.820778', NULL);
47+INSERT INTO job (id, requester, reason, status, progress, last_report_seen, next_report_due, attempt_count, max_retries, log, scheduled_start, lease_expires, date_created, date_started, date_finished) VALUES (2, NULL, NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, '2005-06-15 10:14:12.820778', NULL, NULL);
48
49
50 ALTER TABLE job ENABLE TRIGGER ALL;
51@@ -4615,6 +4635,13 @@
52 ALTER TABLE packageset ENABLE TRIGGER ALL;
53
54
55+ALTER TABLE packagesetgroup DISABLE TRIGGER ALL;
56+
57+
58+
59+ALTER TABLE packagesetgroup ENABLE TRIGGER ALL;
60+
61+
62 ALTER TABLE packagesetinclusion DISABLE TRIGGER ALL;
63
64
65@@ -8569,6 +8596,13 @@
66 ALTER TABLE signedcodeofconduct ENABLE TRIGGER ALL;
67
68
69+ALTER TABLE sourcepackageformatselection DISABLE TRIGGER ALL;
70+
71+
72+
73+ALTER TABLE sourcepackageformatselection ENABLE TRIGGER ALL;
74+
75+
76 ALTER TABLE sourcepackagename DISABLE TRIGGER ALL;
77
78 INSERT INTO sourcepackagename (id, name) VALUES (1, 'mozilla-firefox');
79
80=== modified file 'database/sampledata/current.sql'
81--- database/sampledata/current.sql 2009-11-05 10:51:36 +0000
82+++ database/sampledata/current.sql 2009-11-13 20:43:23 +0000
83@@ -745,6 +745,15 @@
84
85
86
87+
88+
89+
90+
91+
92+
93+
94+
95+
96 ALTER TABLE account DISABLE TRIGGER ALL;
97
98 INSERT INTO account (id, date_created, creation_rationale, status, date_status_set, displayname, openid_identifier, status_comment, old_openid_identifier) VALUES (11, '2005-06-06 08:59:51.591618', 8, 20, '2005-06-06 08:59:51.591618', 'Mark Shuttleworth', 'mark_oid', NULL, '123/mark');
99@@ -1677,10 +1686,19 @@
100 ALTER TABLE builder ENABLE TRIGGER ALL;
101
102
103+ALTER TABLE buildpackagejob DISABLE TRIGGER ALL;
104+
105+INSERT INTO buildpackagejob (id, job, build) VALUES (1, 1, 8);
106+INSERT INTO buildpackagejob (id, job, build) VALUES (2, 2, 11);
107+
108+
109+ALTER TABLE buildpackagejob ENABLE TRIGGER ALL;
110+
111+
112 ALTER TABLE buildqueue DISABLE TRIGGER ALL;
113
114-INSERT INTO buildqueue (id, build, builder, logtail, created, buildstart, lastscore, manual) VALUES (1, 8, 1, 'Dummy sampledata entry, not processing', '2005-06-15 09:14:12.820778', '2005-06-15 09:20:12.820778', 1, false);
115-INSERT INTO buildqueue (id, build, builder, logtail, created, buildstart, lastscore, manual) VALUES (2, 11, NULL, NULL, '2005-06-15 10:14:12.820778', NULL, 10, false);
116+INSERT INTO buildqueue (id, builder, logtail, lastscore, manual, job, job_type) VALUES (1, 1, 'Dummy sampledata entry, not processing', 1, false, 1, 1);
117+INSERT INTO buildqueue (id, builder, logtail, lastscore, manual, job, job_type) VALUES (2, NULL, NULL, 10, false, 2, 1);
118
119
120 ALTER TABLE buildqueue ENABLE TRIGGER ALL;
121@@ -2753,6 +2771,8 @@
122
123 ALTER TABLE job DISABLE TRIGGER ALL;
124
125+INSERT INTO job (id, requester, reason, status, progress, last_report_seen, next_report_due, attempt_count, max_retries, log, scheduled_start, lease_expires, date_created, date_started, date_finished) VALUES (1, NULL, NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, '2005-06-15 09:14:12.820778', '2005-06-15 09:20:12.820778', NULL);
126+INSERT INTO job (id, requester, reason, status, progress, last_report_seen, next_report_due, attempt_count, max_retries, log, scheduled_start, lease_expires, date_created, date_started, date_finished) VALUES (2, NULL, NULL, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, '2005-06-15 10:14:12.820778', NULL, NULL);
127
128
129 ALTER TABLE job ENABLE TRIGGER ALL;
130@@ -4579,6 +4599,13 @@
131 ALTER TABLE packageset ENABLE TRIGGER ALL;
132
133
134+ALTER TABLE packagesetgroup DISABLE TRIGGER ALL;
135+
136+
137+
138+ALTER TABLE packagesetgroup ENABLE TRIGGER ALL;
139+
140+
141 ALTER TABLE packagesetinclusion DISABLE TRIGGER ALL;
142
143
144@@ -8500,6 +8527,13 @@
145 ALTER TABLE signedcodeofconduct ENABLE TRIGGER ALL;
146
147
148+ALTER TABLE sourcepackageformatselection DISABLE TRIGGER ALL;
149+
150+
151+
152+ALTER TABLE sourcepackageformatselection ENABLE TRIGGER ALL;
153+
154+
155 ALTER TABLE sourcepackagename DISABLE TRIGGER ALL;
156
157 INSERT INTO sourcepackagename (id, name) VALUES (1, 'mozilla-firefox');
158
159=== modified file 'database/schema/comments.sql'
160--- database/schema/comments.sql 2009-11-02 15:33:35 +0000
161+++ database/schema/comments.sql 2009-11-13 20:43:23 +0000
162@@ -1531,14 +1531,13 @@
163 COMMENT ON COLUMN Builder.active IS 'Whether to present or not the builder in the public list of builders avaialble. It is used to hide transient or defunct builders while they get fixed.';
164
165 -- BuildQueue
166-COMMENT ON TABLE BuildQueue IS 'BuildQueue: The queue of builds in progress/scheduled to run. This table is the core of the build daemon master. It lists all builds in progress or scheduled to start.';
167-COMMENT ON COLUMN BuildQueue.build IS 'The build for which this queue item exists. This is how the buildd master will find all the files it needs to perform the build';
168+COMMENT ON TABLE BuildQueue IS 'BuildQueue: The queue of jobs in progress/scheduled to run on the Soyuz build farm.';
169 COMMENT ON COLUMN BuildQueue.builder IS 'The builder assigned to this build. Some builds will have a builder assigned to queue them up; some will be building on the specified builder already; others will not have a builder yet (NULL) and will be waiting to be assigned into a builder''s queue';
170-COMMENT ON COLUMN BuildQueue.created IS 'The timestamp of the creation of this row. This is used by the buildd master scheduling algorithm to decide how soon to schedule a build to run on a given builder.';
171-COMMENT ON COLUMN BuildQueue.buildstart IS 'The timestamp of the start of the build run on the given builder. If this is NULL then the build is not running yet.';
172 COMMENT ON COLUMN BuildQueue.logtail IS 'The tail end of the log of the current build. This is updated regularly as the buildd master polls the buildd slaves. Once the build is complete; the full log will be lodged with the librarian and linked into the build table.';
173 COMMENT ON COLUMN BuildQueue.lastscore IS 'The last score ascribed to this build record. This can be used in the UI among other places.';
174 COMMENT ON COLUMN BuildQueue.manual IS 'Indicates if the current record was or not rescored manually, if so it get skipped from the auto-score procedure.';
175+COMMENT ON COLUMN BuildQueue.job IS 'Foreign key to the `Job` table row with the generic job data.';
176+COMMENT ON COLUMN BuildQueue.job_type IS 'Type of job (enumeration value), enables us to find/query the correct table with the data specific to this type of job.';
177
178 -- Mirrors
179
180
181=== added file 'database/schema/patch-2207-11-0.sql'
182--- database/schema/patch-2207-11-0.sql 1970-01-01 00:00:00 +0000
183+++ database/schema/patch-2207-11-0.sql 2009-11-13 20:43:23 +0000
184@@ -0,0 +1,87 @@
185+-- Copyright 2009 Canonical Ltd. This software is licensed under the
186+-- GNU Affero General Public License version 3 (see the file LICENSE).
187+
188+SET client_min_messages=ERROR;
189+
190+-- The schema patch required for the Soyuz buildd generalisation, see
191+-- https://dev.launchpad.net/Soyuz/Specs/BuilddGeneralisation for details.
192+-- Bug #478919.
193+
194+-- Step 1
195+-- The `BuildPackageJob` table captures whatever data is required for
196+-- "normal" Soyuz build farm jobs that build source packages.
197+
198+CREATE TABLE buildpackagejob (
199+ id serial PRIMARY KEY,
200+ -- FK to the `Job` record with "generic" data about this source package
201+ -- build job. Please note that the corresponding `BuildQueue` row will
202+ -- have a FK referencing the same `Job` row.
203+ job integer NOT NULL CONSTRAINT buildpackagejob__job__fk REFERENCES job,
204+ -- FK to the associated `Build` record.
205+ build integer NOT NULL CONSTRAINT buildpackagejob__build__fk REFERENCES build
206+);
207+
208+-- Step 2
209+-- Changes needed to the `BuildQueue` table.
210+
211+-- The 'job' and the 'job_type' columns will enable us to find the correct
212+-- database rows that hold the generic and the specific data pertaining to
213+-- the job respectively.
214+ALTER TABLE ONLY buildqueue ADD COLUMN job integer;
215+ALTER TABLE ONLY buildqueue ADD COLUMN job_type integer NOT NULL DEFAULT 1;
216+
217+-- Step 3
218+-- Data migration for the existing `BuildQueue` records.
219+CREATE OR REPLACE FUNCTION migrate_buildqueue_rows() RETURNS integer
220+LANGUAGE plpgsql AS
221+$$
222+DECLARE
223+ queue_row RECORD;
224+ job_id integer;
225+ buildpackagejob_id integer;
226+ rows_migrated integer;
227+BEGIN
228+ rows_migrated := 0;
229+ FOR queue_row IN SELECT * FROM buildqueue LOOP
230+ INSERT INTO job(status, date_created, date_started) VALUES(0, queue_row.created, queue_row.buildstart);
231+ -- Get the key of the `Job` row just inserted.
232+ SELECT currval('job_id_seq') INTO job_id;
233+ INSERT INTO buildpackagejob(job, build) VALUES(job_id, queue_row.build);
234+ -- Get the key of the `BuildPackageJob` row just inserted.
235+ SELECT currval('buildpackagejob_id_seq') INTO buildpackagejob_id;
236+ UPDATE buildqueue SET job=job_id WHERE id=queue_row.id;
237+ rows_migrated := rows_migrated + 1;
238+ END LOOP;
239+ RETURN rows_migrated;
240+END;
241+$$;
242+
243+-- Run the data migration function.
244+SELECT * FROM migrate_buildqueue_rows();
245+-- The `BuildQueue` data is migrated at this point, we can get rid of the
246+-- data migration function.
247+DROP FUNCTION migrate_buildqueue_rows();
248+
249+-- Now that the data was migrated we can make the 'job' column mandatory
250+-- and define the foreign key constraint for it.
251+ALTER TABLE ONLY buildqueue ALTER COLUMN job SET NOT NULL;
252+ALTER TABLE ONLY buildqueue
253+ ADD CONSTRAINT buildqueue__job__fk
254+ FOREIGN KEY (job) REFERENCES job(id);
255+
256+-- Step 4
257+-- Now remove the obsolete columns, constraints and indexes from `BuildQueue`.
258+-- The latter will from now on refer to the `Build` record via the
259+-- `Job`/`BuildPackageJob` tables (and not directly any more).
260+DROP INDEX buildqueue__build__idx;
261+ALTER TABLE ONLY buildqueue DROP CONSTRAINT "$1";
262+ALTER TABLE ONLY buildqueue DROP COLUMN build;
263+ALTER TABLE ONLY buildqueue DROP COLUMN created;
264+ALTER TABLE ONLY buildqueue DROP COLUMN buildstart;
265+
266+-- Step 5
267+-- Add indexes for the new `BuildQueue` columns.
268+CREATE INDEX buildqueue__job__idx ON buildqueue(job);
269+CREATE INDEX buildqueue__job_type__idx ON buildqueue(job_type);
270+
271+INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 11, 0);
272
273=== modified file 'database/schema/security.cfg'
274--- database/schema/security.cfg 2009-11-06 01:16:21 +0000
275+++ database/schema/security.cfg 2009-11-13 20:43:23 +0000
276@@ -837,6 +837,8 @@
277 public.archivearch = SELECT, UPDATE
278 public.archivedependency = SELECT
279 public.buildqueue = SELECT, INSERT, UPDATE, DELETE
280+public.job = SELECT, INSERT, UPDATE, DELETE
281+public.buildpackagejob = SELECT, INSERT, UPDATE, DELETE
282 public.builder = SELECT, INSERT, UPDATE
283 public.build = SELECT, INSERT, UPDATE
284 public.distribution = SELECT, UPDATE
285@@ -928,6 +930,8 @@
286 public.build = SELECT, INSERT, UPDATE
287 public.builder = SELECT, INSERT, UPDATE
288 public.buildqueue = SELECT, INSERT, UPDATE, DELETE
289+public.job = SELECT, INSERT, UPDATE, DELETE
290+public.buildpackagejob = SELECT, INSERT, UPDATE, DELETE
291 public.component = SELECT, INSERT, UPDATE
292 public.componentselection = SELECT, INSERT, UPDATE
293 public.country = SELECT, INSERT, UPDATE
294@@ -1114,6 +1118,8 @@
295 public.pocketchroot = SELECT
296 public.build = SELECT, INSERT, UPDATE
297 public.buildqueue = SELECT, INSERT, UPDATE
298+public.job = SELECT, INSERT, UPDATE
299+public.buildpackagejob = SELECT, INSERT, UPDATE
300
301 # Thusly the librarian
302 public.libraryfilecontent = SELECT, INSERT
303@@ -1195,6 +1201,8 @@
304 public.distrocomponentuploader = SELECT
305 public.build = SELECT, INSERT, UPDATE
306 public.buildqueue = SELECT, INSERT, UPDATE
307+public.job = SELECT, INSERT, UPDATE
308+public.buildpackagejob = SELECT, INSERT, UPDATE
309 public.pocketchroot = SELECT
310 public.sourcepackagerelease = SELECT, UPDATE
311 public.binarypackagerelease = SELECT, UPDATE
312
313=== modified file 'lib/lp/buildmaster/buildergroup.py'
314--- lib/lp/buildmaster/buildergroup.py 2009-08-16 12:38:12 +0000
315+++ lib/lp/buildmaster/buildergroup.py 2009-11-13 20:43:22 +0000
316@@ -130,8 +130,9 @@
317 try:
318 build = getUtility(IBuildSet).getByBuildID(int(build_id))
319 queue_item = getUtility(IBuildQueueSet).get(int(queue_item_id))
320- # Also check it build and buildqueue are properly related.
321- if queue_item.build.id != build.id:
322+ queued_build = getUtility(IBuildSet).getByQueueEntry(queue_item)
323+ # Also check whether build and buildqueue are properly related.
324+ if queued_build.id != build.id:
325 raise BuildJobMismatch('Job build entry mismatch')
326
327 except (SQLObjectNotFound, NotFoundError, BuildJobMismatch), reason:
328@@ -159,9 +160,10 @@
329
330 Invoke getFileFromSlave method with 'buildlog' identifier.
331 """
332+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
333 return queueItem.builder.transferSlaveFileToLibrarian(
334 'buildlog', queueItem.getLogFileName(),
335- queueItem.build.archive.private)
336+ build.archive.private)
337
338 def updateBuild(self, queueItem):
339 """Verify the current build job status.
340@@ -199,7 +201,7 @@
341 "Unknown status code (%s) returned from status() probe."
342 % builder_status)
343 queueItem.builder = None
344- queueItem.buildstart = None
345+ queueItem.setDateStarted(None)
346 self.commit()
347 return
348
349@@ -261,17 +263,18 @@
350
351 Store Buildlog, datebuilt, duration, dependencies.
352 """
353- queueItem.build.buildlog = self.getLogFromSlave(queueItem)
354- queueItem.build.builder = queueItem.builder
355- queueItem.build.dependencies = dependencies
356+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
357+ build.buildlog = self.getLogFromSlave(queueItem)
358+ build.builder = queueItem.builder
359+ build.dependencies = dependencies
360 # XXX cprov 20060615 bug=120584: Currently buildduration includes
361 # the scanner latency, it should really be asking the slave for
362 # the duration spent building locally.
363- queueItem.build.datebuilt = UTC_NOW
364+ build.datebuilt = UTC_NOW
365 # We need dynamic datetime.now() instance to be able to perform
366 # the time operations for duration.
367 RIGHT_NOW = datetime.datetime.now(pytz.timezone('UTC'))
368- queueItem.build.buildduration = RIGHT_NOW - queueItem.buildstart
369+ build.buildduration = RIGHT_NOW - queueItem.date_started
370
371
372 def buildStatus_OK(self, queueItem, librarian, buildid,
373@@ -287,7 +290,7 @@
374 self.logger.debug("Processing successful build %s" % buildid)
375 # Explode before collect a binary that is denied in this
376 # distroseries/pocket
377- build = queueItem.build
378+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
379 if not build.archive.allowUpdatesToReleasePocket():
380 assert build.distroseries.canUploadToPocket(build.pocket), (
381 "%s (%s) can not be built for pocket %s: illegal status"
382@@ -309,8 +312,9 @@
383 # can be correctly found during the upload:
384 # <archive_id>/distribution_name
385 # for all destination archive types.
386- archive = queueItem.build.archive
387- distribution_name = queueItem.build.distribution.name
388+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
389+ archive = build.archive
390+ distribution_name = build.distribution.name
391 target_path = '%s/%s' % (archive.id, distribution_name)
392 upload_path = os.path.join(upload_dir, target_path)
393 os.makedirs(upload_path)
394@@ -330,10 +334,10 @@
395 # add extra arguments for processing a binary upload
396 extra_args = [
397 "--log-file", "%s" % uploader_logfilename,
398- "-d", "%s" % queueItem.build.distribution.name,
399- "-s", "%s" % (queueItem.build.distroseries.name +
400- pocketsuffix[queueItem.build.pocket]),
401- "-b", "%s" % queueItem.build.id,
402+ "-d", "%s" % build.distribution.name,
403+ "-s", "%s" % (build.distroseries.name +
404+ pocketsuffix[build.pocket]),
405+ "-b", "%s" % build.id,
406 "-J", "%s" % upload_leaf,
407 "%s" % root,
408 ]
409@@ -409,12 +413,11 @@
410 # uploader about this occurrence. The failure notification will
411 # also contain the information required to manually reprocess the
412 # binary upload when it was the case.
413- build = getUtility(IBuildSet).getByBuildID(queueItem.build.id)
414+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
415 if (build.buildstate != BuildStatus.FULLYBUILT or
416 build.binarypackages.count() == 0):
417 self.logger.debug("Build %s upload failed." % build.id)
418- # update builder
419- queueItem.build.buildstate = BuildStatus.FAILEDTOUPLOAD
420+ build.buildstate = BuildStatus.FAILEDTOUPLOAD
421 # Retrieve log file content.
422 possible_locations = (
423 'failed', 'failed-to-move', 'rejected', 'accepted')
424@@ -434,11 +437,13 @@
425 uploader_log_content = 'Could not find upload log file'
426 # Store the upload_log_contents in librarian so it can be
427 # accessed by anyone with permission to see the build.
428- queueItem.build.storeUploadLog(uploader_log_content)
429+ build.storeUploadLog(uploader_log_content)
430 # Notify the build failure.
431- queueItem.build.notify(extra_info=uploader_log_content)
432+ build.notify(extra_info=uploader_log_content)
433 else:
434- self.logger.debug("Gathered build %s completely" % queueItem.name)
435+ self.logger.debug(
436+ "Gathered build %s completely" %
437+ build.sourcepackagerelease.name)
438
439 # Release the builder for another job.
440 queueItem.builder.cleanSlave()
441@@ -456,10 +461,11 @@
442 set the job status as FAILEDTOBUILD, store available info and
443 remove Buildqueue entry.
444 """
445- queueItem.build.buildstate = BuildStatus.FAILEDTOBUILD
446+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
447+ build.buildstate = BuildStatus.FAILEDTOBUILD
448 self.storeBuildInfo(queueItem, librarian, buildid, dependencies)
449 queueItem.builder.cleanSlave()
450- queueItem.build.notify()
451+ build.notify()
452 queueItem.destroySelf()
453
454 def buildStatus_DEPFAIL(self, queueItem, librarian, buildid,
455@@ -470,7 +476,8 @@
456 MANUALDEPWAIT, store available information, remove BuildQueue
457 entry and release builder slave for another job.
458 """
459- queueItem.build.buildstate = BuildStatus.MANUALDEPWAIT
460+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
461+ build.buildstate = BuildStatus.MANUALDEPWAIT
462 self.storeBuildInfo(queueItem, librarian, buildid, dependencies)
463 self.logger.critical("***** %s is MANUALDEPWAIT *****"
464 % queueItem.builder.name)
465@@ -485,12 +492,13 @@
466 job as CHROOTFAIL, store available information, remove BuildQueue
467 and release the builder.
468 """
469- queueItem.build.buildstate = BuildStatus.CHROOTWAIT
470+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
471+ build.buildstate = BuildStatus.CHROOTWAIT
472 self.storeBuildInfo(queueItem, librarian, buildid, dependencies)
473 self.logger.critical("***** %s is CHROOTWAIT *****" %
474 queueItem.builder.name)
475 queueItem.builder.cleanSlave()
476- queueItem.build.notify()
477+ build.notify()
478 queueItem.destroySelf()
479
480 def buildStatus_BUILDERFAIL(self, queueItem, librarian, buildid,
481@@ -507,10 +515,11 @@
482 ("Builder returned BUILDERFAIL when asked "
483 "for its status"))
484 # simply reset job
485- queueItem.build.buildstate = BuildStatus.NEEDSBUILD
486+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
487+ build.buildstate = BuildStatus.NEEDSBUILD
488 self.storeBuildInfo(queueItem, librarian, buildid, dependencies)
489 queueItem.builder = None
490- queueItem.buildstart = None
491+ queueItem.setDateStarted(None)
492
493 def buildStatus_GIVENBACK(self, queueItem, librarian, buildid,
494 filemap=None, dependencies=None):
495@@ -522,7 +531,8 @@
496 """
497 self.logger.warning("***** %s is GIVENBACK by %s *****"
498 % (buildid, queueItem.builder.name))
499- queueItem.build.buildstate = BuildStatus.NEEDSBUILD
500+ build = getUtility(IBuildSet).getByQueueEntry(queueItem)
501+ build.buildstate = BuildStatus.NEEDSBUILD
502 self.storeBuildInfo(queueItem, librarian, buildid, dependencies)
503 # XXX cprov 2006-05-30: Currently this information is not
504 # properly presented in the Web UI. We will discuss it in
505@@ -530,7 +540,7 @@
506 # to use this content. For now we just ensure it's stored.
507 queueItem.builder.cleanSlave()
508 queueItem.builder = None
509- queueItem.buildstart = None
510+ queueItem.setDateStarted(None)
511 queueItem.logtail = None
512 queueItem.lastscore = 0
513
514
515=== added directory 'lib/lp/buildmaster/interfaces'
516=== added file 'lib/lp/buildmaster/interfaces/__init__.py'
517=== added file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
518--- lib/lp/buildmaster/interfaces/buildfarmjob.py 1970-01-01 00:00:00 +0000
519+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2009-11-13 20:43:23 +0000
520@@ -0,0 +1,71 @@
521+# Copyright 2009 Canonical Ltd. This software is licensed under the
522+# GNU Affero General Public License version 3 (see the file LICENSE).
523+
524+# pylint: disable-msg=E0211,E0213
525+
526+"""Interface for Soyuz build farm jobs."""
527+
528+__metaclass__ = type
529+
530+__all__ = [
531+ 'IBuildFarmJob',
532+ 'BuildFarmJobType',
533+ ]
534+
535+from zope.interface import Interface
536+from lazr.enum import DBEnumeratedType, DBItem
537+
538+
539+class BuildFarmJobType(DBEnumeratedType):
540+ """Soyuz build farm job type.
541+
542+ An enumeration with the types of jobs that may be run on the Soyuz build
543+ farm.
544+ """
545+
546+ PACKAGEBUILD = DBItem(1, """
547+ PackageBuildJob
548+
549+ Build a source package.
550+ """)
551+
552+ BRANCHBUILD = DBItem(2, """
553+ BranchBuildJob
554+
555+ Build a package from a bazaar branch.
556+ """)
557+
558+ RECIPEBRANCHBUILD = DBItem(3, """
559+ RecipeBranchBuildJob
560+
561+ Build a package from a bazaar branch and a recipe.
562+ """)
563+
564+ TRANSLATION = DBItem(4, """
565+ TranslationJob
566+
567+ Perform a translation job.
568+ """)
569+
570+
571+class IBuildFarmJob(Interface):
572+ """Operations that Soyuz build farm jobs must implement."""
573+
574+ def score():
575+ """Calculate a job score appropriate for the job type in question."""
576+
577+ def getLogFileName():
578+ """The preferred file name for the log of this Soyuz job."""
579+
580+ def getName():
581+ """An appropriate name for this Soyuz job."""
582+
583+ def jobStarted():
584+ """'Job started' life cycle event, handle as appropriate."""
585+
586+ def jobReset():
587+ """'Job reset' life cycle event, handle as appropriate."""
588+
589+ def jobAborted():
590+ """'Job aborted' life cycle event, handle as appropriate."""
591+
592
593=== modified file 'lib/lp/buildmaster/master.py'
594--- lib/lp/buildmaster/master.py 2009-10-26 18:40:04 +0000
595+++ lib/lp/buildmaster/master.py 2009-11-13 20:43:23 +0000
596@@ -280,8 +280,10 @@
597 "scanActiveBuilders() found %d active build(s) to check"
598 % queueItems.count())
599
600+ build_set = getUtility(IBuildSet)
601 for job in queueItems:
602- proc = job.archseries.processorfamily
603+ build = build_set.getByQueueEntry(job)
604+ proc = build.distroarchseries.processorfamily
605 try:
606 builders = notes[proc]["builders"]
607 except KeyError:
608@@ -309,7 +311,7 @@
609 % candidates.count())
610
611 for job in candidates:
612- uptodate_build = getUtility(IBuildSet).getByBuildID(job.build.id)
613+ uptodate_build = getUtility(IBuildSet).getByQueueEntry(job)
614 if uptodate_build.buildstate != BuildStatus.NEEDSBUILD:
615 continue
616 job.score()
617
618=== added directory 'lib/lp/buildmaster/model'
619=== added file 'lib/lp/buildmaster/model/__init__.py'
620=== added file 'lib/lp/buildmaster/model/buildfarmjob.py'
621--- lib/lp/buildmaster/model/buildfarmjob.py 1970-01-01 00:00:00 +0000
622+++ lib/lp/buildmaster/model/buildfarmjob.py 2009-11-13 20:43:23 +0000
623@@ -0,0 +1,40 @@
624+# Copyright 2009 Canonical Ltd. This software is licensed under the
625+# GNU Affero General Public License version 3 (see the file LICENSE).
626+
627+__metaclass__ = type
628+__all__ = ['BuildFarmJob']
629+
630+
631+from zope.interface import implements
632+
633+from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
634+
635+
636+class BuildFarmJob:
637+ """Mix-in class for `IBuildFarmJob` implementations."""
638+ implements(IBuildFarmJob)
639+
640+ def score(self):
641+ """See `IBuildFarmJob`."""
642+ raise NotImplementedError
643+
644+ def getLogFileName(self):
645+ """See `IBuildFarmJob`."""
646+ raise NotImplementedError
647+
648+ def getName(self):
649+ """See `IBuildFarmJob`."""
650+ raise NotImplementedError
651+
652+ def jobStarted(self):
653+ """See `IBuildFarmJob`."""
654+ pass
655+
656+ def jobReset(self):
657+ """See `IBuildFarmJob`."""
658+ pass
659+
660+ def jobAborted(self):
661+ """See `IBuildFarmJob`."""
662+ pass
663+
664
665=== modified file 'lib/lp/buildmaster/tests/queuebuilder.txt'
666--- lib/lp/buildmaster/tests/queuebuilder.txt 2009-10-26 18:40:04 +0000
667+++ lib/lp/buildmaster/tests/queuebuilder.txt 2009-11-13 20:43:23 +0000
668@@ -229,7 +229,7 @@
669 >>> copied_pub = pub_failed.copyTo(
670 ... hoary, PackagePublishingPocket.RELEASE, warty.main_archive)
671
672- >>> from lp.soyuz.interfaces.build import BuildStatus
673+ >>> from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
674 >>> failed_build = pub_failed.sourcepackagerelease.createBuild(
675 ... warty['i386'], PackagePublishingPocket.RELEASE,
676 ... warty.main_archive, status=BuildStatus.FAILEDTOBUILD)
677@@ -343,7 +343,8 @@
678 happen in parallel with build creation.
679
680 >>> build_queue = active_jobs[0]
681- >>> print build_queue.build.title
682+ >>> build = getUtility(IBuildSet).getByQueueEntry(build_queue)
683+ >>> print build.title
684 i386 build of test-buildd 667 in ubuntu hoary RELEASE
685 >>> build_queue.lastscore
686 2505
687@@ -351,15 +352,15 @@
688 Check the published component name retriever, they might be different,
689 i.e., the published component can be different than the original component.
690
691- >>> print build_queue.build.current_component.name
692+ >>> print build.current_component.name
693 main
694- >>> print build_queue.build.sourcepackagerelease.component.name
695+ >>> print build.sourcepackagerelease.component.name
696 main
697
698 Missing BuildQueue records, resulting from given-back builds, are
699 created in the last stage of the queue-builder script.
700
701- >>> given_back_build = build_queue.build
702+ >>> given_back_build = getUtility(IBuildSet).getByQueueEntry(build_queue)
703 >>> build_queue.destroySelf()
704 >>> flush_database_updates()
705
706
707=== modified file 'lib/lp/buildmaster/tests/test_manager.py'
708--- lib/lp/buildmaster/tests/test_manager.py 2009-09-07 13:02:02 +0000
709+++ lib/lp/buildmaster/tests/test_manager.py 2009-11-13 20:43:23 +0000
710@@ -24,7 +24,7 @@
711 from lp.buildmaster.tests.harness import BuilddManagerTestSetup
712 from canonical.launchpad.ftests import ANONYMOUS, login
713 from lp.soyuz.tests.soyuzbuilddhelpers import SaneBuildingSlave
714-from lp.soyuz.interfaces.build import BuildStatus
715+from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
716 from lp.soyuz.interfaces.builder import IBuilderSet
717 from lp.soyuz.interfaces.buildqueue import IBuildQueueSet
718 from lp.registry.interfaces.distribution import IDistributionSet
719@@ -494,8 +494,9 @@
720
721 self.assertTrue(job is not None)
722 self.assertEqual(job.builder, builder)
723- self.assertTrue(job.buildstart is not None)
724- self.assertEqual(job.build.buildstate, BuildStatus.BUILDING)
725+ self.assertTrue(job.date_started is not None)
726+ build = getUtility(IBuildSet).getByQueueEntry(job)
727+ self.assertEqual(build.buildstate, BuildStatus.BUILDING)
728 self.assertEqual(job.logtail, logtail)
729
730 def _getManager(self):
731@@ -617,8 +618,9 @@
732
733 job = getUtility(IBuildQueueSet).get(job.id)
734 self.assertTrue(job.builder is None)
735- self.assertTrue(job.buildstart is None)
736- self.assertEqual(job.build.buildstate, BuildStatus.NEEDSBUILD)
737+ self.assertTrue(job.date_started is None)
738+ build = getUtility(IBuildSet).getByQueueEntry(job)
739+ self.assertEqual(build.buildstate, BuildStatus.NEEDSBUILD)
740
741 def testScanRescuesJobFromBrokenBuilder(self):
742 # The job assigned to a broken builder is rescued.
743@@ -701,13 +703,14 @@
744 builder.builderok = True
745
746 job = builder.currentjob
747+ build = getUtility(IBuildSet).getByQueueEntry(job)
748 self.assertEqual(
749 'i386 build of mozilla-firefox 0.9 in ubuntu hoary RELEASE',
750- job.build.title)
751+ build.title)
752
753- self.assertEqual('BUILDING', job.build.buildstate.name)
754+ self.assertEqual('BUILDING', build.buildstate.name)
755 self.assertNotEqual(None, job.builder)
756- self.assertNotEqual(None, job.buildstart)
757+ self.assertNotEqual(None, job.date_started)
758 self.assertNotEqual(None, job.logtail)
759
760 transaction.commit()
761@@ -717,9 +720,10 @@
762 def assertJobIsClean(self, job_id):
763 """Re-fetch the `IBuildQueue` record and check if it's clean."""
764 job = getUtility(IBuildQueueSet).get(job_id)
765- self.assertEqual('NEEDSBUILD', job.build.buildstate.name)
766+ build = getUtility(IBuildSet).getByQueueEntry(job)
767+ self.assertEqual('NEEDSBUILD', build.buildstate.name)
768 self.assertEqual(None, job.builder)
769- self.assertEqual(None, job.buildstart)
770+ self.assertEqual(None, job.date_started)
771 self.assertEqual(None, job.logtail)
772
773 def testResetDispatchResult(self):
774
775=== modified file 'lib/lp/registry/model/sourcepackage.py'
776--- lib/lp/registry/model/sourcepackage.py 2009-10-26 18:40:04 +0000
777+++ lib/lp/registry/model/sourcepackage.py 2009-11-13 20:43:23 +0000
778@@ -554,8 +554,10 @@
779 # It should present the builds in a more natural order.
780 if build_state in [BuildStatus.NEEDSBUILD, BuildStatus.BUILDING]:
781 orderBy = ["-BuildQueue.lastscore"]
782+ clauseTables.append('BuildPackageJob')
783+ condition_clauses.append('BuildPackageJob.build = Build.id')
784 clauseTables.append('BuildQueue')
785- condition_clauses.append('BuildQueue.build = Build.id')
786+ condition_clauses.append('BuildQueue.job = BuildPackageJob.job')
787 elif build_state == BuildStatus.SUPERSEDED or build_state is None:
788 orderBy = ["-Build.datecreated"]
789 else:
790
791=== modified file 'lib/lp/services/job/tests/test_job.py'
792--- lib/lp/services/job/tests/test_job.py 2009-07-17 00:26:05 +0000
793+++ lib/lp/services/job/tests/test_job.py 2009-11-13 20:43:23 +0000
794@@ -8,6 +8,8 @@
795 from unittest import TestLoader
796
797 import pytz
798+from zope.component import getUtility
799+
800 from canonical.database.constants import UTC_NOW
801 from canonical.testing import LaunchpadZopelessLayer
802 from storm.locals import Store
803@@ -17,6 +19,8 @@
804 from lp.services.job.interfaces.job import IJob, JobStatus
805 from lp.testing import TestCase
806 from canonical.launchpad.webapp.testing import verifyObject
807+from canonical.launchpad.webapp.interfaces import (
808+ IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
809
810
811 class TestJob(TestCase):
812@@ -155,40 +159,53 @@
813
814 layer = LaunchpadZopelessLayer
815
816+ def _sampleData(self):
817+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
818+ return list(store.execute(Job.ready_jobs))
819+
820 def test_ready_jobs(self):
821 """Job.ready_jobs should include new jobs."""
822+ preexisting = self._sampleData()
823 job = Job()
824 self.assertEqual(
825- [(job.id,)], list(Store.of(job).execute(Job.ready_jobs)))
826+ preexisting + [(job.id,)],
827+ list(Store.of(job).execute(Job.ready_jobs)))
828
829 def test_ready_jobs_started(self):
830 """Job.ready_jobs should not jobs that have been started."""
831+ preexisting = self._sampleData()
832 job = Job(_status=JobStatus.RUNNING)
833 self.assertEqual(
834- [], list(Store.of(job).execute(Job.ready_jobs)))
835+ preexisting, list(Store.of(job).execute(Job.ready_jobs)))
836
837 def test_ready_jobs_lease_expired(self):
838 """Job.ready_jobs should include jobs with expired leases."""
839+ preexisting = self._sampleData()
840 UNIX_EPOCH = datetime.fromtimestamp(0, pytz.timezone('UTC'))
841 job = Job(lease_expires=UNIX_EPOCH)
842 self.assertEqual(
843- [(job.id,)], list(Store.of(job).execute(Job.ready_jobs)))
844+ preexisting + [(job.id,)],
845+ list(Store.of(job).execute(Job.ready_jobs)))
846
847 def test_ready_jobs_lease_in_future(self):
848 """Job.ready_jobs should not include jobs with active leases."""
849+ preexisting = self._sampleData()
850 future = datetime.fromtimestamp(
851 time.time() + 1000, pytz.timezone('UTC'))
852 job = Job(lease_expires=future)
853- self.assertEqual([], list(Store.of(job).execute(Job.ready_jobs)))
854+ self.assertEqual(
855+ preexisting, list(Store.of(job).execute(Job.ready_jobs)))
856
857 def test_ready_jobs_not_jobs_scheduled_in_future(self):
858 """Job.ready_jobs does not included jobs scheduled for a time in the
859 future.
860 """
861+ preexisting = self._sampleData()
862 future = datetime.fromtimestamp(
863 time.time() + 1000, pytz.timezone('UTC'))
864 job = Job(scheduled_start=future)
865- self.assertEqual([], list(Store.of(job).execute(Job.ready_jobs)))
866+ self.assertEqual(
867+ preexisting, list(Store.of(job).execute(Job.ready_jobs)))
868
869 def test_acquireLease(self):
870 """Job.acquireLease should set job.lease_expires."""
871
872=== modified file 'lib/lp/soyuz/browser/build.py'
873--- lib/lp/soyuz/browser/build.py 2009-10-26 18:40:04 +0000
874+++ lib/lp/soyuz/browser/build.py 2009-11-13 20:43:23 +0000
875@@ -288,10 +288,10 @@
876 prefetched_data = dict()
877 build_ids = [build.id for build in builds]
878 results = getUtility(IBuildQueueSet).getForBuilds(build_ids)
879- for (buildqueue, builder) in results:
880+ for (buildqueue, _builder, build_job) in results:
881 # Get the build's id, 'buildqueue', 'sourcepackagerelease' and
882 # 'buildlog' (from the result set) respectively.
883- prefetched_data[buildqueue.build.id] = buildqueue
884+ prefetched_data[build_job.build.id] = buildqueue
885
886 complete_builds = []
887 for build in builds:
888
889=== modified file 'lib/lp/soyuz/browser/builder.py'
890--- lib/lp/soyuz/browser/builder.py 2009-09-17 14:45:15 +0000
891+++ lib/lp/soyuz/browser/builder.py 2009-11-13 20:43:23 +0000
892@@ -237,12 +237,12 @@
893 def current_build_duration(self):
894 """Return the delta representing the duration of the current job."""
895 if (self.context.currentjob is None or
896- self.context.currentjob.buildstart is None):
897+ self.context.currentjob.date_started is None):
898 return None
899 else:
900 UTC = pytz.timezone('UTC')
901- buildstart = self.context.currentjob.buildstart
902- return datetime.datetime.now(UTC) - buildstart
903+ date_started = self.context.currentjob.date_started
904+ return datetime.datetime.now(UTC) - date_started
905
906 @property
907 def page_title(self):
908
909=== modified file 'lib/lp/soyuz/browser/tests/builder-views.txt'
910--- lib/lp/soyuz/browser/tests/builder-views.txt 2009-09-16 19:06:48 +0000
911+++ lib/lp/soyuz/browser/tests/builder-views.txt 2009-11-13 20:43:23 +0000
912@@ -1,7 +1,7 @@
913 = Builder View Classes and Pages =
914
915 >>> from zope.component import getMultiAdapter, getUtility
916- >>> from canonical.launchpad.interfaces import IBuilderSet
917+ >>> from canonical.launchpad.interfaces import IBuildSet, IBuilderSet
918 >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
919
920 >>> builder = getUtility(IBuilderSet).get(1)
921@@ -158,7 +158,8 @@
922 >>> frog = getUtility(IBuilderSet)['frog']
923 >>> frog.builderok = True
924 >>> private_build.builder = frog
925- >>> private_job = BuildQueue(build=private_build, builder=frog)
926+ >>> private_job = private_build.createBuildQueueEntry()
927+ >>> private_job.builder = frog
928 >>> private_job_id = private_job.id
929
930 >>> from canonical.database.sqlbase import flush_database_caches
931@@ -175,7 +176,9 @@
932 >>> print frog.builderok
933 True
934
935- >>> print frog.currentjob.build.title
936+ >>> build_set = getUtility(IBuildSet)
937+ >>> build = build_set.getByQueueEntry(frog.currentjob)
938+ >>> print build.title
939 i386 build of privacy-test 666 in ubuntutest breezy-autotest RELEASE
940
941 >>> print frog.failnotes
942@@ -199,7 +202,8 @@
943 >>> print admin_view.context.builderok
944 True
945
946- >>> print admin_view.context.currentjob.build.title
947+ >>> build = build_set.getByQueueEntry(admin_view.context.currentjob)
948+ >>> print build.title
949 i386 build of privacy-test 666 in ubuntutest breezy-autotest RELEASE
950
951 >>> print admin_view.context.failnotes
952@@ -211,7 +215,7 @@
953
954 >>> import datetime
955 >>> import pytz
956- >>> private_job.buildstart = (
957+ >>> private_job.setDateStarted(
958 ... datetime.datetime.now(pytz.UTC) - datetime.timedelta(10))
959 >>> print admin_view.current_build_duration
960 10 days...
961
962=== modified file 'lib/lp/soyuz/configure.zcml'
963--- lib/lp/soyuz/configure.zcml 2009-11-09 17:59:18 +0000
964+++ lib/lp/soyuz/configure.zcml 2009-11-13 20:43:23 +0000
965@@ -560,7 +560,7 @@
966
967 <require
968 permission="zope.Public"
969- set_attributes="lastscore builder buildstart logtail"/>
970+ set_attributes="lastscore builder logtail date_started"/>
971 </class>
972
973 <!-- BuildQueueSet -->
974@@ -863,5 +863,11 @@
975 interface="lp.soyuz.interfaces.packagesetgroup.IPackagesetGroup"/>
976 </class>
977
978+ <!-- BuildPackageJob -->
979+ <class
980+ class="lp.soyuz.model.buildpackagejob.BuildPackageJob">
981+ <allow
982+ interface="lp.soyuz.interfaces.buildpackagejob.IBuildPackageJob"/>
983+ </class>
984
985 </configure>
986
987=== modified file 'lib/lp/soyuz/doc/build-estimated-dispatch-time.txt'
988--- lib/lp/soyuz/doc/build-estimated-dispatch-time.txt 2009-08-28 07:34:44 +0000
989+++ lib/lp/soyuz/doc/build-estimated-dispatch-time.txt 2009-11-13 20:43:22 +0000
990@@ -59,7 +59,8 @@
991 >>> UTC = pytz.timezone('UTC')
992 >>> bob_the_builder = builder_set.get(1)
993 >>> cur_bqueue = bob_the_builder.currentjob
994- >>> cur_build = cur_bqueue.build
995+ >>> from lp.soyuz.interfaces.build import IBuildSet
996+ >>> cur_build = getUtility(IBuildSet).getByQueueEntry(cur_bqueue)
997
998 Make sure the job at hand is currently being built.
999
1000@@ -73,16 +74,16 @@
1001 of job N in the build queue. These values will now be set for the job
1002 that is currently building.
1003
1004+ >>> from zope.security.proxy import removeSecurityProxy
1005 >>> cur_bqueue.lastscore = 1111
1006- >>> cur_bqueue.buildstart = datetime(2008, 4, 1, 10, 45, 39,
1007- ... tzinfo=UTC)
1008- >>> print cur_bqueue.buildstart
1009+ >>> cur_bqueue.setDateStarted(
1010+ ... datetime(2008, 4, 1, 10, 45, 39, tzinfo=UTC))
1011+ >>> print cur_bqueue.date_started
1012 2008-04-01 10:45:39+00:00
1013
1014 Please note that the "estimated build duration" is an internal property
1015 and not meant to be viewed or modified by an end user.
1016
1017- >>> from zope.security.proxy import removeSecurityProxy
1018 >>> naked_build = removeSecurityProxy(cur_build)
1019 >>> naked_build.estimated_build_duration = timedelta(minutes=56)
1020
1021
1022=== modified file 'lib/lp/soyuz/doc/buildd-dispatching.txt'
1023--- lib/lp/soyuz/doc/buildd-dispatching.txt 2009-10-14 08:20:40 +0000
1024+++ lib/lp/soyuz/doc/buildd-dispatching.txt 2009-11-13 20:43:23 +0000
1025@@ -134,18 +134,20 @@
1026
1027 >>> job.id
1028 2
1029- >>> job.build.buildstate.name
1030+ >>> from lp.soyuz.interfaces.build import IBuildSet
1031+ >>> build = getUtility(IBuildSet).getByQueueEntry(job)
1032+ >>> build.buildstate.name
1033 'NEEDSBUILD'
1034 >>> job.builder is None
1035 True
1036- >>> job.buildstart is None
1037+ >>> job.date_started is None
1038 True
1039- >>> job.is_virtualized
1040+ >>> build.is_virtualized
1041 False
1042
1043 The build start time is not set yet either.
1044
1045- >>> print job.build.date_first_dispatched
1046+ >>> print build.date_first_dispatched
1047 None
1048
1049 Update the SourcePackageReleaseFile corresponding to this job:
1050@@ -154,7 +156,7 @@
1051 >>> alias_id = librarian_client.addFile(
1052 ... 'foo.dsc', len(content), StringIO(content), 'application/dsc')
1053
1054- >>> sprf = job.build.sourcepackagerelease.files[0]
1055+ >>> sprf = build.sourcepackagerelease.files[0]
1056 >>> from zope.security.proxy import removeSecurityProxy
1057 >>> naked_sprf = removeSecurityProxy(sprf)
1058 >>> naked_sprf.libraryfile = getUtility(ILibraryFileAliasSet)[alias_id]
1059@@ -167,35 +169,20 @@
1060
1061 Verify if the job (BuildQueue) was updated appropriately:
1062
1063- >>> def checkTimes(expected, actual):
1064- ... if expected != actual:
1065- ... return "expected: %s, actual: %s" % (expected, actual)
1066- ... else:
1067- ... return "OK"
1068-
1069 >>> job.builder.id == bob_builder.id
1070 True
1071
1072- >>> job.build.buildstate.name
1073+ >>> build = getUtility(IBuildSet).getByQueueEntry(job)
1074+ >>> build.buildstate.name
1075 'BUILDING'
1076
1077- >>> from canonical.database.sqlbase import get_transaction_timestamp
1078- >>> checkTimes(get_transaction_timestamp(), job.buildstart)
1079- 'OK'
1080-
1081-The build start time will be set to the same value.
1082-
1083- >>> checkTimes(get_transaction_timestamp(),
1084- ... job.build.date_first_dispatched)
1085- 'OK'
1086-
1087 Shutdown builder, mark the build record as failed and remove the
1088 buildqueue record, so the build was eliminated:
1089
1090 >>> BuilddSlaveTestSetup().tearDown()
1091
1092 >>> from lp.soyuz.interfaces.build import BuildStatus
1093- >>> job.build.buildstate = BuildStatus.FAILEDTOBUILD
1094+ >>> build.buildstate = BuildStatus.FAILEDTOBUILD
1095 >>> job.destroySelf()
1096 >>> flush_database_updates()
1097
1098@@ -217,12 +204,13 @@
1099 3
1100 >>> ppa_job.builder == None
1101 True
1102- >>> ppa_job.buildstart == None
1103+ >>> ppa_job.date_started == None
1104 True
1105
1106 The build job's archive requires virtualized builds.
1107
1108- >>> ppa_job.build.archive.require_virtualized
1109+ >>> build = getUtility(IBuildSet).getByQueueEntry(ppa_job)
1110+ >>> build.archive.require_virtualized
1111 True
1112
1113 But the builder is not virtualized.
1114@@ -249,10 +237,10 @@
1115 >>> from lp.soyuz.model.publishing import (
1116 ... SourcePackagePublishingHistory)
1117 >>> [old_pub] = SourcePackagePublishingHistory.selectBy(
1118- ... distroseries=ppa_job.build.distroseries,
1119- ... sourcepackagerelease=ppa_job.build.sourcepackagerelease)
1120+ ... distroseries=build.distroseries,
1121+ ... sourcepackagerelease=build.sourcepackagerelease)
1122 >>> new_pub = old_pub.copyTo(
1123- ... old_pub.distroseries, old_pub.pocket, ppa_job.build.archive)
1124+ ... old_pub.distroseries, old_pub.pocket, build.archive)
1125
1126 >>> bob_builder.virtualized = True
1127 >>> syncUpdate(bob_builder)
1128@@ -293,19 +281,16 @@
1129 >>> ppa_job.builder.name
1130 u'bob'
1131
1132- >>> ppa_job.build.buildstate.name
1133+ >>> build.buildstate.name
1134 'BUILDING'
1135
1136- >>> ppa_job.buildstart == get_transaction_timestamp()
1137- True
1138-
1139 Shutdown builder slave, mark the ppa build record as failed, remove the
1140 buildqueue record and make 'bob' builder non-virtual again, so the
1141 environment is back to the initial state.
1142
1143 >>> BuilddSlaveTestSetup().tearDown()
1144
1145- >>> ppa_job.build.buildstate = BuildStatus.FAILEDTOBUILD
1146+ >>> build.buildstate = BuildStatus.FAILEDTOBUILD
1147 >>> ppa_job.destroySelf()
1148 >>> bob_builder.virtualized = False
1149 >>> flush_database_updates()
1150@@ -332,9 +317,9 @@
1151 4
1152 >>> print sec_job.builder
1153 None
1154- >>> print sec_job.buildstart
1155+ >>> print sec_job.date_started
1156 None
1157- >>> sec_job.is_virtualized
1158+ >>> sec_build.is_virtualized
1159 False
1160
1161 In normal conditions the next available candidate would be the job
1162
1163=== modified file 'lib/lp/soyuz/doc/buildd-scoring.txt'
1164--- lib/lp/soyuz/doc/buildd-scoring.txt 2009-08-30 23:57:41 +0000
1165+++ lib/lp/soyuz/doc/buildd-scoring.txt 2009-11-13 20:43:23 +0000
1166@@ -49,7 +49,7 @@
1167 >>> def setUpBuildQueueEntry(
1168 ... component_name='main', urgency=SourcePackageUrgency.HIGH,
1169 ... pocket=PackagePublishingPocket.RELEASE,
1170- ... date_created=LOCAL_NOW, manual=False):
1171+ ... date_created=LOCAL_NOW, manual=False, archive=None):
1172 ... global version
1173 ... commit()
1174 ... LaunchpadZopelessLayer.switchDbUser('launchpad')
1175@@ -57,7 +57,7 @@
1176 ... sourcename='test-build', version=str(version),
1177 ... distroseries=hoary, component=component_name,
1178 ... urgency=urgency, pocket=pocket,
1179- ... status=PackagePublishingStatus.PUBLISHED)
1180+ ... status=PackagePublishingStatus.PUBLISHED, archive=archive)
1181 ... commit()
1182 ... LaunchpadZopelessLayer.switchDbUser(test_dbuser)
1183 ... version += 1
1184@@ -65,7 +65,7 @@
1185 ... hoary386, pub.pocket, pub.archive)
1186 ...
1187 ... build_queue = build.createBuildQueueEntry()
1188- ... build_queue.created = date_created
1189+ ... build_queue.job.date_created = date_created
1190 ... build_queue.manual = manual
1191 ...
1192 ... return build_queue
1193@@ -86,8 +86,10 @@
1194
1195 >>> commit()
1196 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
1197- >>> bq0.build.archive.buildd_secret = "secret"
1198- >>> bq0.build.archive.private = True
1199+ >>> from lp.soyuz.interfaces.build import IBuildSet
1200+ >>> build = getUtility(IBuildSet).getByQueueEntry(bq0)
1201+ >>> build.archive.buildd_secret = "secret"
1202+ >>> build.archive.private = True
1203 >>> bq0.score()
1204 >>> bq0.lastscore
1205 12515
1206@@ -96,19 +98,19 @@
1207 IArchive.relative_build_score to boost by 100 changes the lastscore value
1208 appropriately.
1209
1210- >>> bq0.build.archive.relative_build_score = 100
1211+ >>> build.archive.relative_build_score = 100
1212 >>> bq0.score()
1213 >>> bq0.lastscore
1214 12615
1215
1216 The delta can also be negative.
1217
1218- >>> bq0.build.archive.relative_build_score = -100
1219+ >>> build.archive.relative_build_score = -100
1220 >>> bq0.score()
1221 >>> bq0.lastscore
1222 12415
1223
1224- >>> bq0.build.archive.relative_build_score = 0
1225+ >>> build.archive.relative_build_score = 0
1226 >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
1227
1228
1229@@ -250,9 +252,15 @@
1230 they all have a fixed score of -10. They will get built in the order
1231 they were created.
1232
1233- >>> from canonical.launchpad.interfaces import ArchivePurpose
1234- >>> bqc = setUpBuildQueueEntry()
1235- >>> bqc.build.archive.purpose = ArchivePurpose.COPY
1236+ >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
1237+ >>> from canonical.launchpad.interfaces import (
1238+ ... ArchivePurpose, IArchiveSet)
1239+ >>> copy = getUtility(IArchiveSet).new(
1240+ ... owner=ubuntu.owner, purpose=ArchivePurpose.COPY,
1241+ ... name='test-rebuild')
1242+
1243+ >>> bqc = setUpBuildQueueEntry(archive=copy)
1244+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqc)
1245 >>> bqc.score()
1246 >>> bqc.lastscore
1247 -10
1248
1249=== modified file 'lib/lp/soyuz/doc/buildd-slavescanner.txt'
1250--- lib/lp/soyuz/doc/buildd-slavescanner.txt 2009-11-05 10:51:36 +0000
1251+++ lib/lp/soyuz/doc/buildd-slavescanner.txt 2009-11-13 20:43:23 +0000
1252@@ -188,10 +188,11 @@
1253 To make testing easier we provide a convenience function to put a BuildQueue
1254 object into a preset fixed state:
1255
1256+ >>> from zope.security.proxy import removeSecurityProxy
1257 >>> default_start = datetime.datetime(2005, 1, 1, 8, 0, 0, tzinfo=UTC)
1258 >>> def setupBuildQueue(build_queue, builder):
1259 ... build_queue.builder = builder
1260- ... build_queue.buildstart = default_start
1261+ ... build_queue.setDateStarted(default_start)
1262
1263 Remove any previous buildmaster ROOT directory, to avoid any garbage
1264 lock conflict (it would be recreated automatically if necessary)
1265@@ -216,7 +217,7 @@
1266 >>> from canonical.launchpad.ftests import syncUpdate
1267 >>> if a_builder.currentjob is not None:
1268 ... currentjob = a_builder.currentjob
1269- ... currentjob.buildstart = None
1270+ ... currentjob.setDateStarted(None)
1271 ... currentjob.builder = None
1272 ... syncUpdate(currentjob)
1273
1274@@ -236,17 +237,18 @@
1275 Do the test execution:
1276
1277 >>> buildergroup.updateBuild(bqItem3)
1278- >>> bqItem3.build.builder is not None
1279- True
1280- >>> bqItem3.build.datebuilt is not None
1281- True
1282- >>> bqItem3.build.buildduration is not None
1283- True
1284- >>> bqItem3.build.buildlog is not None
1285+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem3)
1286+ >>> build.builder is not None
1287+ True
1288+ >>> build.datebuilt is not None
1289+ True
1290+ >>> build.buildduration is not None
1291+ True
1292+ >>> build.buildlog is not None
1293 True
1294 >>> check_mail_sent(last_stub_mail_count)
1295 True
1296- >>> bqItem3.build.buildstate.title
1297+ >>> build.buildstate.title
1298 'Failed to build'
1299
1300 Cleanup in preparation for the next test:
1301@@ -270,19 +272,20 @@
1302
1303 >>> buildergroup.updateBuild(bqItem4)
1304 CRITICAL:root:***** bob is MANUALDEPWAIT *****
1305- >>> bqItem4.build.builder is not None
1306- True
1307- >>> bqItem4.build.datebuilt is not None
1308- True
1309- >>> bqItem4.build.buildduration is not None
1310- True
1311- >>> bqItem4.build.buildlog is not None
1312+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem4)
1313+ >>> build.builder is not None
1314+ True
1315+ >>> build.datebuilt is not None
1316+ True
1317+ >>> build.buildduration is not None
1318+ True
1319+ >>> build.buildlog is not None
1320 True
1321 >>> check_mail_sent(last_stub_mail_count)
1322 False
1323- >>> bqItem4.build.dependencies
1324+ >>> build.dependencies
1325 u'baz (>= 1.0.1)'
1326- >>> bqItem4.build.buildstate.title
1327+ >>> build.buildstate.title
1328 'Dependency wait'
1329
1330 Cleanup in preparation for the next test:
1331@@ -302,17 +305,18 @@
1332 ... WaitingSlave('BuildStatus.CHROOTFAIL'))
1333 >>> buildergroup.updateBuild(bqItem5)
1334 CRITICAL:root:***** bob is CHROOTWAIT *****
1335- >>> bqItem5.build.builder is not None
1336- True
1337- >>> bqItem5.build.datebuilt is not None
1338- True
1339- >>> bqItem5.build.buildduration is not None
1340- True
1341- >>> bqItem5.build.buildlog is not None
1342+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem5)
1343+ >>> build.builder is not None
1344+ True
1345+ >>> build.datebuilt is not None
1346+ True
1347+ >>> build.buildduration is not None
1348+ True
1349+ >>> build.buildlog is not None
1350 True
1351 >>> check_mail_sent(last_stub_mail_count)
1352 True
1353- >>> bqItem5.build.buildstate.title
1354+ >>> build.buildstate.title
1355 'Chroot problem'
1356
1357 Cleanup in preparation for the next test:
1358@@ -343,7 +347,8 @@
1359 True
1360 >>> check_mail_sent(last_stub_mail_count)
1361 False
1362- >>> bqItem6.build.buildstate.title
1363+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem6)
1364+ >>> build.buildstate.title
1365 'Needs building'
1366
1367 Cleanup in preparation for the next test:
1368@@ -384,6 +389,8 @@
1369 >>> setupBuildQueue(bqItem8, a_builder)
1370 >>> last_stub_mail_count = len(stub.test_emails)
1371
1372+ >>> bqItem8.builder.setSlaveForTesting(BuildingSlave())
1373+ >>> buildergroup.updateBuild(bqItem8)
1374 >>> bqItem8.builder.setSlaveForTesting(AbortedSlave())
1375 >>> bqItem8.builder.name
1376 u'bob'
1377@@ -445,17 +452,18 @@
1378 FAILEDTOUPLOAD:
1379
1380 >>> buildergroup.updateBuild(bqItem10)
1381- >>> bqItem10.build.builder is not None
1382- True
1383- >>> bqItem10.build.datebuilt is not None
1384- True
1385- >>> bqItem10.build.buildduration is not None
1386- True
1387- >>> bqItem10.build.buildlog is not None
1388+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem10)
1389+ >>> build.builder is not None
1390+ True
1391+ >>> build.datebuilt is not None
1392+ True
1393+ >>> build.buildduration is not None
1394+ True
1395+ >>> build.buildlog is not None
1396 True
1397 >>> check_mail_sent(last_stub_mail_count)
1398 True
1399- >>> bqItem10.build.buildstate.title
1400+ >>> build.buildstate.title
1401 'Failed to upload'
1402
1403 Let's check the emails generated by this 'failure'
1404@@ -493,7 +501,7 @@
1405 output is both emailed in an immediate notification, and stored in the
1406 librarian for future reference.
1407
1408- >>> bqItem10.build.upload_log is not None
1409+ >>> build.upload_log is not None
1410 True
1411
1412 What we can clearly notice is that the buildlog is still containing
1413@@ -514,16 +522,16 @@
1414
1415 >>> bqItem10 = getUtility(IBuildSet).getByBuildID(
1416 ... 6).createBuildQueueEntry()
1417+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem10)
1418
1419 XXX: The pocket attribute is not intended to be changed in regular code, but
1420 for this test we want to change it on the fly. An alternative would be to add
1421 new sample data for a build that can be uploaded with binary packages attached
1422 to it.
1423
1424- >>> from zope.security.proxy import removeSecurityProxy
1425 >>> from lp.registry.interfaces.pocket import PackagePublishingPocket
1426 >>> removeSecurityProxy(
1427- ... bqItem10.build).pocket = PackagePublishingPocket.UPDATES
1428+ ... build).pocket = PackagePublishingPocket.UPDATES
1429 >>> setupBuildQueue(bqItem10, a_builder)
1430 >>> last_stub_mail_count = len(stub.test_emails)
1431
1432@@ -535,22 +543,22 @@
1433 the build record to FULLYBUILT, as the process-upload would do:
1434
1435 >>> from canonical.launchpad.interfaces import BuildStatus
1436- >>> bqItem10.build.buildstate = BuildStatus.FULLYBUILT
1437+ >>> build.buildstate = BuildStatus.FULLYBUILT
1438
1439 Now the updateBuild should recognize this build record as a
1440 Successfully built and uploaded procedure, not sending any
1441 notification and updating the build information:
1442
1443 >>> buildergroup.updateBuild(bqItem10)
1444- >>> bqItem10.build.builder is not None
1445- True
1446- >>> bqItem10.build.datebuilt is not None
1447- True
1448- >>> bqItem10.build.buildduration is not None
1449- True
1450- >>> bqItem10.build.buildlog is not None
1451- True
1452- >>> bqItem10.build.buildstate.title
1453+ >>> build.builder is not None
1454+ True
1455+ >>> build.datebuilt is not None
1456+ True
1457+ >>> build.buildduration is not None
1458+ True
1459+ >>> build.buildlog is not None
1460+ True
1461+ >>> build.buildstate.title
1462 'Successfully built'
1463 >>> check_mail_sent(last_stub_mail_count)
1464 False
1465@@ -558,7 +566,7 @@
1466 We do not store any build log information when the binary upload
1467 processing succeeded.
1468
1469- >>> bqItem10.build.upload_log is None
1470+ >>> build.upload_log is None
1471 True
1472
1473 Cleanup in preparation for the next test:
1474@@ -585,13 +593,14 @@
1475
1476 >>> bqItem11.builder is None
1477 True
1478- >>> bqItem11.buildstart is None
1479+ >>> bqItem11.date_started is None
1480 True
1481 >>> bqItem11.lastscore
1482 0
1483 >>> check_mail_sent(last_stub_mail_count)
1484 False
1485- >>> bqItem11.build.buildstate.title
1486+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem11)
1487+ >>> build.buildstate.title
1488 'Needs building'
1489
1490 Cleanup in preparation for the next test:
1491@@ -790,11 +799,11 @@
1492 tests.
1493
1494 >>> current_job = a_builder.currentjob
1495- >>> resurrect_build = current_job.build
1496+ >>> resurrect_build = getUtility(IBuildSet).getByQueueEntry(current_job)
1497 >>> resurrect_build.buildstate = BuildStatus.NEEDSBUILD
1498 >>> syncUpdate(resurrect_build)
1499 >>> current_job.builder = None
1500- >>> current_job.buildstart = None
1501+ >>> current_job.setDateStarted(None)
1502 >>> syncUpdate(current_job)
1503
1504 IBuilder.findCandidate also identifies if there are builds for
1505@@ -802,7 +811,8 @@
1506 corresponding build record as SUPERSEDED.
1507
1508 >>> old_candidate = a_builder.findBuildCandidate()
1509- >>> print old_candidate.build.buildstate.name
1510+ >>> build = getUtility(IBuildSet).getByQueueEntry(old_candidate)
1511+ >>> print build.buildstate.name
1512 NEEDSBUILD
1513
1514 The 'candidate' is constant until we dispatch it.
1515@@ -814,7 +824,7 @@
1516 Now let's disable the archive of the associated build record and see
1517 whether the candidate will still be found.
1518
1519- >>> old_candidate.build.archive.enabled = False
1520+ >>> build.archive.enabled = False
1521 >>> new_candidate = a_builder.findBuildCandidate()
1522 >>> new_candidate is None
1523 True
1524@@ -823,7 +833,7 @@
1525 archives are ignored. Now let's re-enable that archive and the build
1526 candidate will be found again.
1527
1528- >>> old_candidate.build.archive.enabled = True
1529+ >>> build.archive.enabled = True
1530 >>> new_candidate = a_builder.findBuildCandidate()
1531 >>> new_candidate.id == old_candidate.id
1532 True
1533@@ -836,9 +846,9 @@
1534 >>> from canonical.launchpad.interfaces import PackagePublishingStatus
1535 >>> from canonical.testing.layers import LaunchpadZopelessLayer
1536
1537- >>> spr = old_candidate.build.sourcepackagerelease
1538+ >>> spr = build.sourcepackagerelease
1539 >>> secure_pub = removeSecurityProxy(
1540- ... old_candidate).build.current_source_publication.secure_record
1541+ ... build).current_source_publication.secure_record
1542 >>> commit()
1543 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
1544 >>> secure_pub.status = PackagePublishingStatus.SUPERSEDED
1545@@ -854,7 +864,7 @@
1546 Because the 'previous' candidate was marked as superseded, so it's not
1547 part of the candidates list anymore.
1548
1549- >>> print old_candidate.build.buildstate.name
1550+ >>> print build.buildstate.name
1551 SUPERSEDED
1552
1553 If the candidate is for a private build whose source has not been
1554@@ -862,9 +872,10 @@
1555 published. We need to tweak the status of the publishing record again
1556 to demonstrate this, and also make the archive private:
1557
1558- >>> source = new_candidate.build.sourcepackagerelease
1559+ >>> build = getUtility(IBuildSet).getByQueueEntry(new_candidate)
1560+ >>> source = build.sourcepackagerelease
1561 >>> secure_pub = removeSecurityProxy(
1562- ... new_candidate).build.current_source_publication.secure_record
1563+ ... build).current_source_publication.secure_record
1564 >>> commit()
1565 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
1566 >>> secure_pub.status = PackagePublishingStatus.PENDING
1567@@ -903,22 +914,23 @@
1568
1569 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
1570 >>> secure_pub = removeSecurityProxy(
1571- ... new_candidate).build.current_source_publication.secure_record
1572+ ... build).current_source_publication.secure_record
1573 >>> secure_pub.status = PackagePublishingStatus.DELETED
1574 >>> secure_pub = removeSecurityProxy(
1575- ... new_candidate).build.current_source_publication.secure_record
1576+ ... build).current_source_publication.secure_record
1577 >>> secure_pub.status = PackagePublishingStatus.SUPERSEDED
1578 >>> commit()
1579 >>> LaunchpadZopelessLayer.switchDbUser(config.builddmaster.dbuser)
1580
1581- >>> print current_job.build.buildstate.name
1582+ >>> build = getUtility(IBuildSet).getByQueueEntry(current_job)
1583+ >>> print build.buildstate.name
1584 NEEDSBUILD
1585
1586 >>> another_candidate = a_builder.findBuildCandidate()
1587 >>> print another_candidate
1588 None
1589
1590- >>> print current_job.build.buildstate.name
1591+ >>> print build.buildstate.name
1592 SUPERSEDED
1593
1594 We'll reset the archive back to non-private for further tests:
1595@@ -1147,7 +1159,8 @@
1596 >>> cprov_archive.private = True
1597 >>> cprov_archive.buildd_secret = "secret"
1598 >>> cprov_archive.require_virtualized = False
1599- >>> for build_file in candidate.files:
1600+ >>> build = getUtility(IBuildSet).getByQueueEntry(candidate)
1601+ >>> for build_file in build.sourcepackagerelease.files:
1602 ... removeSecurityProxy(build_file).libraryfile.restricted = True
1603 >>> commit()
1604 >>> LaunchpadZopelessLayer.switchDbUser(test_dbuser)
1605@@ -1169,7 +1182,8 @@
1606 archive and not the one from the PPA, which on the absence of ancestry
1607 defaults to 'universe'.
1608
1609- >>> print candidate.build.current_component.name
1610+ >>> build = getUtility(IBuildSet).getByQueueEntry(candidate)
1611+ >>> print build.current_component.name
1612 main
1613
1614 This is so that the mangling tools will run over the built packages.
1615@@ -1196,7 +1210,7 @@
1616 We will create an ancestry in the primary archive target to the 'main'
1617 component and this time the dispatching will follow that component.
1618
1619- >>> sourcename = candidate.build.sourcepackagerelease.name
1620+ >>> sourcename = build.sourcepackagerelease.name
1621
1622 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
1623 >>> login('foo.bar@canonical.com')
1624@@ -1227,14 +1241,14 @@
1625
1626 >>> candidate = a_build.createBuildQueueEntry()
1627 >>> setupBuildQueue(candidate, a_builder)
1628- >>> candidate.build.upload_log = None
1629+ >>> build.upload_log = None
1630 >>> candidate.builder.setSlaveForTesting(WaitingSlave('BuildStatus.OK'))
1631 >>> buildergroup.updateBuild(candidate)
1632
1633- >>> candidate.build.archive.private
1634+ >>> build.archive.private
1635 True
1636
1637- >>> lfa = candidate.build.buildlog
1638+ >>> lfa = build.buildlog
1639 >>> lfa.restricted
1640 True
1641 >>> print lfa.filename
1642@@ -1269,7 +1283,8 @@
1643
1644 >>> cprov_archive.private = False
1645 >>> cprov_archive.require_virtualized = True
1646- >>> for build_file in candidate.files:
1647+ >>> build = getUtility(IBuildSet).getByQueueEntry(candidate)
1648+ >>> for build_file in build.sourcepackagerelease.files:
1649 ... removeSecurityProxy(build_file).libraryfile.restricted = False
1650 >>> mark_archive = getUtility(IPersonSet).getByName('mark').archive
1651
1652@@ -1388,7 +1403,8 @@
1653 >>> a_builder.currentjob.destroySelf()
1654
1655 >>> bqItem3 = a_build.createBuildQueueEntry()
1656- >>> removeSecurityProxy(bqItem3.build).pocket = (
1657+ >>> build = getUtility(IBuildSet).getByQueueEntry(bqItem3)
1658+ >>> removeSecurityProxy(build).pocket = (
1659 ... PackagePublishingPocket.UPDATES)
1660 >>> last_stub_mail_count = len(stub.test_emails)
1661 >>> a_builder.dispatchBuildCandidate(bqItem3)
1662@@ -1410,7 +1426,7 @@
1663 >>> a_builder.currentjob.destroySelf()
1664
1665 >>> bqItem3 = a_build.createBuildQueueEntry()
1666- >>> removeSecurityProxy(bqItem3.build).pocket = (
1667+ >>> removeSecurityProxy(build).pocket = (
1668 ... PackagePublishingPocket.PROPOSED)
1669 >>> last_stub_mail_count = len(stub.test_emails)
1670 >>> a_builder.dispatchBuildCandidate(bqItem3)
1671@@ -1433,7 +1449,7 @@
1672 >>> a_builder.currentjob.destroySelf()
1673
1674 >>> bqItem3 = a_build.createBuildQueueEntry()
1675- >>> removeSecurityProxy(bqItem3.build).pocket = (
1676+ >>> removeSecurityProxy(build).pocket = (
1677 ... PackagePublishingPocket.BACKPORTS)
1678 >>> last_stub_mail_count = len(stub.test_emails)
1679 >>> a_builder.dispatchBuildCandidate(bqItem3)
1680@@ -1456,9 +1472,9 @@
1681 >>> a_builder.currentjob.destroySelf()
1682
1683 >>> bqItem3 = a_build.createBuildQueueEntry()
1684- >>> removeSecurityProxy(bqItem3.build).buildstate = (
1685+ >>> removeSecurityProxy(build).buildstate = (
1686 ... BuildStatus.NEEDSBUILD)
1687- >>> removeSecurityProxy(bqItem3.build).pocket = (
1688+ >>> removeSecurityProxy(build).pocket = (
1689 ... PackagePublishingPocket.SECURITY)
1690 >>> last_stub_mail_count = len(stub.test_emails)
1691
1692
1693=== modified file 'lib/lp/soyuz/doc/builder.txt'
1694--- lib/lp/soyuz/doc/builder.txt 2009-08-27 19:09:44 +0000
1695+++ lib/lp/soyuz/doc/builder.txt 2009-11-13 20:43:23 +0000
1696@@ -45,8 +45,10 @@
1697
1698 >>> from lp.soyuz.interfaces.archive import ArchivePurpose
1699 >>> from zope.security.proxy import removeSecurityProxy
1700+ >>> from lp.soyuz.interfaces.build import IBuildSet
1701+ >>> build = getUtility(IBuildSet).getByQueueEntry(builder.currentjob)
1702 >>> builder_archive = removeSecurityProxy(
1703- ... builder.currentjob.build.archive)
1704+ ... build.archive)
1705 >>> saved_purpose = builder_archive.purpose
1706 >>> builder_archive.purpose = ArchivePurpose.COPY
1707
1708
1709=== modified file 'lib/lp/soyuz/doc/buildqueue.txt'
1710--- lib/lp/soyuz/doc/buildqueue.txt 2009-08-28 07:34:44 +0000
1711+++ lib/lp/soyuz/doc/buildqueue.txt 2009-11-13 20:43:23 +0000
1712@@ -31,19 +31,21 @@
1713 The IBuild record related to this job is provided by the 'build'
1714 attribute:
1715
1716- >>> bq.build.id
1717+ >>> from lp.soyuz.interfaces.build import IBuildSet
1718+ >>> build = getUtility(IBuildSet).getByQueueEntry(bq)
1719+ >>> build.id
1720 8
1721- >>> bq.build.buildstate.name
1722+ >>> build.buildstate.name
1723 'BUILDING'
1724
1725 The static timestamps, representing when the record was initialised
1726 (inserted) and when the job was dispatched are provided as datetime
1727 instances:
1728
1729- >>> bq.created
1730+ >>> bq.job.date_created
1731 datetime.datetime(2005, 6, 15, 9, 14, 12, 820778, tzinfo=<UTC>)
1732
1733- >>> bq.buildstart
1734+ >>> bq.date_started
1735 datetime.datetime(2005, 6, 15, 9, 20, 12, 820778, tzinfo=<UTC>)
1736
1737 Check Builder foreign key, which indicated which builder 'is processing'
1738@@ -77,29 +79,6 @@
1739 >>> bq.manual
1740 False
1741
1742-BuildQueue provides a property which calculates the partial duration
1743-of the build procedure (NOW - buildstart), it's mainly used in the UI.
1744-
1745- >>> bq.buildduration
1746- datetime.timedelta(...)
1747-
1748-Some local properties inherited from related content classes:
1749-
1750- >>> bq.archseries.id == bq.build.distroarchseries.id
1751- True
1752- >>> bq.urgency == bq.build.sourcepackagerelease.urgency
1753- True
1754- >>> bq.archhintlist == bq.build.sourcepackagerelease.architecturehintlist
1755- True
1756- >>> bq.name == bq.build.sourcepackagerelease.name
1757- True
1758- >>> bq.version == bq.build.sourcepackagerelease.version
1759- True
1760- >>> bq.files.count() == bq.build.sourcepackagerelease.files.count()
1761- True
1762- >>> bq.builddependsindep == bq.build.sourcepackagerelease.builddependsindep
1763- True
1764-
1765 BuildQueue provides the name for the logfile resulting from the build:
1766
1767 >>> bq.getLogFileName()
1768@@ -131,11 +110,12 @@
1769
1770 >>> print job.builder.name
1771 bob
1772- >>> job.buildstart is not None
1773+ >>> job.date_started is not None
1774 True
1775 >>> print job.logtail
1776 Dummy sampledata entry, not processing
1777- >>> print job.build.buildstate.name
1778+ >>> build = getUtility(IBuildSet).getByQueueEntry(job)
1779+ >>> print build.buildstate.name
1780 BUILDING
1781 >>> print job.lastscore
1782 1
1783@@ -150,11 +130,11 @@
1784
1785 >>> print job.builder
1786 None
1787- >>> print job.buildstart
1788+ >>> print job.date_started
1789 None
1790 >>> print job.logtail
1791 None
1792- >>> print job.build.buildstate.name
1793+ >>> print build.buildstate.name
1794 NEEDSBUILD
1795 >>> print job.lastscore
1796 1
1797@@ -169,9 +149,9 @@
1798
1799 >>> print job.builder.name
1800 bob
1801- >>> job.buildstart is not None
1802+ >>> job.date_started is not None
1803 True
1804- >>> print job.build.buildstate.name
1805+ >>> print build.buildstate.name
1806 BUILDING
1807
1808
1809@@ -266,7 +246,7 @@
1810 and restricted
1811
1812 >>> for bq in bqset.calculateCandidates(archseries):
1813- ... build = bq.build
1814+ ... build = getUtility(IBuildSet).getByQueueEntry(bq)
1815 ... print "%s (%s, %d)" % (build.title, bq.lastscore, bq.id)
1816 hppa build of pmount 0.1-2 in ubuntu hoary RELEASE (1500, 4)
1817 i386 build of alsa-utils 1.0.9a-4ubuntu1 in ubuntu hoary RELEASE (1000, 2)
1818@@ -298,7 +278,7 @@
1819 as intended.
1820
1821 >>> for bq in bqset.calculateCandidates(archseries):
1822- ... build = bq.build
1823+ ... build = getUtility(IBuildSet).getByQueueEntry(bq)
1824 ... print "%s (%s, %d)" % (build.title, bq.lastscore, bq.id)
1825 hppa build of pmount 0.1-2 in ubuntu hoary RELEASE (1500, 4)
1826 i386 build of alsa-utils 1.0.9a-4ubuntu1 in ubuntu hoary RELEASE (1000, 2)
1827@@ -310,7 +290,7 @@
1828
1829 >>> archseries = [hoary['hppa']]
1830 >>> for bq in bqset.calculateCandidates(archseries):
1831- ... build = bq.build
1832+ ... build = getUtility(IBuildSet).getByQueueEntry(bq)
1833 ... print "%s (%s, %d)" % (build.title, bq.lastscore, bq.id)
1834 hppa build of pmount 0.1-2 in ubuntu hoary RELEASE (1500, 4)
1835 hppa build of alsa-utils 1.0.9a-4 in ubuntu hoary RELEASE (500, 3)
1836
1837=== modified file 'lib/lp/soyuz/interfaces/build.py'
1838--- lib/lp/soyuz/interfaces/build.py 2009-10-26 18:40:04 +0000
1839+++ lib/lp/soyuz/interfaces/build.py 2009-11-13 20:43:23 +0000
1840@@ -524,6 +524,13 @@
1841 :rtype: ``dict``.
1842 """
1843
1844+ def getByQueueEntry(queue_entry):
1845+ """Return an IBuild instance for the given build queue entry.
1846+
1847+ Retrieve the only one possible build record associated with the given
1848+ build queue entry. If not found, return None.
1849+ """
1850+
1851
1852 class IBuildRescoreForm(Interface):
1853 """Form for rescoring a build."""
1854
1855=== added file 'lib/lp/soyuz/interfaces/buildpackagejob.py'
1856--- lib/lp/soyuz/interfaces/buildpackagejob.py 1970-01-01 00:00:00 +0000
1857+++ lib/lp/soyuz/interfaces/buildpackagejob.py 2009-11-13 20:43:23 +0000
1858@@ -0,0 +1,34 @@
1859+# Copyright 2009 Canonical Ltd. This software is licensed under the
1860+# GNU Affero General Public License version 3 (see the file LICENSE).
1861+
1862+# pylint: disable-msg=E0211,E0213
1863+
1864+"""BuildPackageJob interfaces."""
1865+
1866+__metaclass__ = type
1867+
1868+__all__ = [
1869+ 'IBuildPackageJob',
1870+ ]
1871+
1872+from zope.schema import Int
1873+
1874+from canonical.launchpad import _
1875+from lazr.restful.fields import Reference
1876+from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
1877+from lp.services.job.interfaces.job import IJob
1878+from lp.soyuz.interfaces.build import IBuild
1879+
1880+
1881+class IBuildPackageJob(IBuildFarmJob):
1882+ """A read-only interface for build package jobs."""
1883+ id = Int(title=_('ID'), required=True, readonly=True)
1884+
1885+ job = Reference(
1886+ IJob, title=_("Job"), required=True, readonly=True,
1887+ description=_("Data common to all job types."))
1888+
1889+ build = Reference(
1890+ IBuild, title=_("Build"),
1891+ required=True,readonly=True,
1892+ description=_("Build record associated with this job."))
1893
1894=== modified file 'lib/lp/soyuz/interfaces/buildqueue.py'
1895--- lib/lp/soyuz/interfaces/buildqueue.py 2009-06-25 04:06:00 +0000
1896+++ lib/lp/soyuz/interfaces/buildqueue.py 2009-11-13 20:43:23 +0000
1897@@ -13,6 +13,14 @@
1898 ]
1899
1900 from zope.interface import Interface, Attribute
1901+from zope.schema import Choice, Datetime
1902+
1903+from lazr.restful.fields import Reference
1904+
1905+from canonical.launchpad import _
1906+from lp.buildmaster.interfaces.buildfarmjob import (
1907+ IBuildFarmJob, BuildFarmJobType)
1908+from lp.services.job.interfaces.job import IJob
1909
1910
1911 class IBuildQueue(Interface):
1912@@ -30,59 +38,24 @@
1913 """
1914
1915 id = Attribute("Job identifier")
1916- build = Attribute("The IBuild record that originated this job")
1917 builder = Attribute("The IBuilder instance processing this job")
1918- created = Attribute("The datetime that the queue entry was created")
1919- buildstart = Attribute("The datetime of the last build attempt")
1920 logtail = Attribute("The current tail of the log of the build")
1921 lastscore = Attribute("Last score to be computed for this job")
1922 manual = Attribute("Whether or not the job was manually scored")
1923
1924- # properties inherited from related Content classes.
1925- archseries = Attribute(
1926- "DistroArchSeries target of the IBuild releated to this job.")
1927- name = Attribute(
1928- "Name of the ISourcePackageRelease releated to this job.")
1929- version = Attribute(
1930- "Version of the ISourcePackageRelease releated to this job.")
1931- files = Attribute(
1932- "Collection of files related to the ISourcePackageRelease "
1933- "releated to this job.")
1934- urgency = Attribute(
1935- "Urgency of the ISourcePackageRelease releated to this job.")
1936- archhintlist = Attribute(
1937- "architecturehintlist of the ISourcePackageRelease releated "
1938- "to this job.")
1939- builddependsindep = Attribute(
1940- "builddependsindep of the ISourcePackageRelease releated to "
1941- "this job.")
1942- buildduration = Attribute(
1943- "Duration of the job, calculated on-the-fly based on buildstart.")
1944- is_virtualized = Attribute("See IBuild.is_virtualized.")
1945+ job = Reference(
1946+ IJob, title=_("Job"), required=True, readonly=True,
1947+ description=_("Data common to all job types."))
1948+
1949+ job_type = Choice(
1950+ title=_('Job type'), required=True, vocabulary=BuildFarmJobType,
1951+ description=_("The type of this job."))
1952
1953 def manualScore(value):
1954 """Manually set a score value to a queue item and lock it."""
1955
1956 def score():
1957- """Perform scoring based on heuristic values.
1958-
1959- Creates a 'score' (priority) value based on:
1960-
1961- * Component: main component gets higher values
1962- (main, 1000, restricted, 750, universe, 250, multiverse, 0)
1963-
1964- * Urgency: EMERGENCY sources gets higher values
1965- (EMERGENCY, 20, HIGH, 15, MEDIUM, 10, LOW, 5)
1966-
1967- * Queue time: old records gets a relative higher priority
1968- (The rate against component is something like: a 'multiverse'
1969- build will be as important as a 'main' after 40 hours in queue)
1970-
1971- This method automatically updates IBuildQueue.lastscore value and
1972- skips 'manually-scored' records.
1973-
1974- This method use any logger available in the standard logging system.
1975- """
1976+ """The job score calculated for the job type in question."""
1977
1978 def destroySelf():
1979 """Delete this entry from the database."""
1980@@ -121,6 +94,17 @@
1981 Clean the builder for another jobs.
1982 """
1983
1984+ specific_job = Reference(
1985+ IBuildFarmJob, title=_("Job"),
1986+ description=_("Data and operations common to all build farm jobs."))
1987+
1988+ def setDateStarted(timestamp):
1989+ """Sets the date started property to the given value."""
1990+
1991+ date_started = Datetime(
1992+ title=_('Start time'),
1993+ description=_('Time when the job started.'))
1994+
1995
1996 class IBuildQueueSet(Interface):
1997 """Launchpad Auto Build queue set handler and auxiliary methods."""
1998@@ -165,4 +149,3 @@
1999 Retrieve the build queue and related builder rows associated with the
2000 builds in question where they exist.
2001 """
2002-
2003
2004=== modified file 'lib/lp/soyuz/model/build.py'
2005--- lib/lp/soyuz/model/build.py 2009-10-26 18:40:04 +0000
2006+++ lib/lp/soyuz/model/build.py 2009-11-13 20:43:23 +0000
2007@@ -18,7 +18,6 @@
2008 from zope.security.proxy import removeSecurityProxy
2009 from storm.expr import (
2010 Desc, In, Join, LeftJoin)
2011-from storm.references import Reference
2012 from storm.store import Store
2013 from sqlobject import (
2014 StringCol, ForeignKey, IntervalCol, SQLObjectNotFound)
2015@@ -46,7 +45,9 @@
2016 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
2017 from canonical.launchpad.webapp.tales import DurationFormatterAPI
2018 from lp.archivepublisher.utils import get_ppa_reference
2019+from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
2020 from lp.registry.interfaces.pocket import PackagePublishingPocket
2021+from lp.services.job.model.job import Job
2022 from lp.soyuz.adapters.archivedependencies import get_components_for_building
2023 from lp.soyuz.interfaces.archive import ArchivePurpose
2024 from lp.soyuz.interfaces.build import (
2025@@ -55,6 +56,7 @@
2026 from lp.soyuz.interfaces.publishing import active_publishing_status
2027 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
2028 from lp.soyuz.model.builder import Builder
2029+from lp.soyuz.model.buildpackagejob import BuildPackageJob
2030 from lp.soyuz.model.buildqueue import BuildQueue
2031 from lp.soyuz.model.files import BinaryPackageFile
2032 from lp.soyuz.model.publishing import SourcePackagePublishingHistory
2033@@ -88,8 +90,6 @@
2034 archive = ForeignKey(foreignKey='Archive', dbName='archive', notNull=True)
2035 estimated_build_duration = IntervalCol(default=None)
2036
2037- buildqueue_record = Reference("<primary key>", BuildQueue.buildID,
2038- on_remote=True)
2039 date_first_dispatched = UtcDateTimeCol(dbName='date_first_dispatched')
2040
2041 upload_log = ForeignKey(
2042@@ -105,6 +105,16 @@
2043 return proxied_file.http_url
2044
2045 @property
2046+ def buildqueue_record(self):
2047+ """See `IBuild`."""
2048+ store = Store.of(self)
2049+ results = store.find(
2050+ BuildQueue,
2051+ BuildPackageJob.job == BuildQueue.jobID,
2052+ BuildPackageJob.build == self.id)
2053+ return results.one()
2054+
2055+ @property
2056 def upload_log_url(self):
2057 """See `IBuild`."""
2058 if self.upload_log is None:
2059@@ -351,8 +361,10 @@
2060 Archive
2061 JOIN Build ON
2062 Build.archive = Archive.id
2063+ JOIN BuildPackageJob ON
2064+ Build.id = BuildPackageJob.build
2065 JOIN BuildQueue ON
2066- Build.id = BuildQueue.build
2067+ BuildPackageJob.job = BuildQueue.job
2068 WHERE
2069 Build.buildstate = 0 AND
2070 Build.processor = %s AND
2071@@ -412,16 +424,20 @@
2072 SELECT
2073 CAST (EXTRACT(EPOCH FROM
2074 (Build.estimated_build_duration -
2075- (NOW() - BuildQueue.buildstart))) AS INTEGER)
2076+ (NOW() - Job.date_started))) AS INTEGER)
2077 AS remainder
2078 FROM
2079 Archive
2080 JOIN Build ON
2081 Build.archive = Archive.id
2082+ JOIN BuildPackageJob ON
2083+ Build.id = BuildPackageJob.build
2084 JOIN BuildQueue ON
2085- Build.id = BuildQueue.build
2086+ BuildQueue.job = BuildPackageJob.job
2087 JOIN Builder ON
2088 Builder.id = BuildQueue.builder
2089+ JOIN Job ON
2090+ Job.id = BuildPackageJob.job
2091 WHERE
2092 Archive.require_virtualized = %s AND
2093 Archive.enabled = TRUE AND
2094@@ -605,7 +621,18 @@
2095
2096 def createBuildQueueEntry(self):
2097 """See `IBuild`"""
2098- return BuildQueue(build=self)
2099+ store = Store.of(self)
2100+ job = Job()
2101+ store.add(job)
2102+ specific_job = BuildPackageJob()
2103+ specific_job.build = self.id
2104+ specific_job.job = job.id
2105+ store.add(specific_job)
2106+ queue_entry = BuildQueue()
2107+ queue_entry.job = job.id
2108+ queue_entry.job_type = BuildFarmJobType.PACKAGEBUILD
2109+ store.add(queue_entry)
2110+ return queue_entry
2111
2112 def notify(self, extra_info=None):
2113 """See `IBuild`"""
2114@@ -966,7 +993,9 @@
2115 if status in [BuildStatus.NEEDSBUILD, BuildStatus.BUILDING]:
2116 orderBy = ["-BuildQueue.lastscore", "Build.id"]
2117 clauseTables.append('BuildQueue')
2118- condition_clauses.append('BuildQueue.build = Build.id')
2119+ clauseTables.append('BuildPackageJob')
2120+ condition_clauses.append('BuildPackageJob.build = Build.id')
2121+ condition_clauses.append('BuildPackageJob.job = BuildQueue.job')
2122 elif status == BuildStatus.SUPERSEDED or status is None:
2123 orderBy = ["-Build.datecreated"]
2124 else:
2125@@ -1144,3 +1173,13 @@
2126 # this (pre_iter_hook()) method that will iterate over the
2127 # result set and force the query execution that way.
2128 return list(result_set)
2129+
2130+ def getByQueueEntry(self, queue_entry):
2131+ """See `IBuildSet`."""
2132+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
2133+ result_set = store.find(
2134+ Build,
2135+ BuildPackageJob.build == Build.id,
2136+ BuildPackageJob.job == queue_entry.job)
2137+
2138+ return result_set.one()
2139
2140=== modified file 'lib/lp/soyuz/model/builder.py'
2141--- lib/lp/soyuz/model/builder.py 2009-11-11 10:43:07 +0000
2142+++ lib/lp/soyuz/model/builder.py 2009-11-13 20:43:22 +0000
2143@@ -52,6 +52,7 @@
2144 from lp.soyuz.interfaces.buildqueue import IBuildQueueSet
2145 from lp.soyuz.interfaces.publishing import (
2146 PackagePublishingStatus)
2147+from lp.soyuz.model.buildpackagejob import BuildPackageJob
2148 from canonical.launchpad.webapp import urlappend
2149 from canonical.librarian.utils import copy_and_close
2150
2151@@ -151,11 +152,11 @@
2152 # Avoid circular imports.
2153 from lp.soyuz.model.publishing import makePoolPath
2154
2155- build = build_queue_item.build
2156+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
2157 archive = build.archive
2158 archive_url = archive.archive_url
2159 component_name = build.current_component.name
2160- for source_file in build_queue_item.files:
2161+ for source_file in build.sourcepackagerelease.files:
2162 file_name = source_file.libraryfile.filename
2163 sha1 = source_file.libraryfile.content.sha1
2164 source_name = build.sourcepackagerelease.sourcepackagename.name
2165@@ -264,8 +265,8 @@
2166 * Ensure that the build pocket allows builds for the current
2167 distroseries state.
2168 """
2169- assert not (not self.virtualized and
2170- build_queue_item.is_virtualized), (
2171+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
2172+ assert not (not self.virtualized and build.is_virtualized), (
2173 "Attempt to build non-virtual item on a virtual builder.")
2174
2175 # Assert that we are not silently building SECURITY jobs.
2176@@ -274,27 +275,27 @@
2177 # XXX Julian 2007-12-18 spec=security-in-soyuz: This is being
2178 # addressed in the work on the blueprint:
2179 # https://blueprints.launchpad.net/soyuz/+spec/security-in-soyuz
2180- target_pocket = build_queue_item.build.pocket
2181+ target_pocket = build.pocket
2182 assert target_pocket != PackagePublishingPocket.SECURITY, (
2183 "Soyuz is not yet capable of building SECURITY uploads.")
2184
2185 # Ensure build has the needed chroot
2186- chroot = build_queue_item.archseries.getChroot()
2187+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
2188+ chroot = build.distroarchseries.getChroot()
2189 if chroot is None:
2190 raise CannotBuild(
2191 "Missing CHROOT for %s/%s/%s" % (
2192- build_queue_item.build.distroseries.distribution.name,
2193- build_queue_item.build.distroseries.name,
2194- build_queue_item.build.distroarchseries.architecturetag)
2195+ build.distroseries.distribution.name,
2196+ build.distroseries.name,
2197+ build.distroarchseries.architecturetag)
2198 )
2199
2200 # The main distribution has policies to prevent uploads to some
2201 # pockets (e.g. security) during different parts of the distribution
2202 # series lifecycle. These do not apply to PPA builds nor any archive
2203 # that allows release pocket updates.
2204- if (build_queue_item.build.archive.purpose != ArchivePurpose.PPA and
2205- not build_queue_item.build.archive.allowUpdatesToReleasePocket()):
2206- build = build_queue_item.build
2207+ if (build.archive.purpose != ArchivePurpose.PPA and
2208+ not build.archive.allowUpdatesToReleasePocket()):
2209 # XXX Robert Collins 2007-05-26: not an explicit CannotBuild
2210 # exception yet because the callers have not been audited
2211 assert build.distroseries.canUploadToPocket(build.pocket), (
2212@@ -306,7 +307,8 @@
2213 def _dispatchBuildToSlave(self, build_queue_item, args, buildid, logger):
2214 """Start the build on the slave builder."""
2215 # Send chroot.
2216- chroot = build_queue_item.archseries.getChroot()
2217+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
2218+ chroot = build.distroarchseries.getChroot()
2219 self.cacheFileOnSlave(logger, chroot)
2220
2221 # Build filemap structure with the files required in this build
2222@@ -314,11 +316,11 @@
2223 # If the build is private we tell the slave to get the files from the
2224 # archive instead of the librarian because the slaves cannot
2225 # access the restricted librarian.
2226- private = build_queue_item.build.archive.private
2227+ private = build.archive.private
2228 if private:
2229 self.cachePrivateSourceOnSlave(logger, build_queue_item)
2230 filemap = {}
2231- for source_file in build_queue_item.files:
2232+ for source_file in build.sourcepackagerelease.files:
2233 lfa = source_file.libraryfile
2234 filemap[lfa.filename] = lfa.content.sha1
2235 if not private:
2236@@ -349,9 +351,10 @@
2237
2238 def startBuild(self, build_queue_item, logger):
2239 """See IBuilder."""
2240+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
2241+ spr = build.sourcepackagerelease
2242 logger.info("startBuild(%s, %s, %s, %s)", self.url,
2243- build_queue_item.name, build_queue_item.version,
2244- build_queue_item.build.pocket.title)
2245+ spr.name, spr.version, build.pocket.title)
2246
2247 # Make sure the request is valid; an exception is raised if it's not.
2248 self._verifyBuildRequest(build_queue_item, logger)
2249@@ -365,39 +368,39 @@
2250 # turn 'arch_indep' ON only if build is archindep or if
2251 # the specific architecture is the nominatedarchindep for
2252 # this distroseries (in case it requires any archindep source)
2253- args['arch_indep'] = build_queue_item.archseries.isNominatedArchIndep
2254+ build = getUtility(IBuildSet).getByQueueEntry(build_queue_item)
2255+ args['arch_indep'] = build.distroarchseries.isNominatedArchIndep
2256
2257- suite = build_queue_item.build.distroarchseries.distroseries.name
2258- if build_queue_item.build.pocket != PackagePublishingPocket.RELEASE:
2259- suite += "-%s" % (build_queue_item.build.pocket.name.lower())
2260+ suite = build.distroarchseries.distroseries.name
2261+ if build.pocket != PackagePublishingPocket.RELEASE:
2262+ suite += "-%s" % (build.pocket.name.lower())
2263 args['suite'] = suite
2264
2265- archive_purpose = build_queue_item.build.archive.purpose
2266+ archive_purpose = build.archive.purpose
2267 if (archive_purpose == ArchivePurpose.PPA and
2268- not build_queue_item.build.archive.require_virtualized):
2269+ not build.archive.require_virtualized):
2270 # If we're building a non-virtual PPA, override the purpose
2271 # to PRIMARY and use the primary component override.
2272 # This ensures that the package mangling tools will run over
2273 # the built packages.
2274 args['archive_purpose'] = ArchivePurpose.PRIMARY.name
2275 args["ogrecomponent"] = (
2276- get_primary_current_component(build_queue_item.build))
2277+ get_primary_current_component(build))
2278 else:
2279 args['archive_purpose'] = archive_purpose.name
2280 args["ogrecomponent"] = (
2281- build_queue_item.build.current_component.name)
2282+ build.current_component.name)
2283
2284- args['archives'] = get_sources_list_for_building(
2285- build_queue_item.build)
2286+ args['archives'] = get_sources_list_for_building(build)
2287
2288 # Let the build slave know whether this is a build in a private
2289 # archive.
2290- args['archive_private'] = build_queue_item.build.archive.private
2291+ args['archive_private'] = build.archive.private
2292
2293 # Generate a string which can be used to cross-check when obtaining
2294 # results so we know we are referring to the right database object in
2295 # subsequent runs.
2296- buildid = "%s-%s" % (build_queue_item.build.id, build_queue_item.id)
2297+ buildid = "%s-%s" % (build.id, build_queue_item.id)
2298 logger.debug("Initiating build %s on %s" % (buildid, self.url))
2299
2300 # Do it.
2301@@ -421,8 +424,9 @@
2302 if currentjob is None:
2303 return 'Idle'
2304
2305- msg = 'Building %s' % currentjob.build.title
2306- archive = currentjob.build.archive
2307+ build = getUtility(IBuildSet).getByQueueEntry(currentjob)
2308+ msg = 'Building %s' % build.title
2309+ archive = build.archive
2310 if not archive.owner.private and (archive.is_ppa or archive.is_copy):
2311 return '%s [%s/%s]' % (msg, archive.owner.name, archive.name)
2312 else:
2313@@ -553,7 +557,8 @@
2314 SourcePackagePublishingHistory.status IN %s))
2315 OR
2316 archive.private IS FALSE) AND
2317- buildqueue.build = build.id AND
2318+ buildqueue.job = buildpackagejob.job AND
2319+ buildpackagejob.build = build.id AND
2320 build.distroarchseries = distroarchseries.id AND
2321 build.archive = archive.id AND
2322 archive.enabled = TRUE AND
2323@@ -563,7 +568,8 @@
2324 """ % sqlvalues(
2325 private_statuses, BuildStatus.NEEDSBUILD, self.processor.family)]
2326
2327- clauseTables = ['Build', 'DistroArchSeries', 'Archive']
2328+ clauseTables = [
2329+ 'Build', 'BuildPackageJob', 'DistroArchSeries', 'Archive']
2330
2331 clauses.append("""
2332 archive.require_virtualized = %s
2333@@ -605,7 +611,7 @@
2334
2335 query = " AND ".join(clauses)
2336 candidate = BuildQueue.selectFirst(
2337- query, clauseTables=clauseTables, prejoins=['build'],
2338+ query, clauseTables=clauseTables,
2339 orderBy=['-buildqueue.lastscore', 'build.id'])
2340
2341 return candidate
2342@@ -629,26 +635,28 @@
2343 # Builds in those situation should not be built because they will
2344 # be wasting build-time, the former case already has a newer source
2345 # and the latter could not be built in DAK.
2346+ build_set = getUtility(IBuildSet)
2347 while candidate is not None:
2348- if candidate.build.pocket == PackagePublishingPocket.SECURITY:
2349+ build = build_set.getByQueueEntry(candidate)
2350+ if build.pocket == PackagePublishingPocket.SECURITY:
2351 # We never build anything in the security pocket.
2352 logger.debug(
2353 "Build %s FAILEDTOBUILD, queue item %s REMOVED"
2354- % (candidate.build.id, candidate.id))
2355- candidate.build.buildstate = BuildStatus.FAILEDTOBUILD
2356+ % (build.id, candidate.id))
2357+ build.buildstate = BuildStatus.FAILEDTOBUILD
2358 candidate.destroySelf()
2359 candidate = self._findBuildCandidate()
2360 continue
2361
2362- publication = candidate.build.current_source_publication
2363+ publication = build.current_source_publication
2364
2365 if publication is None:
2366 # The build should be superseded if it no longer has a
2367 # current publishing record.
2368 logger.debug(
2369 "Build %s SUPERSEDED, queue item %s REMOVED"
2370- % (candidate.build.id, candidate.id))
2371- candidate.build.buildstate = BuildStatus.SUPERSEDED
2372+ % (build.id, candidate.id))
2373+ build.buildstate = BuildStatus.SUPERSEDED
2374 candidate.destroySelf()
2375 candidate = self._findBuildCandidate()
2376 continue
2377@@ -751,13 +759,15 @@
2378 origin = (
2379 Archive,
2380 Build,
2381+ BuildPackageJob,
2382 BuildQueue,
2383 DistroArchSeries,
2384 Processor,
2385 )
2386 queue = store.using(*origin).find(
2387 BuildQueue,
2388- BuildQueue.build == Build.id,
2389+ BuildPackageJob.job == BuildQueue.jobID,
2390+ BuildPackageJob.build == Build.id,
2391 Build.distroarchseries == DistroArchSeries.id,
2392 Build.archive == Archive.id,
2393 DistroArchSeries.processorfamilyID == Processor.familyID,
2394
2395=== added file 'lib/lp/soyuz/model/buildpackagejob.py'
2396--- lib/lp/soyuz/model/buildpackagejob.py 1970-01-01 00:00:00 +0000
2397+++ lib/lp/soyuz/model/buildpackagejob.py 2009-11-13 20:43:23 +0000
2398@@ -0,0 +1,168 @@
2399+# Copyright 2009 Canonical Ltd. This software is licensed under the
2400+# GNU Affero General Public License version 3 (see the file LICENSE).
2401+
2402+__metaclass__ = type
2403+__all__ = ['BuildPackageJob']
2404+
2405+
2406+from datetime import datetime
2407+import pytz
2408+
2409+from storm.locals import Int, Reference, Storm
2410+
2411+from zope.interface import implements
2412+
2413+from canonical.database.constants import UTC_NOW
2414+from canonical.launchpad.interfaces import SourcePackageUrgency
2415+from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
2416+from lp.registry.interfaces.pocket import PackagePublishingPocket
2417+from lp.soyuz.interfaces.archive import ArchivePurpose
2418+from lp.soyuz.interfaces.build import BuildStatus
2419+from lp.soyuz.interfaces.buildpackagejob import IBuildPackageJob
2420+
2421+
2422+class BuildPackageJob(Storm):
2423+ """See `IBuildPackageJob`."""
2424+ implements(IBuildFarmJob, IBuildPackageJob)
2425+ __storm_table__ = 'buildpackagejob'
2426+ id = Int(primary=True)
2427+
2428+ job_id = Int(name='job', allow_none=False)
2429+ job = Reference(job_id, 'Job.id')
2430+
2431+ build_id = Int(name='build', allow_none=False)
2432+ build = Reference(build_id, 'Build.id')
2433+
2434+ def score(self):
2435+ """See `IBuildPackageJob`."""
2436+ score_pocketname = {
2437+ PackagePublishingPocket.BACKPORTS: 0,
2438+ PackagePublishingPocket.RELEASE: 1500,
2439+ PackagePublishingPocket.PROPOSED: 3000,
2440+ PackagePublishingPocket.UPDATES: 3000,
2441+ PackagePublishingPocket.SECURITY: 4500,
2442+ }
2443+
2444+ score_componentname = {
2445+ 'multiverse': 0,
2446+ 'universe': 250,
2447+ 'restricted': 750,
2448+ 'main': 1000,
2449+ 'partner' : 1250,
2450+ }
2451+
2452+ score_urgency = {
2453+ SourcePackageUrgency.LOW: 5,
2454+ SourcePackageUrgency.MEDIUM: 10,
2455+ SourcePackageUrgency.HIGH: 15,
2456+ SourcePackageUrgency.EMERGENCY: 20,
2457+ }
2458+
2459+ # Define a table we'll use to calculate the score based on the time
2460+ # in the build queue. The table is a sorted list of (upper time
2461+ # limit in seconds, score) tuples.
2462+ queue_time_scores = [
2463+ (14400, 100),
2464+ (7200, 50),
2465+ (3600, 20),
2466+ (1800, 15),
2467+ (900, 10),
2468+ (300, 5),
2469+ ]
2470+
2471+ private_archive_increment = 10000
2472+
2473+ # For build jobs in rebuild archives a score value of -1
2474+ # was chosen because their priority is lower than build retries
2475+ # or language-packs. They should be built only when there is
2476+ # nothing else to build.
2477+ rebuild_archive_score = -10
2478+
2479+ score = 0
2480+
2481+ # Please note: the score for language packs is to be zero because
2482+ # they unduly delay the building of packages in the main component
2483+ # otherwise.
2484+ if self.build.sourcepackagerelease.section.name == 'translations':
2485+ pass
2486+ elif self.build.archive.purpose == ArchivePurpose.COPY:
2487+ score = rebuild_archive_score
2488+ else:
2489+ # Calculates the urgency-related part of the score.
2490+ urgency = score_urgency[self.build.sourcepackagerelease.urgency]
2491+ score += urgency
2492+
2493+ # Calculates the pocket-related part of the score.
2494+ score_pocket = score_pocketname[self.build.pocket]
2495+ score += score_pocket
2496+
2497+ # Calculates the component-related part of the score.
2498+ score_component = score_componentname[
2499+ self.build.current_component.name]
2500+ score += score_component
2501+
2502+ # Calculates the build queue time component of the score.
2503+ right_now = datetime.now(pytz.timezone('UTC'))
2504+ eta = right_now - self.job.date_created
2505+ for limit, dep_score in queue_time_scores:
2506+ if eta.seconds > limit:
2507+ score += dep_score
2508+ break
2509+
2510+ # Private builds get uber score.
2511+ if self.build.archive.private:
2512+ score += private_archive_increment
2513+
2514+ # Lastly, apply the archive score delta. This is to boost
2515+ # or retard build scores for any build in a particular
2516+ # archive.
2517+ score += self.build.archive.relative_build_score
2518+
2519+ return score
2520+
2521+ def getLogFileName(self):
2522+ """See `IBuildPackageJob`."""
2523+ sourcename = self.build.sourcepackagerelease.name
2524+ version = self.build.sourcepackagerelease.version
2525+ # we rely on previous storage of current buildstate
2526+ # in the state handling methods.
2527+ state = self.build.buildstate.name
2528+
2529+ dar = self.build.distroarchseries
2530+ distroname = dar.distroseries.distribution.name
2531+ distroseriesname = dar.distroseries.name
2532+ archname = dar.architecturetag
2533+
2534+ # logfilename format:
2535+ # buildlog_<DISTRIBUTION>_<DISTROSeries>_<ARCHITECTURE>_\
2536+ # <SOURCENAME>_<SOURCEVERSION>_<BUILDSTATE>.txt
2537+ # as:
2538+ # buildlog_ubuntu_dapper_i386_foo_1.0-ubuntu0_FULLYBUILT.txt
2539+ # it fix request from bug # 30617
2540+ return ('buildlog_%s-%s-%s.%s_%s_%s.txt' % (
2541+ distroname, distroseriesname, archname, sourcename, version, state
2542+ ))
2543+
2544+ def getName(self):
2545+ """See `IBuildPackageJob`."""
2546+ return self.build.sourcepackagerelease.name
2547+
2548+ def jobStarted(self):
2549+ """See `IBuildPackageJob`."""
2550+ self.build.buildstate = BuildStatus.BUILDING
2551+ # The build started, set the start time if not set already.
2552+ if self.build.date_first_dispatched is None:
2553+ self.build.date_first_dispatched = UTC_NOW
2554+
2555+ def jobReset(self):
2556+ """See `IBuildPackageJob`."""
2557+ self.build.buildstate = BuildStatus.NEEDSBUILD
2558+
2559+ def jobAborted(self):
2560+ """See `IBuildPackageJob`."""
2561+ # XXX, al-maisan, Thu, 12 Nov 2009 16:38:52 +0100
2562+ # The setting below was "inherited" from the previous code. We
2563+ # need to investigate whether and why this is really needed and
2564+ # fix it.
2565+ self.build.buildstate = BuildStatus.BUILDING
2566+
2567
2568=== modified file 'lib/lp/soyuz/model/buildqueue.py'
2569--- lib/lp/soyuz/model/buildqueue.py 2009-08-28 06:39:38 +0000
2570+++ lib/lp/soyuz/model/buildqueue.py 2009-11-13 20:43:23 +0000
2571@@ -10,27 +10,25 @@
2572 'BuildQueueSet'
2573 ]
2574
2575-from datetime import datetime
2576 import logging
2577-import pytz
2578
2579 from zope.component import getUtility
2580 from zope.interface import implements
2581
2582 from sqlobject import (
2583 StringCol, ForeignKey, BoolCol, IntCol, SQLObjectNotFound)
2584-from storm.expr import In, LeftJoin
2585+from storm.expr import In, Join, LeftJoin
2586
2587 from canonical import encoding
2588-from canonical.database.constants import UTC_NOW
2589-from canonical.database.datetimecol import UtcDateTimeCol
2590+from canonical.database.enumcol import EnumCol
2591 from canonical.database.sqlbase import SQLBase, sqlvalues
2592 from canonical.launchpad.webapp.interfaces import NotFoundError
2593-from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
2594-from lp.soyuz.interfaces.archive import ArchivePurpose
2595-from lp.soyuz.interfaces.build import BuildStatus
2596+from lp.buildmaster.interfaces.buildfarmjob import BuildFarmJobType
2597+from lp.services.job.interfaces.job import JobStatus
2598+from lp.services.job.model.job import Job
2599+from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
2600 from lp.soyuz.interfaces.buildqueue import IBuildQueue, IBuildQueueSet
2601-from lp.registry.interfaces.pocket import PackagePublishingPocket
2602+from lp.soyuz.model.buildpackagejob import BuildPackageJob
2603 from canonical.launchpad.webapp.interfaces import (
2604 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
2605
2606@@ -40,229 +38,85 @@
2607 _table = "BuildQueue"
2608 _defaultOrder = "id"
2609
2610- build = ForeignKey(dbName='build', foreignKey='Build', notNull=True)
2611+ job = ForeignKey(dbName='job', foreignKey='Job', notNull=True)
2612+ job_type = EnumCol(
2613+ enum=BuildFarmJobType, notNull=True,
2614+ default=BuildFarmJobType.PACKAGEBUILD, dbName='job_type')
2615 builder = ForeignKey(dbName='builder', foreignKey='Builder', default=None)
2616- created = UtcDateTimeCol(dbName='created', default=UTC_NOW)
2617- buildstart = UtcDateTimeCol(dbName='buildstart', default= None)
2618 logtail = StringCol(dbName='logtail', default=None)
2619 lastscore = IntCol(dbName='lastscore', default=0)
2620 manual = BoolCol(dbName='manual', default=False)
2621
2622+ @property
2623+ def specific_job(self):
2624+ """See `IBuildQueue`."""
2625+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
2626+ result_set = store.find(
2627+ BuildPackageJob, BuildPackageJob.job == self.job)
2628+ return result_set.one()
2629+
2630+ @property
2631+ def date_started(self):
2632+ """See `IBuildQueue`."""
2633+ return self.job.date_started
2634+
2635 def manualScore(self, value):
2636 """See `IBuildQueue`."""
2637 self.lastscore = value
2638 self.manual = True
2639
2640- @property
2641- def archseries(self):
2642- """See `IBuildQueue`."""
2643- return self.build.distroarchseries
2644-
2645- @property
2646- def urgency(self):
2647- """See `IBuildQueue`."""
2648- return self.build.sourcepackagerelease.urgency
2649-
2650- @property
2651- def archhintlist(self):
2652- """See `IBuildQueue`."""
2653- return self.build.sourcepackagerelease.architecturehintlist
2654-
2655- @property
2656- def name(self):
2657- """See `IBuildQueue`."""
2658- return self.build.sourcepackagerelease.name
2659-
2660- @property
2661- def version(self):
2662- """See `IBuildQueue`."""
2663- return self.build.sourcepackagerelease.version
2664-
2665- @property
2666- def files(self):
2667- """See `IBuildQueue`."""
2668- return self.build.sourcepackagerelease.files
2669-
2670- @property
2671- def builddependsindep(self):
2672- """See `IBuildQueue`."""
2673- return self.build.sourcepackagerelease.builddependsindep
2674-
2675- @property
2676- def buildduration(self):
2677- """See `IBuildQueue`."""
2678- if self.buildstart:
2679- UTC = pytz.timezone('UTC')
2680- now = datetime.now(UTC)
2681- return now - self.buildstart
2682- return None
2683-
2684- @property
2685- def is_virtualized(self):
2686- """See `IBuildQueue`."""
2687- return self.build.is_virtualized
2688-
2689 def score(self):
2690 """See `IBuildQueue`."""
2691 # Grab any logger instance available.
2692 logger = logging.getLogger()
2693+ name = self.specific_job.getName()
2694
2695 if self.manual:
2696 logger.debug(
2697- "%s (%d) MANUALLY RESCORED" % (self.name, self.lastscore))
2698+ "%s (%d) MANUALLY RESCORED" % (name, self.lastscore))
2699 return
2700
2701- # XXX Al-maisan, 2008-05-14 (bug #230330):
2702- # We keep touching the code here whenever a modification to the
2703- # scoring parameters/weights is needed. Maybe the latter can be
2704- # externalized?
2705-
2706- score_pocketname = {
2707- PackagePublishingPocket.BACKPORTS: 0,
2708- PackagePublishingPocket.RELEASE: 1500,
2709- PackagePublishingPocket.PROPOSED: 3000,
2710- PackagePublishingPocket.UPDATES: 3000,
2711- PackagePublishingPocket.SECURITY: 4500,
2712- }
2713-
2714- score_componentname = {
2715- 'multiverse': 0,
2716- 'universe': 250,
2717- 'restricted': 750,
2718- 'main': 1000,
2719- 'partner' : 1250,
2720- }
2721-
2722- score_urgency = {
2723- SourcePackageUrgency.LOW: 5,
2724- SourcePackageUrgency.MEDIUM: 10,
2725- SourcePackageUrgency.HIGH: 15,
2726- SourcePackageUrgency.EMERGENCY: 20,
2727- }
2728-
2729- # Define a table we'll use to calculate the score based on the time
2730- # in the build queue. The table is a sorted list of (upper time
2731- # limit in seconds, score) tuples.
2732- queue_time_scores = [
2733- (14400, 100),
2734- (7200, 50),
2735- (3600, 20),
2736- (1800, 15),
2737- (900, 10),
2738- (300, 5),
2739- ]
2740-
2741- private_archive_increment = 10000
2742-
2743- # For build jobs in rebuild archives a score value of -1
2744- # was chosen because their priority is lower than build retries
2745- # or language-packs. They should be built only when there is
2746- # nothing else to build.
2747- rebuild_archive_score = -10
2748-
2749- score = 0
2750- msg = "%s (%d) -> " % (self.build.title, self.lastscore)
2751-
2752- # Please note: the score for language packs is to be zero because
2753- # they unduly delay the building of packages in the main component
2754- # otherwise.
2755- if self.build.sourcepackagerelease.section.name == 'translations':
2756- msg += "LPack => score zero"
2757- elif self.build.archive.purpose == ArchivePurpose.COPY:
2758- score = rebuild_archive_score
2759- msg += "Rebuild archive => -10"
2760- else:
2761- # Calculates the urgency-related part of the score.
2762- urgency = score_urgency[self.urgency]
2763- score += urgency
2764- msg += "U+%d " % urgency
2765-
2766- # Calculates the pocket-related part of the score.
2767- score_pocket = score_pocketname[self.build.pocket]
2768- score += score_pocket
2769- msg += "P+%d " % score_pocket
2770-
2771- # Calculates the component-related part of the score.
2772- score_component = score_componentname[
2773- self.build.current_component.name]
2774- score += score_component
2775- msg += "C+%d " % score_component
2776-
2777- # Calculates the build queue time component of the score.
2778- right_now = datetime.now(pytz.timezone('UTC'))
2779- eta = right_now - self.created
2780- for limit, dep_score in queue_time_scores:
2781- if eta.seconds > limit:
2782- score += dep_score
2783- msg += "T+%d " % dep_score
2784- break
2785- else:
2786- msg += "T+0 "
2787-
2788- # Private builds get uber score.
2789- if self.build.archive.private:
2790- score += private_archive_increment
2791-
2792- # Lastly, apply the archive score delta. This is to boost
2793- # or retard build scores for any build in a particular
2794- # archive.
2795- score += self.build.archive.relative_build_score
2796-
2797- # Store current score value.
2798- self.lastscore = score
2799-
2800- logger.debug("%s= %d" % (msg, self.lastscore))
2801+ # Allow the `IBuildFarmJob` instance with the data/logic specific to
2802+ # the job at hand to calculate the score as appropriate.
2803+ self.lastscore = self.specific_job.score()
2804
2805 def getLogFileName(self):
2806 """See `IBuildQueue`."""
2807- sourcename = self.build.sourcepackagerelease.name
2808- version = self.build.sourcepackagerelease.version
2809- # we rely on previous storage of current buildstate
2810- # in the state handling methods.
2811- state = self.build.buildstate.name
2812-
2813- dar = self.build.distroarchseries
2814- distroname = dar.distroseries.distribution.name
2815- distroseriesname = dar.distroseries.name
2816- archname = dar.architecturetag
2817-
2818- # logfilename format:
2819- # buildlog_<DISTRIBUTION>_<DISTROSeries>_<ARCHITECTURE>_\
2820- # <SOURCENAME>_<SOURCEVERSION>_<BUILDSTATE>.txt
2821- # as:
2822- # buildlog_ubuntu_dapper_i386_foo_1.0-ubuntu0_FULLYBUILT.txt
2823- # it fix request from bug # 30617
2824- return ('buildlog_%s-%s-%s.%s_%s_%s.txt' % (
2825- distroname, distroseriesname, archname, sourcename, version, state
2826- ))
2827+ # Allow the `IBuildFarmJob` instance with the data/logic specific to
2828+ # the job at hand to calculate the log file name as appropriate.
2829+ return self.specific_job.getLogFileName()
2830
2831 def markAsBuilding(self, builder):
2832 """See `IBuildQueue`."""
2833 self.builder = builder
2834- self.buildstart = UTC_NOW
2835- self.build.buildstate = BuildStatus.BUILDING
2836- # The build started, set the start time if not set already.
2837- if self.build.date_first_dispatched is None:
2838- self.build.date_first_dispatched = UTC_NOW
2839+ if self.job.status != JobStatus.RUNNING:
2840+ self.job.start()
2841+ self.specific_job.jobStarted()
2842
2843 def reset(self):
2844 """See `IBuildQueue`."""
2845 self.builder = None
2846- self.buildstart = None
2847+ if self.job.status != JobStatus.WAITING:
2848+ self.job.queue()
2849+ self.job.date_started = None
2850+ self.job.date_finished = None
2851 self.logtail = None
2852- self.build.buildstate = BuildStatus.NEEDSBUILD
2853+ self.specific_job.jobReset()
2854
2855 def updateBuild_IDLE(self, build_id, build_status, logtail,
2856 filemap, dependencies, logger):
2857 """See `IBuildQueue`."""
2858+ build = getUtility(IBuildSet).getByQueueEntry(self)
2859 logger.warn(
2860 "Builder %s forgot about build %s -- resetting buildqueue record"
2861- % (self.builder.url, self.build.title))
2862+ % (self.builder.url, build.title))
2863 self.reset()
2864
2865 def updateBuild_BUILDING(self, build_id, build_status,
2866 logtail, filemap, dependencies, logger):
2867 """See `IBuildQueue`."""
2868+ if self.job.status != JobStatus.RUNNING:
2869+ self.job.start()
2870 self.logtail = encoding.guess(str(logtail))
2871
2872 def updateBuild_ABORTING(self, buildid, build_status,
2873@@ -275,8 +129,15 @@
2874 """See `IBuildQueue`."""
2875 self.builder.cleanSlave()
2876 self.builder = None
2877- self.buildstart = None
2878- self.build.buildstate = BuildStatus.BUILDING
2879+ if self.job.status != JobStatus.FAILED:
2880+ self.job.fail()
2881+ self.job.date_started = None
2882+ self.job.date_finished = None
2883+ self.specific_job.jobAborted()
2884+
2885+ def setDateStarted(self, timestamp):
2886+ """See `IBuildQueue`."""
2887+ self.job.date_started = timestamp
2888
2889
2890 class BuildQueueSet(object):
2891@@ -311,7 +172,12 @@
2892
2893 def getActiveBuildJobs(self):
2894 """See `IBuildQueueSet`."""
2895- return BuildQueue.select('buildstart is not null')
2896+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
2897+ result_set = store.find(
2898+ BuildQueue,
2899+ BuildQueue.job == Job.id,
2900+ Job.date_started != None)
2901+ return result_set
2902
2903 def calculateCandidates(self, archseries):
2904 """See `IBuildQueueSet`."""
2905@@ -323,30 +189,37 @@
2906 query = """
2907 Build.distroarchseries IN %s AND
2908 Build.buildstate = %s AND
2909- BuildQueue.build = build.id AND
2910+ BuildQueue.job_type = %s AND
2911+ BuildQueue.job = BuildPackageJob.job AND
2912+ BuildPackageJob.build = build.id AND
2913 BuildQueue.builder IS NULL
2914- """ % sqlvalues(arch_ids, BuildStatus.NEEDSBUILD)
2915+ """ % sqlvalues(
2916+ arch_ids, BuildStatus.NEEDSBUILD, BuildFarmJobType.PACKAGEBUILD)
2917
2918 candidates = BuildQueue.select(
2919- query, clauseTables=['Build'], orderBy=['-BuildQueue.lastscore'])
2920+ query, clauseTables=['Build', 'BuildPackageJob'],
2921+ orderBy=['-BuildQueue.lastscore'])
2922
2923 return candidates
2924
2925 def getForBuilds(self, build_ids):
2926 """See `IBuildQueueSet`."""
2927 # Avoid circular import problem.
2928+ from lp.soyuz.model.build import Build
2929 from lp.soyuz.model.builder import Builder
2930
2931 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
2932
2933 origin = (
2934- BuildQueue,
2935+ BuildPackageJob,
2936+ Join(BuildQueue, BuildPackageJob.job == BuildQueue.jobID),
2937+ Join(Build, BuildPackageJob.build == Build.id),
2938 LeftJoin(
2939 Builder,
2940 BuildQueue.builderID == Builder.id),
2941 )
2942 result_set = store.using(*origin).find(
2943- (BuildQueue, Builder),
2944- In(BuildQueue.buildID, build_ids))
2945+ (BuildQueue, Builder, BuildPackageJob),
2946+ In(Build.id, build_ids))
2947
2948 return result_set
2949
2950=== modified file 'lib/lp/soyuz/stories/soyuz/xx-build-record.txt'
2951--- lib/lp/soyuz/stories/soyuz/xx-build-record.txt 2009-09-23 16:38:10 +0000
2952+++ lib/lp/soyuz/stories/soyuz/xx-build-record.txt 2009-11-13 20:43:23 +0000
2953@@ -23,8 +23,9 @@
2954 >>> bob_builder.builderok = True
2955
2956 # Set a known duration for the current job.
2957- >>> in_progress_build = removeSecurityProxy(
2958- ... bob_builder.currentjob.build)
2959+ >>> from lp.soyuz.interfaces.build import IBuildSet
2960+ >>> build2 = getUtility(IBuildSet).getByQueueEntry(bob_builder.currentjob)
2961+ >>> in_progress_build = removeSecurityProxy(build2)
2962 >>> one_minute = datetime.timedelta(seconds=60)
2963 >>> in_progress_build.estimated_build_duration = one_minute
2964
2965@@ -122,12 +123,8 @@
2966 >>> login('foo.bar@canonical.com')
2967 >>> from canonical.database.constants import UTC_NOW
2968 >>> from lp.soyuz.interfaces.build import BuildStatus
2969- >>> in_progress_build.buildstate = BuildStatus.NEEDSBUILD
2970- >>> in_progress_build.buildqueue_record.buildstart = None
2971- >>> in_progress_build.buildqueue_record.builder = None
2972- >>> build.buildstate = BuildStatus.BUILDING
2973- >>> build.buildqueue_record.buildstart = UTC_NOW
2974- >>> build.buildqueue_record.builder = bob_builder
2975+ >>> in_progress_build.buildqueue_record.reset()
2976+ >>> build.buildqueue_record.markAsBuilding(bob_builder)
2977 >>> build.buildqueue_record.logtail = 'one line\nanother line'
2978 >>> logout()
2979
2980
2981=== modified file 'lib/lp/soyuz/templates/build-index.pt'
2982--- lib/lp/soyuz/templates/build-index.pt 2009-09-17 12:08:45 +0000
2983+++ lib/lp/soyuz/templates/build-index.pt 2009-11-13 20:43:23 +0000
2984@@ -161,8 +161,8 @@
2985 <tal:building condition="context/buildstate/enumvalue:BUILDING">
2986 <li>
2987 Started
2988- <span tal:attributes="title view/buildqueue/buildstart/fmt:datetime"
2989- tal:content="view/buildqueue/buildstart/fmt:approximatedate"
2990+ <span tal:attributes="title view/buildqueue/job/date_started/fmt:datetime"
2991+ tal:content="view/buildqueue/job/date_started/fmt:approximatedate"
2992 >5 minutes ago</span>
2993 </li>
2994 </tal:building>
2995
2996=== modified file 'lib/lp/soyuz/templates/builder-index.pt'
2997--- lib/lp/soyuz/templates/builder-index.pt 2009-09-16 19:06:48 +0000
2998+++ lib/lp/soyuz/templates/builder-index.pt 2009-11-13 20:43:23 +0000
2999@@ -104,9 +104,14 @@
3000 </tal:buildernok>
3001 </tal:no_job>
3002
3003+ <tal:comment replace="nothing">
3004+ In the very near future, 'job' will not just be a Build job.
3005+ The template needs to cope with that as and when new job types are
3006+ added.
3007+ </tal:comment>
3008 <tal:job condition="job">
3009 <span class="sortkey" tal:content="job/id" />
3010- <tal:build define="build job/build">
3011+ <tal:build define="build job/specific_job/build">
3012 <tal:visible condition="build/required:launchpad.View">
3013 <tal:icon replace="structure build/image:icon" />
3014 Building
3015@@ -153,10 +158,12 @@
3016
3017 <tal:job condition="job">
3018 <p class="sprite">Started
3019- <span tal:attributes="title job/buildstart/fmt:datetime"
3020+ <span tal:attributes="title job/job/date_started/fmt:datetime"
3021 tal:content="view/current_build_duration/fmt:exactduration"
3022 /> ago.</p>
3023- <tal:visible condition="job/build/required:launchpad.View">
3024+ <tal:visible
3025+ define="build job/specific_job/build"
3026+ condition="build/required:launchpad.View">
3027 <tal:logtail condition="job/logtail">
3028 <h3>Buildlog</h3>
3029 <div tal:content="structure job/logtail/fmt:text-to-html"
3030
3031=== modified file 'lib/lp/soyuz/templates/builds-list.pt'
3032--- lib/lp/soyuz/templates/builds-list.pt 2009-08-18 12:33:37 +0000
3033+++ lib/lp/soyuz/templates/builds-list.pt 2009-11-13 20:43:23 +0000
3034@@ -101,8 +101,8 @@
3035 <tal:building condition="bq/builder">
3036 Build started
3037 <span
3038- tal:attributes="title bq/buildstart/fmt:datetime"
3039- tal:content="bq/buildstart/fmt:displaydate" />
3040+ tal:attributes="title bq/job/date_started/fmt:datetime"
3041+ tal:content="bq/job/date_started/fmt:displaydate" />
3042 on
3043 <a tal:content="bq/builder/title"
3044 tal:attributes="href bq/builder/fmt:url"/>
3045
3046=== modified file 'lib/lp/soyuz/tests/test_builder.py'
3047--- lib/lp/soyuz/tests/test_builder.py 2009-11-11 10:43:07 +0000
3048+++ lib/lp/soyuz/tests/test_builder.py 2009-11-13 20:43:23 +0000
3049@@ -9,8 +9,8 @@
3050
3051 from canonical.testing import LaunchpadZopelessLayer
3052 from lp.soyuz.interfaces.archive import ArchivePurpose
3053+from lp.soyuz.interfaces.build import BuildStatus, IBuildSet
3054 from lp.soyuz.interfaces.builder import IBuilderSet
3055-from lp.soyuz.interfaces.build import BuildStatus
3056 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
3057 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
3058 from lp.testing import TestCaseWithFactory
3059@@ -77,14 +77,16 @@
3060
3061 # Asking frog to find a candidate should give us the joesppa build.
3062 next_job = self.frog_builder.findBuildCandidate()
3063- self.assertEqual('joesppa', next_job.build.archive.name)
3064+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3065+ self.assertEqual('joesppa', build.archive.name)
3066
3067 # If bob is in a failed state the joesppa build is still
3068 # returned.
3069 self.bob_builder.builderok = False
3070 self.bob_builder.manual = False
3071 next_job = self.frog_builder.findBuildCandidate()
3072- self.assertEqual('joesppa', next_job.build.archive.name)
3073+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3074+ self.assertEqual('joesppa', build.archive.name)
3075
3076
3077 class TestFindBuildCandidatePPA(TestFindBuildCandidateBase):
3078@@ -156,14 +158,16 @@
3079 # A PPA cannot start a build if it would use 80% or more of the
3080 # builders.
3081 next_job = self.builder4.findBuildCandidate()
3082- self.failIfEqual('joesppa', next_job.build.archive.name)
3083+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3084+ self.failIfEqual('joesppa', build.archive.name)
3085
3086 def test_findBuildCandidate_first_build_finished(self):
3087 # When joe's first ppa build finishes, his fourth i386 build
3088 # will be the next build candidate.
3089 self.joe_builds[0].buildstate = BuildStatus.FAILEDTOBUILD
3090 next_job = self.builder4.findBuildCandidate()
3091- self.failUnlessEqual('joesppa', next_job.build.archive.name)
3092+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3093+ self.failUnlessEqual('joesppa', build.archive.name)
3094
3095 def test_findBuildCandidate_for_private_ppa(self):
3096 # If a ppa is private it will be able to have parallel builds
3097@@ -171,7 +175,8 @@
3098 self.ppa_joe.private = True
3099 self.ppa_joe.buildd_secret = 'sekrit'
3100 next_job = self.builder4.findBuildCandidate()
3101- self.failUnlessEqual('joesppa', next_job.build.archive.name)
3102+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3103+ self.failUnlessEqual('joesppa', build.archive.name)
3104
3105
3106 class TestFindBuildCandidateDistroArchive(TestFindBuildCandidateBase):
3107@@ -196,18 +201,18 @@
3108 # arch.
3109
3110 next_job = self.builder2.findBuildCandidate()
3111- self.failUnlessEqual('primary', next_job.build.archive.name)
3112- self.failUnlessEqual(
3113- 'gedit', next_job.build.sourcepackagerelease.name)
3114+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3115+ self.failUnlessEqual('primary', build.archive.name)
3116+ self.failUnlessEqual('gedit', build.sourcepackagerelease.name)
3117
3118 # Now even if we set the build building, we'll still get the
3119 # second non-ppa build for the same archive as the next candidate.
3120- next_job.build.buildstate = BuildStatus.BUILDING
3121- next_job.build.builder = self.builder2
3122+ build.buildstate = BuildStatus.BUILDING
3123+ build.builder = self.builder2
3124 next_job = self.builder2.findBuildCandidate()
3125- self.failUnlessEqual('primary', next_job.build.archive.name)
3126- self.failUnlessEqual(
3127- 'firefox', next_job.build.sourcepackagerelease.name)
3128+ build = getUtility(IBuildSet).getByQueueEntry(next_job)
3129+ self.failUnlessEqual('primary', build.archive.name)
3130+ self.failUnlessEqual('firefox', build.sourcepackagerelease.name)
3131
3132 def test_suite():
3133 return unittest.TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: