Merge lp:~wgrant/launchpad/abpb-everywhere into lp:launchpad

Proposed by William Grant on 2014-05-12
Status: Work in progress
Proposed branch: lp:~wgrant/launchpad/abpb-everywhere
Merge into: lp:launchpad
Prerequisite: lp:~wgrant/launchpad/abpb
Diff against target: 691 lines (+151/-117)
12 files modified
lib/lp/buildmaster/interfaces/buildfarmjob.py (+3/-0)
lib/lp/buildmaster/model/buildfarmjob.py (+4/-1)
lib/lp/buildmaster/model/buildqueue.py (+2/-2)
lib/lp/soyuz/browser/build.py (+1/-34)
lib/lp/soyuz/browser/configure.zcml (+0/-3)
lib/lp/soyuz/browser/tests/test_build_views.py (+25/-24)
lib/lp/soyuz/browser/tests/test_builder_views.py (+6/-2)
lib/lp/soyuz/configure.zcml (+1/-1)
lib/lp/soyuz/model/binarypackagebuild.py (+82/-42)
lib/lp/soyuz/stories/soyuz/xx-build-record.txt (+5/-2)
lib/lp/soyuz/tests/test_binarypackagebuild.py (+14/-6)
lib/lp/testing/factory.py (+8/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/abpb-everywhere
Reviewer Review Type Date Requested Status
Launchpad code reviewers 2014-05-12 Pending
Review via email: mp+219143@code.launchpad.net
To post a comment you must log in.

Unmerged revisions

17022. By William Grant on 2014-05-12

Fix tests to use ABPBs rather than BPBs where they need a URL.

17021. By William Grant on 2014-05-12

Drop the URL for IBinaryPackageBuild. Everything should be giving out IArchiveBinaryPackageBuilds.

17020. By William Grant on 2014-05-12

Move BinaryPackageBuild's ProxiedLibraryFileAliases to ArchiveBinaryPackageBuild, as BinaryPackageBuild's URL is not long for this world. This makes BinaryPackageBuild not totally compliant with its interface, for now.

17019. By William Grant on 2014-05-12

Fix tests to expect ABPBs.

17018. By William Grant on 2014-05-12

Fix ArchiveBinaryPackageBuild.__eq__ to function correctly when other is security-proxied.

17017. By William Grant on 2014-05-12

clearBuildQueueRecordCache -> cacheBuildQueueRecord, and use it in the population method so it actually populates.

17016. By William Grant on 2014-05-12

Ensure that ABPBs are always security-proxied, and their wrapped BPBs aren't. Some code assumes it can molest specific_builds after an rSP.

17015. By William Grant on 2014-05-12

Ensure the we clear the cached buildqueue_record on the underlying BuildFarmJobMixin derivative, not a wrapper.

17014. By William Grant on 2014-05-09

BABPBS now returns ABPBs.

17013. By William Grant on 2014-05-09

Fix postprocessCandidate tests to test the write class.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
2--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2014-05-12 07:32:57 +0000
3+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2014-05-12 07:32:57 +0000
4@@ -130,6 +130,9 @@
5 schema=Interface, required=True,
6 title=_("Corresponding BuildQueue record"))
7
8+ def cacheBuildQueueRecord(bq):
9+ """Populate the buildqueue_record cache."""
10+
11 status = exported(
12 Choice(
13 title=_('Status'), required=True,
14
15=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
16--- lib/lp/buildmaster/model/buildfarmjob.py 2013-11-28 05:13:37 +0000
17+++ lib/lp/buildmaster/model/buildfarmjob.py 2014-05-12 07:32:57 +0000
18@@ -119,6 +119,9 @@
19 return Store.of(self).find(
20 BuildQueue, _build_farm_job_id=self.build_farm_job_id).one()
21
22+ def cacheBuildQueueRecord(self, bq):
23+ get_property_cache(self).buildqueue_record = bq
24+
25 @property
26 def is_private(self):
27 """See `IBuildFarmJob`.
28@@ -209,7 +212,7 @@
29 queue_entry.suspend()
30
31 Store.of(self).add(queue_entry)
32- del get_property_cache(self).buildqueue_record
33+ self.cacheBuildQueueRecord(queue_entry)
34 return queue_entry
35
36
37
38=== modified file 'lib/lp/buildmaster/model/buildqueue.py'
39--- lib/lp/buildmaster/model/buildqueue.py 2013-12-04 07:32:44 +0000
40+++ lib/lp/buildmaster/model/buildqueue.py 2014-05-12 07:32:57 +0000
41@@ -149,7 +149,7 @@
42 Store.of(self).flush()
43 if builder is not None:
44 del get_property_cache(builder).currentjob
45- del get_property_cache(specific_build).buildqueue_record
46+ specific_build.cacheBuildQueueRecord(None)
47 self._clear_specific_build_cache()
48
49 def manualScore(self, value):
50@@ -256,5 +256,5 @@
51 for build in builds:
52 bq = prefetched_data.get(
53 removeSecurityProxy(build).build_farm_job_id)
54- get_property_cache(build).buildqueue_record = bq
55+ build.cacheBuildQueueRecord(bq)
56 return bqs
57
58=== modified file 'lib/lp/soyuz/browser/build.py'
59--- lib/lp/soyuz/browser/build.py 2014-05-12 07:32:57 +0000
60+++ lib/lp/soyuz/browser/build.py 2014-05-12 07:32:57 +0000
61@@ -6,13 +6,13 @@
62 __metaclass__ = type
63
64 __all__ = [
65+ 'ArchiveBuildUrl',
66 'BuildBreadcrumb',
67 'BuildCancelView',
68 'BuildContextMenu',
69 'BuildNavigation',
70 'BuildRecordsView',
71 'BuildRescoringView',
72- 'BuildUrl',
73 'BuildView',
74 'DistributionBuildRecordsView',
75 'get_build_by_id_str',
76@@ -98,39 +98,6 @@
77 return None
78
79
80-class BuildUrl:
81- """Dynamic URL declaration for IBinaryPackageBuild.
82-
83- When dealing with distribution builds we want to present them
84- under IDistributionSourcePackageRelease url:
85-
86- /ubuntu/+source/foo/1.0/+build/1234
87-
88- On the other hand, PPA builds will be presented under the PPA page:
89-
90- /~cprov/+archive/+build/1235
91-
92- Copy archives will be presented under the archives page:
93- /ubuntu/+archive/my-special-archive/+build/1234
94- """
95- implements(ICanonicalUrlData)
96- rootsite = None
97-
98- def __init__(self, context):
99- self.context = context
100-
101- @property
102- def inside(self):
103- if self.context.archive.is_ppa or self.context.archive.is_copy:
104- return self.context.archive
105- else:
106- return self.context.distributionsourcepackagerelease
107-
108- @property
109- def path(self):
110- return u"+build/%d" % self.context.id
111-
112-
113 class ArchiveBuildUrl:
114 """Dynamic URL declaration for IArchiveBinaryPackageBuild.
115
116
117=== modified file 'lib/lp/soyuz/browser/configure.zcml'
118--- lib/lp/soyuz/browser/configure.zcml 2014-05-12 07:32:57 +0000
119+++ lib/lp/soyuz/browser/configure.zcml 2014-05-12 07:32:57 +0000
120@@ -336,9 +336,6 @@
121 for="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild"
122 name="+index"/>
123 <browser:url
124- for="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuild"
125- urldata="lp.soyuz.browser.build.BuildUrl"/>
126- <browser:url
127 for="lp.soyuz.interfaces.binarypackagebuild.IArchiveBinaryPackageBuild"
128 urldata="lp.soyuz.browser.build.ArchiveBuildUrl"/>
129 <browser:navigation
130
131=== modified file 'lib/lp/soyuz/browser/tests/test_build_views.py'
132--- lib/lp/soyuz/browser/tests/test_build_views.py 2013-12-04 07:07:04 +0000
133+++ lib/lp/soyuz/browser/tests/test_build_views.py 2014-05-12 07:32:57 +0000
134@@ -59,7 +59,7 @@
135 # The component name is provided when the component is known.
136 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
137 removeSecurityProxy(archive).require_virtualized = False
138- build = self.factory.makeBinaryPackageBuild(archive=archive)
139+ build = self.factory.makeArchiveBinaryPackageBuild(archive=archive)
140 view = create_initialized_view(build, name="+index")
141 self.assertEqual('multiverse', view.component_name)
142
143@@ -79,7 +79,7 @@
144 # for builds targeted to the PRIMARY archive.
145 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
146 removeSecurityProxy(archive).require_virtualized = False
147- build = self.factory.makeBinaryPackageBuild(archive=archive)
148+ build = self.factory.makeArchiveBinaryPackageBuild(archive=archive)
149 build_menu = BuildContextMenu(build)
150 self.assertEquals(
151 build_menu.links,
152@@ -94,7 +94,7 @@
153 # to a PPA.
154 ppa = self.factory.makeArchive(
155 purpose=ArchivePurpose.PPA, virtualized=True)
156- build = self.factory.makeBinaryPackageBuild(archive=ppa)
157+ build = self.factory.makeArchiveBinaryPackageBuild(archive=ppa)
158 build.queueBuild()
159 build_menu = BuildContextMenu(build)
160 self.assertEquals(
161@@ -114,7 +114,7 @@
162 # The build cannot be retried (see IBuild) because it's targeted to a
163 # released distroseries.
164 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
165- build = self.factory.makeBinaryPackageBuild(
166+ build = self.factory.makeArchiveBinaryPackageBuild(
167 archive=archive, status=BuildStatus.FAILEDTOBUILD)
168 distroseries = build.distro_arch_series.distroseries
169 with person_logged_in(self.admin):
170@@ -130,7 +130,7 @@
171 def test_retry_ppa_builds(self):
172 # PPA builds can always be retried, no matter what status the
173 # distroseries has.
174- build = self.factory.makeBinaryPackageBuild(
175+ build = self.factory.makeArchiveBinaryPackageBuild(
176 status=BuildStatus.FAILEDTOBUILD)
177 build_view = getMultiAdapter(
178 (build, self.empty_request), name="+index")
179@@ -143,7 +143,7 @@
180 # Buildd admins can retry any build as long is it's in a state that
181 # permits it to be re-tried.
182 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
183- build = self.factory.makeBinaryPackageBuild(
184+ build = self.factory.makeArchiveBinaryPackageBuild(
185 archive=archive, status=BuildStatus.FAILEDTOBUILD)
186 with person_logged_in(self.admin):
187 self.assertTrue(build.can_be_retried)
188@@ -162,7 +162,7 @@
189 # also retry failed builds of contained source packages.
190 team = self.factory.makeTeam()
191 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
192- build = self.factory.makeBinaryPackageBuild(
193+ build = self.factory.makeArchiveBinaryPackageBuild(
194 archive=archive, status=BuildStatus.FAILEDTOBUILD)
195 with person_logged_in(self.admin):
196 packageset = getUtility(IPackagesetSet).new(
197@@ -180,7 +180,7 @@
198 # `BuildView.package_upload` returns the cached `PackageUpload`
199 # record corresponding to this build. It's None if the binaries for
200 # a build were not yet collected.
201- build = self.factory.makeBinaryPackageBuild()
202+ build = self.factory.makeArchiveBinaryPackageBuild()
203 build_view = getMultiAdapter(
204 (build, self.empty_request), name="+index")
205 self.assertEquals(build_view.package_upload, None)
206@@ -189,7 +189,7 @@
207 PackagePublishingPocket.UPDATES, build.archive,
208 'changes.txt', 'my changes')
209 # Old SQL Object: creating it, adds it automatically to the store.
210- PackageUploadBuild(packageupload=package_upload, build=build)
211+ PackageUploadBuild(packageupload=package_upload, build=build.build)
212 self.assertEquals(package_upload.status.name, 'NEW')
213 build_view = getMultiAdapter(
214 (build, self.empty_request), name="+index")
215@@ -205,7 +205,7 @@
216 def test_build_view_files_helper(self):
217 # The BuildIndex view also has a files helper which returns
218 # all the files from the related binary package releases.
219- build = self.factory.makeBinaryPackageBuild(
220+ build = self.factory.makeArchiveBinaryPackageBuild(
221 status=BuildStatus.FULLYBUILT)
222 bpr = self.factory.makeBinaryPackageRelease(build=build)
223 bprf = self.factory.makeBinaryPackageFile(binarypackagerelease=bpr)
224@@ -224,19 +224,19 @@
225 # corresponding `BuildQueue.lastscore` record for a `Build`.
226 # It redirects users to the `Build` page when the build cannot be
227 # rescored.
228- build = self.factory.makeBinaryPackageBuild(
229+ build = self.factory.makeArchiveBinaryPackageBuild(
230 status=BuildStatus.FAILEDTOBUILD)
231 self.assertFalse(build.can_be_rescored)
232 view = create_initialized_view(build, name='+rescore')
233 self.assertEquals(view.request.response.getStatus(), 302)
234 self.assertEquals(view.request.response.getHeader('Location'),
235 canonical_url(build))
236- pending_build = self.factory.makeBinaryPackageBuild()
237+ pending_build = self.factory.makeArchiveBinaryPackageBuild()
238 view = create_initialized_view(pending_build, name='+rescore')
239 self.assertEquals(view.cancel_url, canonical_url(pending_build))
240
241 def test_rescore_value_too_large(self):
242- build = self.factory.makeBinaryPackageBuild()
243+ build = self.factory.makeArchiveBinaryPackageBuild()
244 view = create_initialized_view(
245 build, name="+rescore", form={
246 'field.priority': str(2 ** 31 + 1),
247@@ -245,7 +245,7 @@
248 self.assertEquals(view.errors[0].doc(), "Value is too big")
249
250 def test_rescore_value_too_small(self):
251- build = self.factory.makeBinaryPackageBuild()
252+ build = self.factory.makeArchiveBinaryPackageBuild()
253 view = create_initialized_view(
254 build, name="+rescore", form={
255 'field.priority': '-' + str(2 ** 31 + 1),
256@@ -254,7 +254,7 @@
257 self.assertEquals(view.errors[0].doc(), "Value is too small")
258
259 def test_rescore(self):
260- pending_build = self.factory.makeBinaryPackageBuild()
261+ pending_build = self.factory.makeArchiveBinaryPackageBuild()
262 pending_build.queueBuild()
263 with person_logged_in(self.admin):
264 view = create_initialized_view(
265@@ -266,7 +266,7 @@
266 self.assertEquals(pending_build.buildqueue_record.lastscore, 0)
267
268 def test_build_page_has_cancel_link(self):
269- build = self.factory.makeBinaryPackageBuild()
270+ build = self.factory.makeArchiveBinaryPackageBuild()
271 build.queueBuild()
272 person = build.archive.owner
273 with person_logged_in(person):
274@@ -281,7 +281,7 @@
275
276 def test_cancelling_pending_build(self):
277 ppa = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
278- pending_build = self.factory.makeBinaryPackageBuild(archive=ppa)
279+ pending_build = self.factory.makeArchiveBinaryPackageBuild(archive=ppa)
280 pending_build.queueBuild()
281 with person_logged_in(ppa.owner):
282 view = create_initialized_view(
283@@ -293,7 +293,7 @@
284
285 def test_cancelling_building_build(self):
286 ppa = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
287- pending_build = self.factory.makeBinaryPackageBuild(archive=ppa)
288+ pending_build = self.factory.makeArchiveBinaryPackageBuild(archive=ppa)
289 pending_build.queueBuild().markAsBuilding(self.factory.makeBuilder())
290 with person_logged_in(ppa.owner):
291 view = create_initialized_view(
292@@ -306,7 +306,8 @@
293
294 def test_cancelling_uncancellable_build(self):
295 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
296- pending_build = self.factory.makeBinaryPackageBuild(archive=archive)
297+ pending_build = self.factory.makeArchiveBinaryPackageBuild(
298+ archive=archive)
299 pending_build.queueBuild()
300 pending_build.updateStatus(BuildStatus.FAILEDTOBUILD)
301 with person_logged_in(archive.owner):
302@@ -325,7 +326,7 @@
303 for i in range(5):
304 das = self.factory.makeDistroArchSeries(distroseries=distroseries)
305 arch_list.append(das.architecturetag)
306- build = self.factory.makeBinaryPackageBuild(
307+ build = self.factory.makeArchiveBinaryPackageBuild(
308 distroarchseries=das, archive=distroseries.main_archive,
309 status=BuildStatus.NEEDSBUILD)
310 build.updateStatus(BuildStatus.BUILDING)
311@@ -356,7 +357,7 @@
312 def test_dispatch_estimate(self):
313 # A dispatch time estimate is available for pending binary builds
314 # that have not been suspended.
315- build = self.factory.makeBinaryPackageBuild()
316+ build = self.factory.makeArchiveBinaryPackageBuild()
317 build.queueBuild()
318 view = create_initialized_view(build, name="+index")
319 bq = view.context.buildqueue_record
320@@ -371,7 +372,7 @@
321 def test_old_url_redirection(self):
322 # When users go to the old build URLs, they are redirected to the
323 # equivalent new URLs.
324- build = self.factory.makeBinaryPackageBuild()
325+ build = self.factory.makeArchiveBinaryPackageBuild()
326 build.queueBuild()
327 url = "http://launchpad.dev/+builds/+build/%s" % build.id
328 expected_url = canonical_url(build)
329@@ -391,7 +392,7 @@
330 distribution = distroseries.distribution
331 das = self.factory.makeDistroArchSeries(distroseries=distroseries)
332 for status in BuildStatus.items:
333- build = self.factory.makeBinaryPackageBuild(
334+ build = self.factory.makeArchiveBinaryPackageBuild(
335 distroarchseries=das, archive=distroseries.main_archive,
336 status=status)
337 # BPBs in certain states need a bit tweaking to appear in
338@@ -432,7 +433,7 @@
339 def test_eta(self):
340 # BuildView.eta returns a non-None value when it should, or None
341 # when there's no start time.
342- build = self.factory.makeBinaryPackageBuild()
343+ build = self.factory.makeArchiveBinaryPackageBuild()
344 build.queueBuild()
345 self.factory.makeBuilder(
346 processors=[build.processor], virtualized=True)
347
348=== modified file 'lib/lp/soyuz/browser/tests/test_builder_views.py'
349--- lib/lp/soyuz/browser/tests/test_builder_views.py 2013-11-26 01:36:10 +0000
350+++ lib/lp/soyuz/browser/tests/test_builder_views.py 2014-05-12 07:32:57 +0000
351@@ -25,6 +25,10 @@
352 from lp.registry.interfaces.person import IPersonSet
353 from lp.services.database.sqlbase import flush_database_updates
354 from lp.soyuz.browser.build import getSpecificJobs
355+from lp.soyuz.model.binarypackagebuild import (
356+ BUILD_ARCHIVE,
357+ decorate_binarypackagebuilds,
358+ )
359 from lp.testing import (
360 celebrity_logged_in,
361 record_two_runs,
362@@ -54,8 +58,8 @@
363 return sprb
364
365 def createBinaryPackageBuild(self):
366- build = self.factory.makeBinaryPackageBuild()
367- return build
368+ bpb = self.factory.makeBinaryPackageBuild()
369+ return decorate_binarypackagebuilds([bpb], archive=BUILD_ARCHIVE)[0]
370
371 def createBuilds(self):
372 builds = []
373
374=== modified file 'lib/lp/soyuz/configure.zcml'
375--- lib/lp/soyuz/configure.zcml 2014-05-12 07:32:57 +0000
376+++ lib/lp/soyuz/configure.zcml 2014-05-12 07:32:57 +0000
377@@ -519,7 +519,7 @@
378 interface="lp.soyuz.interfaces.binarypackagebuild.IBinaryPackageBuildSet"/>
379 </securedutility>
380 <securedutility
381- class="lp.soyuz.model.binarypackagebuild.BinaryPackageBuildSet"
382+ class="lp.soyuz.model.binarypackagebuild.BuildArchiveBinaryPackageBuildSet"
383 provides="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJobSource"
384 name="PACKAGEBUILD">
385 <allow interface="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJobSource"/>
386
387=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
388--- lib/lp/soyuz/model/binarypackagebuild.py 2014-05-12 07:32:57 +0000
389+++ lib/lp/soyuz/model/binarypackagebuild.py 2014-05-12 07:32:57 +0000
390@@ -41,7 +41,10 @@
391 from storm.zope import IResultSet
392 from zope.component import getUtility
393 from zope.interface import implements
394-from zope.security.proxy import removeSecurityProxy
395+from zope.security.proxy import (
396+ ProxyFactory,
397+ removeSecurityProxy,
398+ )
399
400 from lp.app.browser.tales import DurationFormatterAPI
401 from lp.app.errors import NotFoundError
402@@ -51,7 +54,10 @@
403 BuildFarmJobType,
404 BuildStatus,
405 )
406-from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource
407+from lp.buildmaster.interfaces.buildfarmjob import (
408+ IBuildFarmJobSource,
409+ ISpecificBuildFarmJobSource,
410+ )
411 from lp.buildmaster.model.builder import Builder
412 from lp.buildmaster.model.buildfarmjob import (
413 BuildFarmJob,
414@@ -168,20 +174,28 @@
415 If it's a list or tuple, the return value will be a list.
416 Otherwise, the return value will be a generator.
417 """
418+ # Some code assumes that it can use rSP to get unfettered access to
419+ # the specific_build. Make that true here by relying on the outside
420+ # proxy, and eliminating the inner ones.
421 if archive is None:
422- splitter = lambda build: (build[1], build[0])
423+ splitter = lambda build: (
424+ build[1], removeSecurityProxy(build[0]))
425 elif archive is BUILD_ARCHIVE:
426- splitter = lambda build: (build.archive, build)
427+ splitter = lambda build: (
428+ ProxyFactory(removeSecurityProxy(build).archive),
429+ removeSecurityProxy(build))
430 else:
431- splitter = lambda build: (archive, build)
432+ splitter = lambda build: (
433+ archive, removeSecurityProxy(build))
434
435 if IResultSet.providedBy(bpbs):
436- return DecoratedResultSet(
437- bpbs,
438- lambda build: ArchiveBinaryPackageBuild(*splitter(build)))
439+ return ProxyFactory(
440+ DecoratedResultSet(
441+ bpbs,
442+ lambda build: ArchiveBinaryPackageBuild(*splitter(build))))
443 else:
444 seq = (
445- ArchiveBinaryPackageBuild(*splitter(build))
446+ ProxyFactory(ArchiveBinaryPackageBuild(*splitter(build)))
447 for build in bpbs)
448 if isinstance(bpbs, (list, tuple)):
449 seq = list(seq)
450@@ -291,14 +305,6 @@
451 return package_upload.changesfile
452
453 @property
454- def changesfile_url(self):
455- """See `IBinaryPackageBuild`."""
456- changesfile = self.upload_changesfile
457- if changesfile is None:
458- return None
459- return ProxiedLibraryFileAlias(changesfile, self).http_url
460-
461- @property
462 def package_upload(self):
463 """See `IBuild`."""
464 store = Store.of(self)
465@@ -354,28 +360,6 @@
466 return self.distro_arch_series.architecturetag
467
468 @property
469- def log_url(self):
470- """See `IPackageBuild`.
471-
472- Overridden here for the case of builds for distro archives,
473- currently only supported for binary package builds.
474- """
475- if self.log is None:
476- return None
477- return ProxiedLibraryFileAlias(self.log, self).http_url
478-
479- @property
480- def upload_log_url(self):
481- """See `IPackageBuild`.
482-
483- Overridden here for the case of builds for distro archives,
484- currently only supported for binary package builds.
485- """
486- if self.upload_log is None:
487- return None
488- return ProxiedLibraryFileAlias(self.upload_log, self).http_url
489-
490- @property
491 def distributionsourcepackagerelease(self):
492 """See `IBuild`."""
493 from lp.soyuz.model.distributionsourcepackagerelease \
494@@ -942,15 +926,45 @@
495
496 def __eq__(self, other):
497 return (
498- isinstance(other, self.__class__)
499+ self.__class__ == other.__class__
500 and self.parent_archive == other.parent_archive
501 and self.build == other.build)
502
503 def __ne__(self, other):
504 return not self.__eq__(other)
505
506-
507-class BinaryPackageBuildSet(SpecificBuildFarmJobSourceMixin):
508+ @property
509+ def log_url(self):
510+ """See `IPackageBuild`.
511+
512+ Overridden here for the case of builds for distro archives,
513+ currently only supported for binary package builds.
514+ """
515+ if self.log is None:
516+ return None
517+ return ProxiedLibraryFileAlias(self.log, self).http_url
518+
519+ @property
520+ def upload_log_url(self):
521+ """See `IPackageBuild`.
522+
523+ Overridden here for the case of builds for distro archives,
524+ currently only supported for binary package builds.
525+ """
526+ if self.upload_log is None:
527+ return None
528+ return ProxiedLibraryFileAlias(self.upload_log, self).http_url
529+
530+ @property
531+ def changesfile_url(self):
532+ """See `IBinaryPackageBuild`."""
533+ changesfile = self.upload_changesfile
534+ if changesfile is None:
535+ return None
536+ return ProxiedLibraryFileAlias(changesfile, self).http_url
537+
538+
539+class BinaryPackageBuildSet:
540 implements(IBinaryPackageBuildSet)
541
542 def new(self, distro_arch_series, source_package_release, processor,
543@@ -1312,6 +1326,32 @@
544 return IStore(BinaryPackageBuild).find(
545 BinaryPackageBuild, build_farm_job_id=bfj_id).one()
546
547+
548+class BuildArchiveBinaryPackageBuildSet(SpecificBuildFarmJobSourceMixin):
549+ """`ISpecificBuildFarmJobSource` for `IBinaryPackageBuild`s.
550+
551+ `BinaryPackageBuilds` themselves aren't completely specific. To be
552+ useful (for permission checking, URL generation, that sort of thing)
553+ they need an `Archive` attached. For published builds this is the
554+ archive in which they're being viewed, but as an `IBuildFarmJob` the
555+ context is always the archive in which the build was originally
556+ performed.
557+ """
558+
559+ implements(ISpecificBuildFarmJobSource)
560+
561+ def getByID(self, id):
562+ bpb = getUtility(IBinaryPackageBuildSet).getByID(id)
563+ return decorate_binarypackagebuilds([bpb], archive=BUILD_ARCHIVE)[0]
564+
565+ def getByBuildFarmJobs(self, bfjs):
566+ bpbs = getUtility(IBinaryPackageBuildSet).getByBuildFarmJobs(bfjs)
567+ return decorate_binarypackagebuilds(bpbs, archive=BUILD_ARCHIVE)
568+
569+ def getByBuildFarmJob(self, bfj):
570+ bpb = getUtility(IBinaryPackageBuildSet).getByBuildFarmJob(bfj)
571+ return decorate_binarypackagebuilds([bpb], archive=BUILD_ARCHIVE)[0]
572+
573 @staticmethod
574 def addCandidateSelectionCriteria(processor, virtualized):
575 """See `ISpecificBuildFarmJobSource`."""
576
577=== modified file 'lib/lp/soyuz/stories/soyuz/xx-build-record.txt'
578--- lib/lp/soyuz/stories/soyuz/xx-build-record.txt 2013-11-19 02:51:24 +0000
579+++ lib/lp/soyuz/stories/soyuz/xx-build-record.txt 2014-05-12 07:32:57 +0000
580@@ -227,9 +227,12 @@
581 http://launchpad.dev/builders/bob
582
583 >>> login(ANONYMOUS)
584- >>> anon_browser.getLink('buildlog').url == build.log_url
585+ >>> from lp.soyuz.model.binarypackagebuild import (
586+ ... BUILD_ARCHIVE, decorate_binarypackagebuilds)
587+ >>> [abpb] = decorate_binarypackagebuilds([build], archive=BUILD_ARCHIVE)
588+ >>> anon_browser.getLink('buildlog').url == abpb.log_url
589 True
590- >>> anon_browser.getLink('uploadlog').url == build.upload_log_url
591+ >>> anon_browser.getLink('uploadlog').url == abpb.upload_log_url
592 True
593 >>> logout()
594
595
596=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
597--- lib/lp/soyuz/tests/test_binarypackagebuild.py 2013-12-04 07:07:04 +0000
598+++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2014-05-12 07:32:57 +0000
599@@ -34,7 +34,7 @@
600 )
601 from lp.soyuz.interfaces.component import IComponentSet
602 from lp.soyuz.model.binarypackagebuild import (
603- BinaryPackageBuildSet,
604+ BuildArchiveBinaryPackageBuildSet,
605 COPY_ARCHIVE_SCORE_PENALTY,
606 PRIVATE_ARCHIVE_SCORE_BONUS,
607 SCORE_BY_COMPONENT,
608@@ -77,7 +77,8 @@
609 self.assertProvides(bq, IBuildQueue)
610 self.assertEqual(
611 self.build.build_farm_job, removeSecurityProxy(bq)._build_farm_job)
612- self.assertEqual(self.build, bq.specific_build)
613+ self.assertEqual(self.build, bq.specific_build.build)
614+ self.assertEqual(self.build.archive, bq.specific_build.parent_archive)
615 self.assertEqual(self.build.is_virtualized, bq.virtualized)
616 self.assertIsNotNone(bq.processor)
617 self.assertEqual(bq, self.build.buildqueue_record)
618@@ -484,6 +485,9 @@
619
620 layer = DatabaseFunctionalLayer
621
622+ def setUp(self):
623+ super(TestPostprocessCandidate, self).setUp()
624+
625 def makeBuildJob(self, pocket="RELEASE"):
626 build = self.factory.makeBinaryPackageBuild(pocket=pocket)
627 return build.queueBuild()
628@@ -491,13 +495,15 @@
629 def test_release_job(self):
630 job = self.makeBuildJob()
631 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
632- self.assertTrue(BinaryPackageBuildSet.postprocessCandidate(job, None))
633+ self.assertTrue(
634+ BuildArchiveBinaryPackageBuildSet.postprocessCandidate(job, None))
635 self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
636
637 def test_security_job_is_failed(self):
638 job = self.makeBuildJob(pocket="SECURITY")
639 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
640- BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
641+ BuildArchiveBinaryPackageBuildSet.postprocessCandidate(
642+ job, DevNullLogger())
643 self.assertEqual(BuildStatus.FAILEDTOBUILD, build.status)
644
645 def test_obsolete_job_without_flag_is_failed(self):
646@@ -505,7 +511,8 @@
647 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
648 distroseries = build.distro_arch_series.distroseries
649 removeSecurityProxy(distroseries).status = SeriesStatus.OBSOLETE
650- BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
651+ BuildArchiveBinaryPackageBuildSet.postprocessCandidate(
652+ job, DevNullLogger())
653 self.assertEqual(BuildStatus.FAILEDTOBUILD, build.status)
654
655 def test_obsolete_job_with_flag_is_not_failed(self):
656@@ -515,7 +522,8 @@
657 archive = build.archive
658 removeSecurityProxy(distroseries).status = SeriesStatus.OBSOLETE
659 removeSecurityProxy(archive).permit_obsolete_series_uploads = True
660- BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
661+ BuildArchiveBinaryPackageBuildSet.postprocessCandidate(
662+ job, DevNullLogger())
663 self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
664
665
666
667=== modified file 'lib/lp/testing/factory.py'
668--- lib/lp/testing/factory.py 2014-04-24 02:16:27 +0000
669+++ lib/lp/testing/factory.py 2014-05-12 07:32:57 +0000
670@@ -289,6 +289,10 @@
671 from lp.soyuz.interfaces.publishing import IPublishingSet
672 from lp.soyuz.interfaces.queue import IPackageUploadSet
673 from lp.soyuz.interfaces.section import ISectionSet
674+from lp.soyuz.model.binarypackagebuild import (
675+ BUILD_ARCHIVE,
676+ decorate_binarypackagebuilds,
677+ )
678 from lp.soyuz.model.component import ComponentSelection
679 from lp.soyuz.model.distributionsourcepackagecache import (
680 DistributionSourcePackageCache,
681@@ -3615,6 +3619,10 @@
682 IStore(binary_package_build).flush()
683 return binary_package_build
684
685+ def makeArchiveBinaryPackageBuild(self, *args, **kwargs):
686+ bpb = self.makeBinaryPackageBuild(*args, **kwargs)
687+ return decorate_binarypackagebuilds([bpb], archive=BUILD_ARCHIVE)[0]
688+
689 def makeSourcePackagePublishingHistory(self,
690 distroseries=None,
691 archive=None,