Merge lp:~wgrant/launchpad/flatten-bfj-2-garbo into lp:launchpad
- flatten-bfj-2-garbo
- Merge into devel
Proposed by
William Grant
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 16467 | ||||||||
Proposed branch: | lp:~wgrant/launchpad/flatten-bfj-2-garbo | ||||||||
Merge into: | lp:launchpad | ||||||||
Prerequisite: | lp:~wgrant/launchpad/flatten-bfj-1-populate | ||||||||
Diff against target: |
543 lines (+380/-1) 5 files modified
database/schema/security.cfg (+4/-0) lib/lp/scripts/garbo.py (+227/-0) lib/lp/scripts/tests/test_garbo.py (+136/-1) lib/lp/soyuz/interfaces/binarypackagebuild.py (+3/-0) lib/lp/soyuz/model/binarypackagebuild.py (+10/-0) |
||||||||
To merge this branch: | bzr merge lp:~wgrant/launchpad/flatten-bfj-2-garbo | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Steve Kowalik (community) | code | Approve | |
Review via email: mp+145542@code.launchpad.net |
Commit message
Add garbo jobs to backfill the denormalised BPB/SRPB/TTB columns.
Description of the change
The build farm job schema is being reworked to improve performance. Columns from PackageBuild and BuildFarmJob are being merged into tables that previously delegated to them. The PackageBuild table will end up dying entirely, but BuildFarmJob will remain, a shadow of its former self, to answer questions about Archive:+builds and Builder:+history. Additionally, BinaryPackageBuild is growing new distribution, distroseries, sourcepackagename and is_distro_archive columns to make searches even faster.
This is the second app part: a garbo job to backfill the values that I started setting in the prereq.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'database/schema/security.cfg' |
2 | --- database/schema/security.cfg 2013-02-04 02:04:26 +0000 |
3 | +++ database/schema/security.cfg 2013-02-04 02:04:26 +0000 |
4 | @@ -2218,6 +2218,7 @@ |
5 | public.answercontact = SELECT, DELETE |
6 | public.branch = SELECT, UPDATE |
7 | public.branchjob = SELECT, DELETE |
8 | +public.binarypackagebuild = SELECT, UPDATE |
9 | public.binarypackagename = SELECT |
10 | public.binarypackagerelease = SELECT |
11 | public.binarypackagepublishinghistory = SELECT, UPDATE |
12 | @@ -2240,6 +2241,7 @@ |
13 | public.bugtaskflat = SELECT |
14 | public.bugwatch = SELECT, UPDATE |
15 | public.bugwatchactivity = SELECT, DELETE |
16 | +public.buildfarmjob = SELECT, UPDATE |
17 | public.codeimportevent = SELECT, DELETE |
18 | public.codeimporteventdata = SELECT, DELETE |
19 | public.codeimportresult = SELECT, DELETE |
20 | @@ -2263,6 +2265,7 @@ |
21 | public.revisionauthor = SELECT, UPDATE |
22 | public.revisioncache = SELECT, DELETE |
23 | public.sourcepackagename = SELECT |
24 | +public.sourcepackagerecipebuild = SELECT, UPDATE |
25 | public.sourcepackagerelease = SELECT |
26 | public.sourcepackagepublishinghistory = SELECT, UPDATE |
27 | public.suggestivepotemplate = INSERT, DELETE |
28 | @@ -2270,6 +2273,7 @@ |
29 | public.teamparticipation = SELECT, DELETE |
30 | public.translationmessage = SELECT, DELETE |
31 | public.translationtemplateitem = SELECT, DELETE |
32 | +public.translationtemplatesbuild = SELECT, UPDATE |
33 | type=user |
34 | |
35 | [garbo_daily] |
36 | |
37 | === modified file 'lib/lp/scripts/garbo.py' |
38 | --- lib/lp/scripts/garbo.py 2013-01-17 00:25:48 +0000 |
39 | +++ lib/lp/scripts/garbo.py 2013-02-04 02:04:26 +0000 |
40 | @@ -57,6 +57,8 @@ |
41 | BugWatchScheduler, |
42 | MAX_SAMPLE_SIZE, |
43 | ) |
44 | +from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
45 | +from lp.buildmaster.model.packagebuild import PackageBuild |
46 | from lp.code.interfaces.revision import IRevisionSet |
47 | from lp.code.model.codeimportevent import CodeImportEvent |
48 | from lp.code.model.codeimportresult import CodeImportResult |
49 | @@ -64,8 +66,10 @@ |
50 | RevisionAuthor, |
51 | RevisionCache, |
52 | ) |
53 | +from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild |
54 | from lp.hardwaredb.model.hwdb import HWSubmission |
55 | from lp.registry.model.commercialsubscription import CommercialSubscription |
56 | +from lp.registry.model.distroseries import DistroSeries |
57 | from lp.registry.model.person import Person |
58 | from lp.registry.model.product import Product |
59 | from lp.registry.model.teammembership import TeamMembership |
60 | @@ -104,6 +108,7 @@ |
61 | from lp.services.librarian.model import TimeLimitedToken |
62 | from lp.services.log.logger import PrefixFilter |
63 | from lp.services.looptuner import TunableLoop |
64 | +from lp.services.memcache.interfaces import IMemcacheClient |
65 | from lp.services.oauth.model import OAuthNonce |
66 | from lp.services.openid.model.openidconsumer import OpenIDConsumerNonce |
67 | from lp.services.propertycache import cachedproperty |
68 | @@ -118,7 +123,10 @@ |
69 | ) |
70 | from lp.services.session.model import SessionData |
71 | from lp.services.verification.model.logintoken import LoginToken |
72 | +from lp.soyuz.interfaces.archive import MAIN_ARCHIVE_PURPOSES |
73 | from lp.soyuz.model.archive import Archive |
74 | +from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild |
75 | +from lp.soyuz.model.distroarchseries import DistroArchSeries |
76 | from lp.soyuz.model.publishing import SourcePackagePublishingHistory |
77 | from lp.soyuz.model.reporting import LatestPersonSourcePackageReleaseCache |
78 | from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease |
79 | @@ -129,6 +137,9 @@ |
80 | from lp.translations.model.translationtemplateitem import ( |
81 | TranslationTemplateItem, |
82 | ) |
83 | +from lp.translations.model.translationtemplatesbuild import ( |
84 | + TranslationTemplatesBuild, |
85 | + ) |
86 | from lp.translations.scripts.scrub_pofiletranslator import ( |
87 | ScrubPOFileTranslator, |
88 | ) |
89 | @@ -1335,6 +1346,219 @@ |
90 | transaction.commit() |
91 | |
92 | |
93 | +class BinaryPackageBuildFlattener(TunableLoop): |
94 | + """Populates the new denormalised columns on BinaryPackageBuild.""" |
95 | + |
96 | + maximum_chunk_size = 5000 |
97 | + |
98 | + def __init__(self, log, abort_time=None): |
99 | + super(BinaryPackageBuildFlattener, self).__init__(log, abort_time) |
100 | + |
101 | + self.memcache_key = '%s:bpb-flattener' % config.instance_name |
102 | + watermark = getUtility(IMemcacheClient).get(self.memcache_key) |
103 | + self.start_at = watermark or 0 |
104 | + self.store = IMasterStore(BinaryPackageBuild) |
105 | + |
106 | + def findIDs(self): |
107 | + return self.store.find( |
108 | + BinaryPackageBuild.id, |
109 | + BinaryPackageBuild.id >= self.start_at, |
110 | + ).order_by(BinaryPackageBuild.id) |
111 | + |
112 | + def isDone(self): |
113 | + return ( |
114 | + not getFeatureFlag('soyuz.flatten_bfj.garbo.enabled') |
115 | + or self.findIDs().is_empty()) |
116 | + |
117 | + def __call__(self, chunk_size): |
118 | + """See `ITunableLoop`.""" |
119 | + ids = list(self.findIDs()[:chunk_size]) |
120 | + updated_columns = { |
121 | + BinaryPackageBuild._new_archive_id: PackageBuild.archive_id, |
122 | + BinaryPackageBuild._new_pocket: PackageBuild.pocket, |
123 | + BinaryPackageBuild._new_processor_id: BuildFarmJob.processor_id, |
124 | + BinaryPackageBuild._new_virtualized: BuildFarmJob.virtualized, |
125 | + BinaryPackageBuild._new_date_created: BuildFarmJob.date_created, |
126 | + BinaryPackageBuild._new_date_started: BuildFarmJob.date_started, |
127 | + BinaryPackageBuild._new_date_finished: BuildFarmJob.date_finished, |
128 | + BinaryPackageBuild._new_date_first_dispatched: |
129 | + BuildFarmJob.date_first_dispatched, |
130 | + BinaryPackageBuild._new_builder_id: BuildFarmJob.builder_id, |
131 | + BinaryPackageBuild._new_status: BuildFarmJob.status, |
132 | + BinaryPackageBuild._new_log_id: BuildFarmJob.log_id, |
133 | + BinaryPackageBuild._new_upload_log_id: PackageBuild.upload_log_id, |
134 | + BinaryPackageBuild._new_dependencies: PackageBuild.dependencies, |
135 | + BinaryPackageBuild._new_failure_count: BuildFarmJob.failure_count, |
136 | + BinaryPackageBuild._new_build_farm_job_id: BuildFarmJob.id, |
137 | + BinaryPackageBuild._new_distribution_id: |
138 | + DistroSeries.distributionID, |
139 | + BinaryPackageBuild._new_distro_series_id: DistroSeries.id, |
140 | + BinaryPackageBuild._new_source_package_name_id: |
141 | + SourcePackageRelease.sourcepackagenameID, |
142 | + BinaryPackageBuild._new_is_distro_archive: |
143 | + Archive.purpose.is_in(MAIN_ARCHIVE_PURPOSES), |
144 | + } |
145 | + condition = And( |
146 | + BinaryPackageBuild.id.is_in(ids), |
147 | + PackageBuild.id == BinaryPackageBuild.package_build_id, |
148 | + BuildFarmJob.id == PackageBuild.build_farm_job_id) |
149 | + extra_condition = And( |
150 | + condition, |
151 | + SourcePackageRelease.id == |
152 | + BinaryPackageBuild.source_package_release_id, |
153 | + Archive.id == PackageBuild.archive_id, |
154 | + DistroArchSeries.id == BinaryPackageBuild.distro_arch_series_id, |
155 | + DistroSeries.id == DistroArchSeries.distroseriesID) |
156 | + self.store.execute( |
157 | + BulkUpdate( |
158 | + updated_columns, table=BinaryPackageBuild, |
159 | + values=( |
160 | + PackageBuild, BuildFarmJob, Archive, DistroArchSeries, |
161 | + DistroSeries, SourcePackageRelease), |
162 | + where=And(condition, extra_condition))) |
163 | + self.store.execute( |
164 | + BulkUpdate( |
165 | + {BuildFarmJob.archive_id: PackageBuild.archive_id}, |
166 | + table=BuildFarmJob, values=(PackageBuild, BinaryPackageBuild), |
167 | + where=condition)) |
168 | + transaction.commit() |
169 | + self.start_at = ids[-1] + 1 |
170 | + getUtility(IMemcacheClient).set(self.memcache_key, self.start_at) |
171 | + |
172 | + |
173 | +class SourcePackageRecipeBuildFlattener(TunableLoop): |
174 | + """Populates the new denormalised columns on SourcePackageRecipeBuild.""" |
175 | + |
176 | + maximum_chunk_size = 5000 |
177 | + |
178 | + def __init__(self, log, abort_time=None): |
179 | + super(SourcePackageRecipeBuildFlattener, self).__init__( |
180 | + log, abort_time) |
181 | + |
182 | + self.memcache_key = '%s:sprb-flattener' % config.instance_name |
183 | + watermark = getUtility(IMemcacheClient).get(self.memcache_key) |
184 | + self.start_at = watermark or 0 |
185 | + self.store = IMasterStore(SourcePackageRecipeBuild) |
186 | + |
187 | + def findIDs(self): |
188 | + return self.store.find( |
189 | + SourcePackageRecipeBuild.id, |
190 | + SourcePackageRecipeBuild.id >= self.start_at, |
191 | + ).order_by(SourcePackageRecipeBuild.id) |
192 | + |
193 | + def isDone(self): |
194 | + return ( |
195 | + not getFeatureFlag('soyuz.flatten_bfj.garbo.enabled') |
196 | + or self.findIDs().is_empty()) |
197 | + |
198 | + def __call__(self, chunk_size): |
199 | + """See `ITunableLoop`.""" |
200 | + ids = list(self.findIDs()[:chunk_size]) |
201 | + updated_columns = { |
202 | + SourcePackageRecipeBuild._new_archive_id: PackageBuild.archive_id, |
203 | + SourcePackageRecipeBuild._new_pocket: PackageBuild.pocket, |
204 | + SourcePackageRecipeBuild._new_processor_id: |
205 | + BuildFarmJob.processor_id, |
206 | + SourcePackageRecipeBuild._new_virtualized: |
207 | + BuildFarmJob.virtualized, |
208 | + SourcePackageRecipeBuild._new_date_created: |
209 | + BuildFarmJob.date_created, |
210 | + SourcePackageRecipeBuild._new_date_started: |
211 | + BuildFarmJob.date_started, |
212 | + SourcePackageRecipeBuild._new_date_finished: |
213 | + BuildFarmJob.date_finished, |
214 | + SourcePackageRecipeBuild._new_date_first_dispatched: |
215 | + BuildFarmJob.date_first_dispatched, |
216 | + SourcePackageRecipeBuild._new_builder_id: BuildFarmJob.builder_id, |
217 | + SourcePackageRecipeBuild._new_status: BuildFarmJob.status, |
218 | + SourcePackageRecipeBuild._new_log_id: BuildFarmJob.log_id, |
219 | + SourcePackageRecipeBuild._new_upload_log_id: |
220 | + PackageBuild.upload_log_id, |
221 | + SourcePackageRecipeBuild._new_dependencies: |
222 | + PackageBuild.dependencies, |
223 | + SourcePackageRecipeBuild._new_failure_count: |
224 | + BuildFarmJob.failure_count, |
225 | + SourcePackageRecipeBuild._new_build_farm_job_id: BuildFarmJob.id, |
226 | + } |
227 | + condition = And( |
228 | + SourcePackageRecipeBuild.id.is_in(ids), |
229 | + PackageBuild.id == SourcePackageRecipeBuild.package_build_id, |
230 | + BuildFarmJob.id == PackageBuild.build_farm_job_id) |
231 | + self.store.execute( |
232 | + BulkUpdate( |
233 | + updated_columns, table=SourcePackageRecipeBuild, |
234 | + values=(PackageBuild, BuildFarmJob), where=condition)) |
235 | + self.store.execute( |
236 | + BulkUpdate( |
237 | + {BuildFarmJob.archive_id: PackageBuild.archive_id}, |
238 | + table=BuildFarmJob, |
239 | + values=(PackageBuild, SourcePackageRecipeBuild), |
240 | + where=condition)) |
241 | + transaction.commit() |
242 | + self.start_at = ids[-1] + 1 |
243 | + getUtility(IMemcacheClient).set(self.memcache_key, self.start_at) |
244 | + |
245 | + |
246 | +class TranslationTemplatesBuildFlattener(TunableLoop): |
247 | + """Populates the new denormalised columns on TranslationTemplatesBuild.""" |
248 | + |
249 | + maximum_chunk_size = 5000 |
250 | + |
251 | + def __init__(self, log, abort_time=None): |
252 | + super(TranslationTemplatesBuildFlattener, self).__init__( |
253 | + log, abort_time) |
254 | + |
255 | + self.memcache_key = '%s:ttb-flattener' % config.instance_name |
256 | + watermark = getUtility(IMemcacheClient).get(self.memcache_key) |
257 | + self.start_at = watermark or 0 |
258 | + self.store = IMasterStore(TranslationTemplatesBuild) |
259 | + |
260 | + def findIDs(self): |
261 | + return self.store.find( |
262 | + TranslationTemplatesBuild.id, |
263 | + TranslationTemplatesBuild.id >= self.start_at, |
264 | + ).order_by(TranslationTemplatesBuild.id) |
265 | + |
266 | + def isDone(self): |
267 | + return ( |
268 | + not getFeatureFlag('soyuz.flatten_bfj.garbo.enabled') |
269 | + or self.findIDs().is_empty()) |
270 | + |
271 | + def __call__(self, chunk_size): |
272 | + """See `ITunableLoop`.""" |
273 | + ids = list(self.findIDs()[:chunk_size]) |
274 | + updated_columns = { |
275 | + TranslationTemplatesBuild._new_processor_id: |
276 | + BuildFarmJob.processor_id, |
277 | + TranslationTemplatesBuild._new_virtualized: |
278 | + BuildFarmJob.virtualized, |
279 | + TranslationTemplatesBuild._new_date_created: |
280 | + BuildFarmJob.date_created, |
281 | + TranslationTemplatesBuild._new_date_started: |
282 | + BuildFarmJob.date_started, |
283 | + TranslationTemplatesBuild._new_date_finished: |
284 | + BuildFarmJob.date_finished, |
285 | + TranslationTemplatesBuild._new_date_first_dispatched: |
286 | + BuildFarmJob.date_first_dispatched, |
287 | + TranslationTemplatesBuild._new_builder_id: BuildFarmJob.builder_id, |
288 | + TranslationTemplatesBuild._new_status: BuildFarmJob.status, |
289 | + TranslationTemplatesBuild._new_log_id: BuildFarmJob.log_id, |
290 | + TranslationTemplatesBuild._new_failure_count: |
291 | + BuildFarmJob.failure_count, |
292 | + } |
293 | + self.store.execute( |
294 | + BulkUpdate( |
295 | + updated_columns, table=TranslationTemplatesBuild, |
296 | + values=BuildFarmJob, |
297 | + where=And( |
298 | + TranslationTemplatesBuild.id.is_in(ids), |
299 | + BuildFarmJob.id == |
300 | + TranslationTemplatesBuild.build_farm_job_id))) |
301 | + transaction.commit() |
302 | + self.start_at = ids[-1] + 1 |
303 | + getUtility(IMemcacheClient).set(self.memcache_key, self.start_at) |
304 | + |
305 | + |
306 | class BaseDatabaseGarbageCollector(LaunchpadCronScript): |
307 | """Abstract base class to run a collection of TunableLoops.""" |
308 | script_name = None # Script name for locking and database user. Override. |
309 | @@ -1590,6 +1814,9 @@ |
310 | UnusedSessionPruner, |
311 | DuplicateSessionPruner, |
312 | BugHeatUpdater, |
313 | + BinaryPackageBuildFlattener, |
314 | + SourcePackageRecipeBuildFlattener, |
315 | + TranslationTemplatesBuildFlattener, |
316 | ] |
317 | experimental_tunable_loops = [] |
318 | |
319 | |
320 | === modified file 'lib/lp/scripts/tests/test_garbo.py' |
321 | --- lib/lp/scripts/tests/test_garbo.py 2013-01-17 00:25:48 +0000 |
322 | +++ lib/lp/scripts/tests/test_garbo.py 2013-02-04 02:04:26 +0000 |
323 | @@ -31,6 +31,7 @@ |
324 | from testtools.matchers import ( |
325 | Equals, |
326 | GreaterThan, |
327 | + MatchesStructure, |
328 | ) |
329 | import transaction |
330 | from zope.component import getUtility |
331 | @@ -42,6 +43,7 @@ |
332 | BugNotification, |
333 | BugNotificationRecipient, |
334 | ) |
335 | +from lp.buildmaster.enums import BuildStatus |
336 | from lp.code.bzr import ( |
337 | BranchFormat, |
338 | RepositoryFormat, |
339 | @@ -58,6 +60,7 @@ |
340 | BranchSharingPolicy, |
341 | BugSharingPolicy, |
342 | ) |
343 | +from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild |
344 | from lp.registry.interfaces.accesspolicy import IAccessPolicySource |
345 | from lp.registry.interfaces.person import IPersonSet |
346 | from lp.registry.interfaces.teammembership import TeamMembershipStatus |
347 | @@ -114,6 +117,7 @@ |
348 | from lp.services.verification.model.logintoken import LoginToken |
349 | from lp.services.worlddata.interfaces.language import ILanguageSet |
350 | from lp.soyuz.enums import PackagePublishingStatus |
351 | +from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild |
352 | from lp.soyuz.model.reporting import LatestPersonSourcePackageReleaseCache |
353 | from lp.testing import ( |
354 | FakeAdapterMixin, |
355 | @@ -121,7 +125,10 @@ |
356 | TestCase, |
357 | TestCaseWithFactory, |
358 | ) |
359 | -from lp.testing.dbuser import switch_dbuser |
360 | +from lp.testing.dbuser import ( |
361 | + dbuser, |
362 | + switch_dbuser, |
363 | + ) |
364 | from lp.testing.layers import ( |
365 | DatabaseLayer, |
366 | LaunchpadScriptLayer, |
367 | @@ -133,6 +140,9 @@ |
368 | from lp.translations.model.translationtemplateitem import ( |
369 | TranslationTemplateItem, |
370 | ) |
371 | +from lp.translations.model.translationtemplatesbuild import ( |
372 | + TranslationTemplatesBuild, |
373 | + ) |
374 | |
375 | |
376 | class TestGarboScript(TestCase): |
377 | @@ -1273,6 +1283,131 @@ |
378 | 'PopulateLatestPersonSourcePackageReleaseCache') |
379 | self.assertEqual(spph_2.id, job_data['last_spph_id']) |
380 | |
381 | + def test_BinaryPackageBuildFlattener(self): |
382 | + store = IMasterStore(BinaryPackageBuild) |
383 | + # Sampledata builds start off with the new columns set to None, |
384 | + # and garbo won't run without a feature flag set. |
385 | + self.runHourly() |
386 | + self.assertNotEqual( |
387 | + 0, store.find(BinaryPackageBuild, _new_archive=None).count()) |
388 | + |
389 | + # But after a garbo run they're all set properly. |
390 | + with dbuser('testadmin'): |
391 | + IMasterStore(FeatureFlag).add(FeatureFlag( |
392 | + u'default', 0, u'soyuz.flatten_bfj.garbo.enabled', u'true')) |
393 | + self.runHourly() |
394 | + self.assertEqual( |
395 | + 0, store.find(BinaryPackageBuild, _new_archive=None).count()) |
396 | + |
397 | + with dbuser('testadmin'): |
398 | + # Create a build with lots of attributes set. |
399 | + build = self.factory.makeBinaryPackageBuild() |
400 | + build.gotFailure() |
401 | + build.updateStatus( |
402 | + BuildStatus.BUILDING, builder=self.factory.makeBuilder()) |
403 | + build.updateStatus(BuildStatus.FULLYBUILT) |
404 | + build.setLog(self.factory.makeLibraryFileAlias()) |
405 | + build.storeUploadLog('uploaded') |
406 | + |
407 | + # Manually unset the build's denormed columns. |
408 | + attrs = ( |
409 | + 'archive', 'pocket', 'processor', 'virtualized', |
410 | + 'date_created', 'date_started', 'date_finished', |
411 | + 'date_first_dispatched', 'builder', 'status', 'log', |
412 | + 'upload_log', 'dependencies', 'failure_count', |
413 | + 'build_farm_job', 'distribution', 'distro_series', |
414 | + 'source_package_name', 'is_distro_archive') |
415 | + for attr in attrs: |
416 | + setattr(removeSecurityProxy(build), '_new_' + attr, None) |
417 | + removeSecurityProxy(build.build_farm_job).archive = None |
418 | + self.assertEqual( |
419 | + 1, store.find(BinaryPackageBuild, _new_archive=None).count()) |
420 | + self.runHourly() |
421 | + self.assertEqual( |
422 | + 0, store.find(BinaryPackageBuild, _new_archive=None).count()) |
423 | + |
424 | + self.assertThat( |
425 | + removeSecurityProxy(build), |
426 | + MatchesStructure.byEquality( |
427 | + **dict( |
428 | + ('_new_' + attr, getattr(build, attr)) for attr in attrs))) |
429 | + self.assertEqual( |
430 | + build.archive, removeSecurityProxy(build.build_farm_job).archive) |
431 | + |
432 | + def test_SourcePackageRecipeBuildFlattener(self): |
433 | + store = IMasterStore(BinaryPackageBuild) |
434 | + with dbuser('testadmin'): |
435 | + IMasterStore(FeatureFlag).add(FeatureFlag( |
436 | + u'default', 0, u'soyuz.flatten_bfj.garbo.enabled', u'true')) |
437 | + |
438 | + with dbuser('testadmin'): |
439 | + # Create a build with lots of attributes set. |
440 | + build = self.factory.makeSourcePackageRecipeBuild() |
441 | + build.gotFailure() |
442 | + build.updateStatus( |
443 | + BuildStatus.BUILDING, builder=self.factory.makeBuilder()) |
444 | + build.updateStatus(BuildStatus.FULLYBUILT) |
445 | + build.setLog(self.factory.makeLibraryFileAlias()) |
446 | + build.storeUploadLog('uploaded') |
447 | + |
448 | + # Manually unset the build's denormed columns. |
449 | + attrs = ( |
450 | + 'archive', 'pocket', 'processor', 'virtualized', |
451 | + 'date_created', 'date_started', 'date_finished', |
452 | + 'date_first_dispatched', 'builder', 'status', 'log', |
453 | + 'upload_log', 'dependencies', 'failure_count', |
454 | + 'build_farm_job') |
455 | + for attr in attrs: |
456 | + setattr(removeSecurityProxy(build), '_new_' + attr, None) |
457 | + removeSecurityProxy(build).build_farm_job.archive = None |
458 | + self.assertEqual( |
459 | + 1, store.find(SourcePackageRecipeBuild, _new_archive=None).count()) |
460 | + self.runHourly() |
461 | + self.assertEqual( |
462 | + 0, store.find(SourcePackageRecipeBuild, _new_archive=None).count()) |
463 | + |
464 | + self.assertThat( |
465 | + removeSecurityProxy(build), |
466 | + MatchesStructure.byEquality( |
467 | + **dict( |
468 | + ('_new_' + attr, getattr(build, attr)) for attr in attrs))) |
469 | + self.assertEqual( |
470 | + build.archive, removeSecurityProxy(build.build_farm_job).archive) |
471 | + |
472 | + def test_TranslationTemplatesBuildFlattener(self): |
473 | + store = IMasterStore(BinaryPackageBuild) |
474 | + with dbuser('testadmin'): |
475 | + IMasterStore(FeatureFlag).add(FeatureFlag( |
476 | + u'default', 0, u'soyuz.flatten_bfj.garbo.enabled', u'true')) |
477 | + |
478 | + with dbuser('testadmin'): |
479 | + # Create a build with lots of attributes set. |
480 | + build = self.factory.makeTranslationTemplatesBuildJob().build |
481 | + build.gotFailure() |
482 | + build.updateStatus( |
483 | + BuildStatus.BUILDING, builder=self.factory.makeBuilder()) |
484 | + build.updateStatus(BuildStatus.FULLYBUILT) |
485 | + build.setLog(self.factory.makeLibraryFileAlias()) |
486 | + |
487 | + # Manually unset the build's denormed columns. |
488 | + attrs = ( |
489 | + 'processor', 'virtualized', 'date_created', 'date_started', |
490 | + 'date_finished', 'date_first_dispatched', 'builder', 'status', |
491 | + 'log', 'failure_count') |
492 | + for attr in attrs: |
493 | + setattr(removeSecurityProxy(build), '_new_' + attr, None) |
494 | + self.assertEqual( |
495 | + 1, store.find(TranslationTemplatesBuild, _new_status=None).count()) |
496 | + self.runHourly() |
497 | + self.assertEqual( |
498 | + 0, store.find(TranslationTemplatesBuild, _new_status=None).count()) |
499 | + |
500 | + self.assertThat( |
501 | + removeSecurityProxy(build), |
502 | + MatchesStructure.byEquality( |
503 | + **dict( |
504 | + ('_new_' + attr, getattr(build, attr)) for attr in attrs))) |
505 | + |
506 | |
507 | class TestGarboTasks(TestCaseWithFactory): |
508 | layer = LaunchpadZopelessLayer |
509 | |
510 | === modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py' |
511 | --- lib/lp/soyuz/interfaces/binarypackagebuild.py 2013-01-22 08:31:09 +0000 |
512 | +++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2013-02-04 02:04:26 +0000 |
513 | @@ -100,6 +100,9 @@ |
514 | distro_series = Attribute("Direct parent needed by CanonicalURL") |
515 | arch_tag = exported( |
516 | Text(title=_("Architecture tag"), required=False)) |
517 | + source_package_name = Attribute("Source package name") |
518 | + is_distro_archive = Attribute( |
519 | + "Whether the target archive belongs to the distro") |
520 | distributionsourcepackagerelease = Attribute("The page showing the " |
521 | "details for this sourcepackagerelease in this distribution.") |
522 | binarypackages = Attribute( |
523 | |
524 | === modified file 'lib/lp/soyuz/model/binarypackagebuild.py' |
525 | --- lib/lp/soyuz/model/binarypackagebuild.py 2013-02-04 02:04:26 +0000 |
526 | +++ lib/lp/soyuz/model/binarypackagebuild.py 2013-02-04 02:04:26 +0000 |
527 | @@ -265,6 +265,16 @@ |
528 | return self.distro_series.distribution |
529 | |
530 | @property |
531 | + def source_package_name(self): |
532 | + """See `IBinaryPackageBuild`.""" |
533 | + return self.source_package_release.sourcepackagename |
534 | + |
535 | + @property |
536 | + def is_distro_archive(self): |
537 | + """See `IBinaryPackageBuild`.""" |
538 | + return self.archive.is_main |
539 | + |
540 | + @property |
541 | def is_virtualized(self): |
542 | """See `IBuild`""" |
543 | return self.archive.require_virtualized |
You could *almost* get away with refactoring this into a base class and then having 3 smaller classes that use the base as a super class. But this code should be short lived enough that it probably does not matter.
518 + is_distro_archive = Attribute(
519 + "Whether the target archive belongs to the distro")
Terrible message is terrible.