Merge lp:~allenap/launchpad/undo-read-only-transactions-in-buildmaster into lp:launchpad
- undo-read-only-transactions-in-buildmaster
- Merge into devel
Proposed by
Gavin Panella
Status: | Merged |
---|---|
Approved by: | Gavin Panella |
Approved revision: | no longer in the source branch. |
Merged at revision: | 14552 |
Proposed branch: | lp:~allenap/launchpad/undo-read-only-transactions-in-buildmaster |
Merge into: | lp:launchpad |
Diff against target: |
1620 lines (+251/-470) 13 files modified
lib/lp/archiveuploader/tests/test_uploadprocessor.py (+2/-4) lib/lp/buildmaster/interfaces/builder.py (+4/-6) lib/lp/buildmaster/manager.py (+50/-78) lib/lp/buildmaster/model/builder.py (+15/-28) lib/lp/buildmaster/model/buildfarmjobbehavior.py (+33/-63) lib/lp/buildmaster/model/packagebuild.py (+57/-91) lib/lp/buildmaster/tests/test_builder.py (+7/-27) lib/lp/buildmaster/tests/test_manager.py (+40/-127) lib/lp/buildmaster/tests/test_packagebuild.py (+20/-10) lib/lp/code/model/tests/test_sourcepackagerecipebuild.py (+8/-6) lib/lp/services/database/transaction_policy.py (+2/-5) lib/lp/soyuz/tests/test_binarypackagebuild.py (+4/-11) lib/lp/translations/model/translationtemplatesbuildbehavior.py (+9/-14) |
To merge this branch: | bzr merge lp:~allenap/launchpad/undo-read-only-transactions-in-buildmaster |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+86299@code.launchpad.net |
Commit message
[r=allenap][bug=905853,905855,906079] Revert r14499 and r14459 because read-only transactions in buildmaster are causing production issues.
Description of the change
Revert r14499 and r14459 because read-only transactions in buildmaster
are causing production issues. This should bring stable into line with
current cowboy on cesium.
To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py' |
2 | --- lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-12-13 17:10:46 +0000 |
3 | +++ lib/lp/archiveuploader/tests/test_uploadprocessor.py 2011-12-19 21:48:31 +0000 |
4 | @@ -629,8 +629,7 @@ |
5 | from_addr, to_addrs, raw_msg = stub.test_emails.pop() |
6 | foo_bar = "Foo Bar <foo.bar@canonical.com>" |
7 | daniel = "Daniel Silverstone <daniel.silverstone@canonical.com>" |
8 | - self.assertContentEqual( |
9 | - [foo_bar, daniel], [e.strip() for e in to_addrs]) |
10 | + self.assertEqual([e.strip() for e in to_addrs], [foo_bar, daniel]) |
11 | self.assertTrue( |
12 | "NEW" in raw_msg, "Expected email containing 'NEW', got:\n%s" |
13 | % raw_msg) |
14 | @@ -664,8 +663,7 @@ |
15 | from_addr, to_addrs, raw_msg = stub.test_emails.pop() |
16 | daniel = "Daniel Silverstone <daniel.silverstone@canonical.com>" |
17 | foo_bar = "Foo Bar <foo.bar@canonical.com>" |
18 | - self.assertContentEqual( |
19 | - [foo_bar, daniel], [e.strip() for e in to_addrs]) |
20 | + self.assertEqual([e.strip() for e in to_addrs], [foo_bar, daniel]) |
21 | self.assertTrue("Waiting for approval" in raw_msg, |
22 | "Expected an 'upload awaits approval' email.\n" |
23 | "Got:\n%s" % raw_msg) |
24 | |
25 | === modified file 'lib/lp/buildmaster/interfaces/builder.py' |
26 | --- lib/lp/buildmaster/interfaces/builder.py 2011-11-09 11:50:17 +0000 |
27 | +++ lib/lp/buildmaster/interfaces/builder.py 2011-12-19 21:48:31 +0000 |
28 | @@ -1,4 +1,4 @@ |
29 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
30 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
31 | # GNU Affero General Public License version 3 (see the file LICENSE). |
32 | |
33 | # pylint: disable-msg=E0211,E0213 |
34 | @@ -25,10 +25,10 @@ |
35 | export_as_webservice_entry, |
36 | export_read_operation, |
37 | exported, |
38 | - operation_for_version, |
39 | operation_parameters, |
40 | + operation_returns_entry, |
41 | operation_returns_collection_of, |
42 | - operation_returns_entry, |
43 | + operation_for_version, |
44 | ) |
45 | from lazr.restful.fields import ( |
46 | Reference, |
47 | @@ -50,12 +50,12 @@ |
48 | from lp.app.validators.name import name_validator |
49 | from lp.app.validators.url import builder_url_validator |
50 | from lp.registry.interfaces.role import IHasOwner |
51 | +from lp.soyuz.interfaces.processor import IProcessor |
52 | from lp.services.fields import ( |
53 | Description, |
54 | PersonChoice, |
55 | Title, |
56 | ) |
57 | -from lp.soyuz.interfaces.processor import IProcessor |
58 | |
59 | |
60 | class BuildDaemonError(Exception): |
61 | @@ -195,8 +195,6 @@ |
62 | |
63 | def setSlaveForTesting(proxy): |
64 | """Sets the RPC proxy through which to operate the build slave.""" |
65 | - # XXX JeroenVermeulen 2011-11-09, bug=888010: Don't use this. |
66 | - # It's a trap. See bug for details. |
67 | |
68 | def verifySlaveBuildCookie(slave_build_id): |
69 | """Verify that a slave's build cookie is consistent. |
70 | |
71 | === modified file 'lib/lp/buildmaster/manager.py' |
72 | --- lib/lp/buildmaster/manager.py 2011-12-08 11:39:10 +0000 |
73 | +++ lib/lp/buildmaster/manager.py 2011-12-19 21:48:31 +0000 |
74 | @@ -1,4 +1,4 @@ |
75 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
76 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
77 | # GNU Affero General Public License version 3 (see the file LICENSE). |
78 | |
79 | """Soyuz buildd slave manager logic.""" |
80 | @@ -23,6 +23,10 @@ |
81 | from zope.component import getUtility |
82 | |
83 | from lp.buildmaster.enums import BuildStatus |
84 | +from lp.buildmaster.interfaces.buildfarmjobbehavior import ( |
85 | + BuildBehaviorMismatch, |
86 | + ) |
87 | +from lp.buildmaster.model.builder import Builder |
88 | from lp.buildmaster.interfaces.builder import ( |
89 | BuildDaemonError, |
90 | BuildSlaveFailure, |
91 | @@ -30,11 +34,6 @@ |
92 | CannotFetchFile, |
93 | CannotResumeHost, |
94 | ) |
95 | -from lp.buildmaster.interfaces.buildfarmjobbehavior import ( |
96 | - BuildBehaviorMismatch, |
97 | - ) |
98 | -from lp.buildmaster.model.builder import Builder |
99 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
100 | |
101 | |
102 | BUILDD_MANAGER_LOG_NAME = "slave-scanner" |
103 | @@ -112,17 +111,13 @@ |
104 | # algorithm for polling. |
105 | SCAN_INTERVAL = 15 |
106 | |
107 | - def __init__(self, builder_name, logger, clock=None): |
108 | + def __init__(self, builder_name, logger): |
109 | self.builder_name = builder_name |
110 | self.logger = logger |
111 | - if clock is None: |
112 | - clock = reactor |
113 | - self._clock = clock |
114 | |
115 | def startCycle(self): |
116 | """Scan the builder and dispatch to it or deal with failures.""" |
117 | self.loop = LoopingCall(self.singleCycle) |
118 | - self.loop.clock = self._clock |
119 | self.stopping_deferred = self.loop.start(self.SCAN_INTERVAL) |
120 | return self.stopping_deferred |
121 | |
122 | @@ -143,58 +138,51 @@ |
123 | 1. Print the error in the log |
124 | 2. Increment and assess failure counts on the builder and job. |
125 | """ |
126 | - # Since this is a failure path, we could be in a broken |
127 | - # transaction. Get us a fresh one. |
128 | + # Make sure that pending database updates are removed as it |
129 | + # could leave the database in an inconsistent state (e.g. The |
130 | + # job says it's running but the buildqueue has no builder set). |
131 | transaction.abort() |
132 | |
133 | # If we don't recognise the exception include a stack trace with |
134 | # the error. |
135 | error_message = failure.getErrorMessage() |
136 | - familiar_error = failure.check( |
137 | + if failure.check( |
138 | BuildSlaveFailure, CannotBuild, BuildBehaviorMismatch, |
139 | - CannotResumeHost, BuildDaemonError, CannotFetchFile) |
140 | - if familiar_error: |
141 | - self.logger.info( |
142 | - "Scanning %s failed with: %s", |
143 | - self.builder_name, error_message) |
144 | + CannotResumeHost, BuildDaemonError, CannotFetchFile): |
145 | + self.logger.info("Scanning %s failed with: %s" % ( |
146 | + self.builder_name, error_message)) |
147 | else: |
148 | - self.logger.info( |
149 | - "Scanning %s failed with: %s\n%s", |
150 | + self.logger.info("Scanning %s failed with: %s\n%s" % ( |
151 | self.builder_name, failure.getErrorMessage(), |
152 | - failure.getTraceback()) |
153 | + failure.getTraceback())) |
154 | |
155 | # Decide if we need to terminate the job or fail the |
156 | # builder. |
157 | try: |
158 | builder = get_builder(self.builder_name) |
159 | - transaction.commit() |
160 | - |
161 | - with DatabaseTransactionPolicy(read_only=False): |
162 | - builder.gotFailure() |
163 | - |
164 | - if builder.currentjob is None: |
165 | - self.logger.info( |
166 | - "Builder %s failed a probe, count: %s", |
167 | - self.builder_name, builder.failure_count) |
168 | - else: |
169 | - build_farm_job = builder.getCurrentBuildFarmJob() |
170 | - build_farm_job.gotFailure() |
171 | - self.logger.info( |
172 | - "builder %s failure count: %s, " |
173 | - "job '%s' failure count: %s", |
174 | + builder.gotFailure() |
175 | + if builder.currentjob is not None: |
176 | + build_farm_job = builder.getCurrentBuildFarmJob() |
177 | + build_farm_job.gotFailure() |
178 | + self.logger.info( |
179 | + "builder %s failure count: %s, " |
180 | + "job '%s' failure count: %s" % ( |
181 | self.builder_name, |
182 | builder.failure_count, |
183 | build_farm_job.title, |
184 | - build_farm_job.failure_count) |
185 | - |
186 | - assessFailureCounts(builder, failure.getErrorMessage()) |
187 | - transaction.commit() |
188 | + build_farm_job.failure_count)) |
189 | + else: |
190 | + self.logger.info( |
191 | + "Builder %s failed a probe, count: %s" % ( |
192 | + self.builder_name, builder.failure_count)) |
193 | + assessFailureCounts(builder, failure.getErrorMessage()) |
194 | + transaction.commit() |
195 | except: |
196 | # Catastrophic code failure! Not much we can do. |
197 | - transaction.abort() |
198 | self.logger.error( |
199 | "Miserable failure when trying to examine failure counts:\n", |
200 | exc_info=True) |
201 | + transaction.abort() |
202 | |
203 | def checkCancellation(self, builder): |
204 | """See if there is a pending cancellation request. |
205 | @@ -248,9 +236,14 @@ |
206 | """ |
207 | # We need to re-fetch the builder object on each cycle as the |
208 | # Storm store is invalidated over transaction boundaries. |
209 | + |
210 | self.builder = get_builder(self.builder_name) |
211 | |
212 | def status_updated(ignored): |
213 | + # Commit the changes done while possibly rescuing jobs, to |
214 | + # avoid holding table locks. |
215 | + transaction.commit() |
216 | + |
217 | # See if we think there's an active build on the builder. |
218 | buildqueue = self.builder.getBuildQueue() |
219 | |
220 | @@ -260,10 +253,14 @@ |
221 | return self.builder.updateBuild(buildqueue) |
222 | |
223 | def build_updated(ignored): |
224 | + # Commit changes done while updating the build, to avoid |
225 | + # holding table locks. |
226 | + transaction.commit() |
227 | + |
228 | # If the builder is in manual mode, don't dispatch anything. |
229 | if self.builder.manual: |
230 | self.logger.debug( |
231 | - '%s is in manual mode, not dispatching.', |
232 | + '%s is in manual mode, not dispatching.' % |
233 | self.builder.name) |
234 | return |
235 | |
236 | @@ -281,33 +278,22 @@ |
237 | job = self.builder.currentjob |
238 | if job is not None and not self.builder.builderok: |
239 | self.logger.info( |
240 | - "%s was made unavailable; resetting attached job.", |
241 | - self.builder.name) |
242 | + "%s was made unavailable, resetting attached " |
243 | + "job" % self.builder.name) |
244 | + job.reset() |
245 | transaction.commit() |
246 | - with DatabaseTransactionPolicy(read_only=False): |
247 | - job.reset() |
248 | - transaction.commit() |
249 | return |
250 | |
251 | # See if there is a job we can dispatch to the builder slave. |
252 | |
253 | - # XXX JeroenVermeulen 2011-10-11, bug=872112: The job's |
254 | - # failure count will be reset once the job has started |
255 | - # successfully. Because of intervening commits, you may see |
256 | - # a build with a nonzero failure count that's actually going |
257 | - # to succeed later (and have a failure count of zero). Or |
258 | - # it may fail yet end up with a lower failure count than you |
259 | - # saw earlier. |
260 | d = self.builder.findAndStartJob() |
261 | |
262 | def job_started(candidate): |
263 | if self.builder.currentjob is not None: |
264 | # After a successful dispatch we can reset the |
265 | # failure_count. |
266 | + self.builder.resetFailureCount() |
267 | transaction.commit() |
268 | - with DatabaseTransactionPolicy(read_only=False): |
269 | - self.builder.resetFailureCount() |
270 | - transaction.commit() |
271 | return self.builder.slave |
272 | else: |
273 | return None |
274 | @@ -386,7 +372,6 @@ |
275 | self.logger = self._setupLogger() |
276 | self.new_builders_scanner = NewBuildersScanner( |
277 | manager=self, clock=clock) |
278 | - self.transaction_policy = DatabaseTransactionPolicy(read_only=True) |
279 | |
280 | def _setupLogger(self): |
281 | """Set up a 'slave-scanner' logger that redirects to twisted. |
282 | @@ -405,28 +390,16 @@ |
283 | logger.setLevel(level) |
284 | return logger |
285 | |
286 | - def enterReadOnlyDatabasePolicy(self): |
287 | - """Set the database transaction policy to read-only. |
288 | - |
289 | - Any previously pending changes are committed first. |
290 | - """ |
291 | - transaction.commit() |
292 | - self.transaction_policy.__enter__() |
293 | - |
294 | - def exitReadOnlyDatabasePolicy(self, *args): |
295 | - """Reset database transaction policy to the default read-write.""" |
296 | - self.transaction_policy.__exit__(None, None, None) |
297 | - |
298 | def startService(self): |
299 | """Service entry point, called when the application starts.""" |
300 | + |
301 | + # Get a list of builders and set up scanners on each one. |
302 | + |
303 | # Avoiding circular imports. |
304 | from lp.buildmaster.interfaces.builder import IBuilderSet |
305 | - |
306 | - self.enterReadOnlyDatabasePolicy() |
307 | - |
308 | - # Get a list of builders and set up scanners on each one. |
309 | - self.addScanForBuilders( |
310 | - [builder.name for builder in getUtility(IBuilderSet)]) |
311 | + builder_set = getUtility(IBuilderSet) |
312 | + builders = [builder.name for builder in builder_set] |
313 | + self.addScanForBuilders(builders) |
314 | self.new_builders_scanner.scheduleScan() |
315 | |
316 | # Events will now fire in the SlaveScanner objects to scan each |
317 | @@ -447,7 +420,6 @@ |
318 | # stopped, so we can wait on them all at once here before |
319 | # exiting. |
320 | d = defer.DeferredList(deferreds, consumeErrors=True) |
321 | - d.addCallback(self.exitReadOnlyDatabasePolicy) |
322 | return d |
323 | |
324 | def addScanForBuilders(self, builders): |
325 | |
326 | === modified file 'lib/lp/buildmaster/model/builder.py' |
327 | --- lib/lp/buildmaster/model/builder.py 2011-12-13 13:33:04 +0000 |
328 | +++ lib/lp/buildmaster/model/builder.py 2011-12-19 21:48:31 +0000 |
329 | @@ -1,4 +1,4 @@ |
330 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
331 | +# Copyright 2009,2011 Canonical Ltd. This software is licensed under the |
332 | # GNU Affero General Public License version 3 (see the file LICENSE). |
333 | |
334 | # pylint: disable-msg=E0611,W0212 |
335 | @@ -76,7 +76,6 @@ |
336 | specific_job_classes, |
337 | ) |
338 | from lp.registry.interfaces.person import validate_public_person |
339 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
340 | from lp.services.job.interfaces.job import JobStatus |
341 | from lp.services.job.model.job import Job |
342 | from lp.services.propertycache import ( |
343 | @@ -546,8 +545,6 @@ |
344 | |
345 | def setSlaveForTesting(self, proxy): |
346 | """See IBuilder.""" |
347 | - # XXX JeroenVermeulen 2011-11-09, bug=888010: Don't use this. |
348 | - # It's a trap. See bug for details. |
349 | self._testing_slave = proxy |
350 | del get_property_cache(self).slave |
351 | |
352 | @@ -676,13 +673,10 @@ |
353 | bytes_written = out_file.tell() |
354 | out_file.seek(0) |
355 | |
356 | - transaction.commit() |
357 | - with DatabaseTransactionPolicy(read_only=False): |
358 | - library_file = getUtility(ILibraryFileAliasSet).create( |
359 | - filename, bytes_written, out_file, |
360 | - contentType=filenameToContentType(filename), |
361 | - restricted=private) |
362 | - transaction.commit() |
363 | + library_file = getUtility(ILibraryFileAliasSet).create( |
364 | + filename, bytes_written, out_file, |
365 | + contentType=filenameToContentType(filename), |
366 | + restricted=private) |
367 | finally: |
368 | # Remove the temporary file. getFile() closes the file |
369 | # object. |
370 | @@ -720,7 +714,7 @@ |
371 | def acquireBuildCandidate(self): |
372 | """Acquire a build candidate in an atomic fashion. |
373 | |
374 | - When retrieving a candidate we need to mark it as building |
375 | + When retrieiving a candidate we need to mark it as building |
376 | immediately so that it is not dispatched by another builder in the |
377 | build manager. |
378 | |
379 | @@ -730,15 +724,12 @@ |
380 | can be in this code at the same time. |
381 | |
382 | If there's ever more than one build manager running at once, then |
383 | - this code will need some sort of mutex, or run in a single |
384 | - transaction. |
385 | + this code will need some sort of mutex. |
386 | """ |
387 | candidate = self._findBuildCandidate() |
388 | if candidate is not None: |
389 | + candidate.markAsBuilding(self) |
390 | transaction.commit() |
391 | - with DatabaseTransactionPolicy(read_only=False): |
392 | - candidate.markAsBuilding(self) |
393 | - transaction.commit() |
394 | return candidate |
395 | |
396 | def _findBuildCandidate(self): |
397 | @@ -801,17 +792,13 @@ |
398 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
399 | candidate_jobs = store.execute(query).get_all() |
400 | |
401 | - transaction.commit() |
402 | - with DatabaseTransactionPolicy(read_only=False): |
403 | - for (candidate_id,) in candidate_jobs: |
404 | - candidate = getUtility(IBuildQueueSet).get(candidate_id) |
405 | - job_class = job_classes[candidate.job_type] |
406 | - candidate_approved = job_class.postprocessCandidate( |
407 | - candidate, logger) |
408 | - if candidate_approved: |
409 | - transaction.commit() |
410 | - return candidate |
411 | - transaction.commit() |
412 | + for (candidate_id,) in candidate_jobs: |
413 | + candidate = getUtility(IBuildQueueSet).get(candidate_id) |
414 | + job_class = job_classes[candidate.job_type] |
415 | + candidate_approved = job_class.postprocessCandidate( |
416 | + candidate, logger) |
417 | + if candidate_approved: |
418 | + return candidate |
419 | |
420 | return None |
421 | |
422 | |
423 | === modified file 'lib/lp/buildmaster/model/buildfarmjobbehavior.py' |
424 | --- lib/lp/buildmaster/model/buildfarmjobbehavior.py 2011-10-11 07:15:10 +0000 |
425 | +++ lib/lp/buildmaster/model/buildfarmjobbehavior.py 2011-12-19 21:48:31 +0000 |
426 | @@ -1,4 +1,4 @@ |
427 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
428 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
429 | # GNU Affero General Public License version 3 (see the file LICENSE). |
430 | |
431 | # pylint: disable-msg=E0211,E0213 |
432 | @@ -16,8 +16,8 @@ |
433 | import socket |
434 | import xmlrpclib |
435 | |
436 | -import transaction |
437 | from twisted.internet import defer |
438 | + |
439 | from zope.component import getUtility |
440 | from zope.interface import implements |
441 | from zope.security.proxy import removeSecurityProxy |
442 | @@ -32,7 +32,6 @@ |
443 | IBuildFarmJobBehavior, |
444 | ) |
445 | from lp.services import encoding |
446 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
447 | from lp.services.job.interfaces.job import JobStatus |
448 | |
449 | |
450 | @@ -71,25 +70,6 @@ |
451 | if slave_build_cookie != expected_cookie: |
452 | raise CorruptBuildCookie("Invalid slave build cookie.") |
453 | |
454 | - def _getBuilderStatusHandler(self, status_text, logger): |
455 | - """Look up the handler method for a given builder status. |
456 | - |
457 | - If status is not a known one, logs an error and returns None. |
458 | - """ |
459 | - builder_status_handlers = { |
460 | - 'BuilderStatus.IDLE': self.updateBuild_IDLE, |
461 | - 'BuilderStatus.BUILDING': self.updateBuild_BUILDING, |
462 | - 'BuilderStatus.ABORTING': self.updateBuild_ABORTING, |
463 | - 'BuilderStatus.ABORTED': self.updateBuild_ABORTED, |
464 | - 'BuilderStatus.WAITING': self.updateBuild_WAITING, |
465 | - } |
466 | - handler = builder_status_handlers.get(status_text) |
467 | - if handler is None: |
468 | - logger.critical( |
469 | - "Builder on %s returned unknown status %s; failing it.", |
470 | - self._builder.url, status_text) |
471 | - return handler |
472 | - |
473 | def updateBuild(self, queueItem): |
474 | """See `IBuildFarmJobBehavior`.""" |
475 | logger = logging.getLogger('slave-scanner') |
476 | @@ -97,7 +77,6 @@ |
477 | d = self._builder.slaveStatus() |
478 | |
479 | def got_failure(failure): |
480 | - transaction.abort() |
481 | failure.trap(xmlrpclib.Fault, socket.error) |
482 | info = failure.value |
483 | info = ("Could not contact the builder %s, caught a (%s)" |
484 | @@ -105,22 +84,27 @@ |
485 | raise BuildSlaveFailure(info) |
486 | |
487 | def got_status(slave_status): |
488 | + builder_status_handlers = { |
489 | + 'BuilderStatus.IDLE': self.updateBuild_IDLE, |
490 | + 'BuilderStatus.BUILDING': self.updateBuild_BUILDING, |
491 | + 'BuilderStatus.ABORTING': self.updateBuild_ABORTING, |
492 | + 'BuilderStatus.ABORTED': self.updateBuild_ABORTED, |
493 | + 'BuilderStatus.WAITING': self.updateBuild_WAITING, |
494 | + } |
495 | + |
496 | builder_status = slave_status['builder_status'] |
497 | - status_handler = self._getBuilderStatusHandler( |
498 | - builder_status, logger) |
499 | - if status_handler is None: |
500 | - error = ( |
501 | + if builder_status not in builder_status_handlers: |
502 | + logger.critical( |
503 | + "Builder on %s returned unknown status %s, failing it" |
504 | + % (self._builder.url, builder_status)) |
505 | + self._builder.failBuilder( |
506 | "Unknown status code (%s) returned from status() probe." |
507 | % builder_status) |
508 | - transaction.commit() |
509 | - with DatabaseTransactionPolicy(read_only=False): |
510 | - self._builder.failBuilder(error) |
511 | - # XXX: This will leave the build and job in a bad |
512 | - # state, but should never be possible since our |
513 | - # builder statuses are known. |
514 | - queueItem._builder = None |
515 | - queueItem.setDateStarted(None) |
516 | - transaction.commit() |
517 | + # XXX: This will leave the build and job in a bad state, but |
518 | + # should never be possible, since our builder statuses are |
519 | + # known. |
520 | + queueItem._builder = None |
521 | + queueItem.setDateStarted(None) |
522 | return |
523 | |
524 | # Since logtail is a xmlrpclib.Binary container and it is |
525 | @@ -130,8 +114,9 @@ |
526 | # will simply remove the proxy. |
527 | logtail = removeSecurityProxy(slave_status.get('logtail')) |
528 | |
529 | + method = builder_status_handlers[builder_status] |
530 | return defer.maybeDeferred( |
531 | - status_handler, queueItem, slave_status, logtail, logger) |
532 | + method, queueItem, slave_status, logtail, logger) |
533 | |
534 | d.addErrback(got_failure) |
535 | d.addCallback(got_status) |
536 | @@ -143,32 +128,22 @@ |
537 | Log this and reset the record. |
538 | """ |
539 | logger.warn( |
540 | - "Builder %s forgot about buildqueue %d -- " |
541 | - "resetting buildqueue record.", |
542 | - queueItem.builder.url, queueItem.id) |
543 | - transaction.commit() |
544 | - with DatabaseTransactionPolicy(read_only=False): |
545 | - queueItem.reset() |
546 | - transaction.commit() |
547 | + "Builder %s forgot about buildqueue %d -- resetting buildqueue " |
548 | + "record" % (queueItem.builder.url, queueItem.id)) |
549 | + queueItem.reset() |
550 | |
551 | def updateBuild_BUILDING(self, queueItem, slave_status, logtail, logger): |
552 | """Build still building, collect the logtail""" |
553 | - transaction.commit() |
554 | - with DatabaseTransactionPolicy(read_only=False): |
555 | - if queueItem.job.status != JobStatus.RUNNING: |
556 | - queueItem.job.start() |
557 | - queueItem.logtail = encoding.guess(str(logtail)) |
558 | - transaction.commit() |
559 | + if queueItem.job.status != JobStatus.RUNNING: |
560 | + queueItem.job.start() |
561 | + queueItem.logtail = encoding.guess(str(logtail)) |
562 | |
563 | def updateBuild_ABORTING(self, queueItem, slave_status, logtail, logger): |
564 | """Build was ABORTED. |
565 | |
566 | Master-side should wait until the slave finish the process correctly. |
567 | """ |
568 | - transaction.commit() |
569 | - with DatabaseTransactionPolicy(read_only=False): |
570 | - queueItem.logtail = "Waiting for slave process to be terminated" |
571 | - transaction.commit() |
572 | + queueItem.logtail = "Waiting for slave process to be terminated" |
573 | |
574 | def updateBuild_ABORTED(self, queueItem, slave_status, logtail, logger): |
575 | """ABORTING process has successfully terminated. |
576 | @@ -176,16 +151,11 @@ |
577 | Clean the builder for another jobs. |
578 | """ |
579 | d = queueItem.builder.cleanSlave() |
580 | - |
581 | def got_cleaned(ignored): |
582 | - transaction.commit() |
583 | - with DatabaseTransactionPolicy(read_only=False): |
584 | - queueItem.builder = None |
585 | - if queueItem.job.status != JobStatus.FAILED: |
586 | - queueItem.job.fail() |
587 | - queueItem.specific_job.jobAborted() |
588 | - transaction.commit() |
589 | - |
590 | + queueItem.builder = None |
591 | + if queueItem.job.status != JobStatus.FAILED: |
592 | + queueItem.job.fail() |
593 | + queueItem.specific_job.jobAborted() |
594 | return d.addCallback(got_cleaned) |
595 | |
596 | def extractBuildStatus(self, slave_status): |
597 | |
598 | === modified file 'lib/lp/buildmaster/model/packagebuild.py' |
599 | --- lib/lp/buildmaster/model/packagebuild.py 2011-12-08 11:39:10 +0000 |
600 | +++ lib/lp/buildmaster/model/packagebuild.py 2011-12-19 21:48:31 +0000 |
601 | @@ -1,4 +1,4 @@ |
602 | -# Copyright 2010-2011 Canonical Ltd. This software is licensed under the |
603 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
604 | # GNU Affero General Public License version 3 (see the file LICENSE). |
605 | |
606 | __metaclass__ = type |
607 | @@ -9,11 +9,11 @@ |
608 | ] |
609 | |
610 | |
611 | -from cStringIO import StringIO |
612 | import datetime |
613 | import logging |
614 | import os.path |
615 | |
616 | +from cStringIO import StringIO |
617 | from lazr.delegates import delegates |
618 | import pytz |
619 | from storm.expr import Desc |
620 | @@ -24,7 +24,6 @@ |
621 | Storm, |
622 | Unicode, |
623 | ) |
624 | -import transaction |
625 | from zope.component import getUtility |
626 | from zope.interface import ( |
627 | classProvides, |
628 | @@ -44,8 +43,8 @@ |
629 | MAIN_STORE, |
630 | ) |
631 | from lp.buildmaster.enums import ( |
632 | + BuildStatus, |
633 | BuildFarmJobType, |
634 | - BuildStatus, |
635 | ) |
636 | from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource |
637 | from lp.buildmaster.interfaces.packagebuild import ( |
638 | @@ -58,8 +57,9 @@ |
639 | BuildFarmJobDerived, |
640 | ) |
641 | from lp.buildmaster.model.buildqueue import BuildQueue |
642 | -from lp.registry.interfaces.pocket import PackagePublishingPocket |
643 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
644 | +from lp.registry.interfaces.pocket import ( |
645 | + PackagePublishingPocket, |
646 | + ) |
647 | from lp.soyuz.adapters.archivedependencies import ( |
648 | default_component_dependency_name, |
649 | ) |
650 | @@ -179,24 +179,19 @@ |
651 | def storeBuildInfo(build, librarian, slave_status): |
652 | """See `IPackageBuild`.""" |
653 | def got_log(lfa_id): |
654 | - dependencies = slave_status.get('dependencies') |
655 | - if dependencies is not None: |
656 | - dependencies = unicode(dependencies) |
657 | - |
658 | # log, builder and date_finished are read-only, so we must |
659 | # currently remove the security proxy to set them. |
660 | naked_build = removeSecurityProxy(build) |
661 | - |
662 | - transaction.commit() |
663 | - with DatabaseTransactionPolicy(read_only=False): |
664 | - naked_build.log = lfa_id |
665 | - naked_build.builder = build.buildqueue_record.builder |
666 | - # XXX cprov 20060615 bug=120584: Currently buildduration |
667 | - # includes the scanner latency. It should really be asking |
668 | - # the slave for the duration spent building locally. |
669 | - naked_build.date_finished = datetime.datetime.now(pytz.UTC) |
670 | - build.dependencies = dependencies |
671 | - transaction.commit() |
672 | + naked_build.log = lfa_id |
673 | + naked_build.builder = build.buildqueue_record.builder |
674 | + # XXX cprov 20060615 bug=120584: Currently buildduration includes |
675 | + # the scanner latency, it should really be asking the slave for |
676 | + # the duration spent building locally. |
677 | + naked_build.date_finished = datetime.datetime.now(pytz.UTC) |
678 | + if slave_status.get('dependencies') is not None: |
679 | + build.dependencies = unicode(slave_status.get('dependencies')) |
680 | + else: |
681 | + build.dependencies = None |
682 | |
683 | d = build.getLogFromSlave(build) |
684 | return d.addCallback(got_log) |
685 | @@ -297,41 +292,22 @@ |
686 | |
687 | def handleStatus(self, status, librarian, slave_status): |
688 | """See `IPackageBuild`.""" |
689 | - # Avoid circular imports. |
690 | from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME |
691 | - |
692 | logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME) |
693 | send_notification = status in self.ALLOWED_STATUS_NOTIFICATIONS |
694 | method = getattr(self, '_handleStatus_' + status, None) |
695 | if method is None: |
696 | - logger.critical( |
697 | - "Unknown BuildStatus '%s' for builder '%s'", |
698 | - status, self.buildqueue_record.builder.url) |
699 | - return None |
700 | - |
701 | + logger.critical("Unknown BuildStatus '%s' for builder '%s'" |
702 | + % (status, self.buildqueue_record.builder.url)) |
703 | + return |
704 | d = method(librarian, slave_status, logger, send_notification) |
705 | return d |
706 | |
707 | - def _destroy_buildqueue_record(self, unused_arg): |
708 | - """Destroy this build's `BuildQueue` record.""" |
709 | - transaction.commit() |
710 | - with DatabaseTransactionPolicy(read_only=False): |
711 | - self.buildqueue_record.destroySelf() |
712 | - transaction.commit() |
713 | - |
714 | def _release_builder_and_remove_queue_item(self): |
715 | # Release the builder for another job. |
716 | d = self.buildqueue_record.builder.cleanSlave() |
717 | # Remove BuildQueue record. |
718 | - return d.addCallback(self._destroy_buildqueue_record) |
719 | - |
720 | - def _notify_if_appropriate(self, appropriate=True, extra_info=None): |
721 | - """If `appropriate`, call `self.notify` in a write transaction.""" |
722 | - if appropriate: |
723 | - transaction.commit() |
724 | - with DatabaseTransactionPolicy(read_only=False): |
725 | - self.notify(extra_info=extra_info) |
726 | - transaction.commit() |
727 | + return d.addCallback(lambda x: self.buildqueue_record.destroySelf()) |
728 | |
729 | def _handleStatus_OK(self, librarian, slave_status, logger, |
730 | send_notification): |
731 | @@ -347,19 +323,16 @@ |
732 | self.buildqueue_record.specific_job.build.title, |
733 | self.buildqueue_record.builder.name)) |
734 | |
735 | - # If this is a binary package build for a source that is no |
736 | - # longer published, discard it. |
737 | + # If this is a binary package build, discard it if its source is |
738 | + # no longer published. |
739 | if self.build_farm_job_type == BuildFarmJobType.PACKAGEBUILD: |
740 | build = self.buildqueue_record.specific_job.build |
741 | if not build.current_source_publication: |
742 | - transaction.commit() |
743 | - with DatabaseTransactionPolicy(read_only=False): |
744 | - build.status = BuildStatus.SUPERSEDED |
745 | - transaction.commit() |
746 | + build.status = BuildStatus.SUPERSEDED |
747 | return self._release_builder_and_remove_queue_item() |
748 | |
749 | - # Explode rather than collect a binary that is denied in this |
750 | - # distroseries/pocket. |
751 | + # Explode before collect a binary that is denied in this |
752 | + # distroseries/pocket |
753 | if not self.archive.allowUpdatesToReleasePocket(): |
754 | assert self.distro_series.canUploadToPocket(self.pocket), ( |
755 | "%s (%s) can not be built for pocket %s: illegal status" |
756 | @@ -404,26 +377,18 @@ |
757 | # files from the slave. |
758 | if successful_copy_from_slave: |
759 | logger.info( |
760 | - "Gathered %s %d completely. " |
761 | - "Moving %s to uploader queue.", |
762 | - self.__class__.__name__, self.id, upload_leaf) |
763 | + "Gathered %s %d completely. Moving %s to uploader queue." |
764 | + % (self.__class__.__name__, self.id, upload_leaf)) |
765 | target_dir = os.path.join(root, "incoming") |
766 | - resulting_status = BuildStatus.UPLOADING |
767 | + self.status = BuildStatus.UPLOADING |
768 | else: |
769 | logger.warning( |
770 | - "Copy from slave for build %s was unsuccessful.", |
771 | - self.id) |
772 | + "Copy from slave for build %s was unsuccessful.", self.id) |
773 | + self.status = BuildStatus.FAILEDTOUPLOAD |
774 | + if send_notification: |
775 | + self.notify( |
776 | + extra_info='Copy from slave was unsuccessful.') |
777 | target_dir = os.path.join(root, "failed") |
778 | - resulting_status = BuildStatus.FAILEDTOUPLOAD |
779 | - |
780 | - transaction.commit() |
781 | - with DatabaseTransactionPolicy(read_only=False): |
782 | - self.status = resulting_status |
783 | - transaction.commit() |
784 | - |
785 | - if not successful_copy_from_slave: |
786 | - self._notify_if_appropriate( |
787 | - send_notification, "Copy from slave was unsuccessful.") |
788 | |
789 | if not os.path.exists(target_dir): |
790 | os.mkdir(target_dir) |
791 | @@ -431,6 +396,10 @@ |
792 | # Release the builder for another job. |
793 | d = self._release_builder_and_remove_queue_item() |
794 | |
795 | + # Commit so there are no race conditions with archiveuploader |
796 | + # about self.status. |
797 | + Store.of(self).commit() |
798 | + |
799 | # Move the directory used to grab the binaries into |
800 | # the incoming directory so the upload processor never |
801 | # sees half-finished uploads. |
802 | @@ -454,15 +423,14 @@ |
803 | set the job status as FAILEDTOBUILD, store available info and |
804 | remove Buildqueue entry. |
805 | """ |
806 | - transaction.commit() |
807 | - with DatabaseTransactionPolicy(read_only=False): |
808 | - self.status = BuildStatus.FAILEDTOBUILD |
809 | - transaction.commit() |
810 | + self.status = BuildStatus.FAILEDTOBUILD |
811 | |
812 | def build_info_stored(ignored): |
813 | - self._notify_if_appropriate(send_notification) |
814 | + if send_notification: |
815 | + self.notify() |
816 | d = self.buildqueue_record.builder.cleanSlave() |
817 | - return d.addCallback(self._destroy_buildqueue_record) |
818 | + return d.addCallback( |
819 | + lambda x: self.buildqueue_record.destroySelf()) |
820 | |
821 | d = self.storeBuildInfo(self, librarian, slave_status) |
822 | return d.addCallback(build_info_stored) |
823 | @@ -480,9 +448,11 @@ |
824 | def build_info_stored(ignored): |
825 | logger.critical("***** %s is MANUALDEPWAIT *****" |
826 | % self.buildqueue_record.builder.name) |
827 | - self._notify_if_appropriate(send_notification) |
828 | + if send_notification: |
829 | + self.notify() |
830 | d = self.buildqueue_record.builder.cleanSlave() |
831 | - return d.addCallback(self._destroy_buildqueue_record) |
832 | + return d.addCallback( |
833 | + lambda x: self.buildqueue_record.destroySelf()) |
834 | |
835 | d = self.storeBuildInfo(self, librarian, slave_status) |
836 | return d.addCallback(build_info_stored) |
837 | @@ -498,24 +468,17 @@ |
838 | self.status = BuildStatus.CHROOTWAIT |
839 | |
840 | def build_info_stored(ignored): |
841 | - logger.critical( |
842 | - "***** %s is CHROOTWAIT *****", |
843 | - self.buildqueue_record.builder.name) |
844 | - |
845 | - self._notify_if_appropriate(send_notification) |
846 | + logger.critical("***** %s is CHROOTWAIT *****" % |
847 | + self.buildqueue_record.builder.name) |
848 | + if send_notification: |
849 | + self.notify() |
850 | d = self.buildqueue_record.builder.cleanSlave() |
851 | - return d.addCallback(self._destroy_buildqueue_record) |
852 | + return d.addCallback( |
853 | + lambda x: self.buildqueue_record.destroySelf()) |
854 | |
855 | d = self.storeBuildInfo(self, librarian, slave_status) |
856 | return d.addCallback(build_info_stored) |
857 | |
858 | - def _reset_buildqueue_record(self, ignored_arg=None): |
859 | - """Reset the `BuildQueue` record, in a write transaction.""" |
860 | - transaction.commit() |
861 | - with DatabaseTransactionPolicy(read_only=False): |
862 | - self.buildqueue_record.reset() |
863 | - transaction.commit() |
864 | - |
865 | def _handleStatus_BUILDERFAIL(self, librarian, slave_status, logger, |
866 | send_notification): |
867 | """Handle builder failures. |
868 | @@ -529,8 +492,11 @@ |
869 | self.buildqueue_record.builder.failBuilder( |
870 | "Builder returned BUILDERFAIL when asked for its status") |
871 | |
872 | + def build_info_stored(ignored): |
873 | + # simply reset job |
874 | + self.buildqueue_record.reset() |
875 | d = self.storeBuildInfo(self, librarian, slave_status) |
876 | - return d.addCallback(self._reset_buildqueue_record) |
877 | + return d.addCallback(build_info_stored) |
878 | |
879 | def _handleStatus_GIVENBACK(self, librarian, slave_status, logger, |
880 | send_notification): |
881 | @@ -550,7 +516,7 @@ |
882 | # the next Paris Summit, infinity has some ideas about how |
883 | # to use this content. For now we just ensure it's stored. |
884 | d = self.buildqueue_record.builder.cleanSlave() |
885 | - self._reset_buildqueue_record() |
886 | + self.buildqueue_record.reset() |
887 | return d |
888 | |
889 | d = self.storeBuildInfo(self, librarian, slave_status) |
890 | |
891 | === modified file 'lib/lp/buildmaster/tests/test_builder.py' |
892 | --- lib/lp/buildmaster/tests/test_builder.py 2011-12-13 13:33:04 +0000 |
893 | +++ lib/lp/buildmaster/tests/test_builder.py 2011-12-19 21:48:31 +0000 |
894 | @@ -1,4 +1,4 @@ |
895 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
896 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
897 | # GNU Affero General Public License version 3 (see the file LICENSE). |
898 | |
899 | """Test Builder features.""" |
900 | @@ -8,14 +8,13 @@ |
901 | import tempfile |
902 | import xmlrpclib |
903 | |
904 | -from lpbuildd.slave import BuilderStatus |
905 | from testtools.deferredruntest import ( |
906 | assert_fails_with, |
907 | AsynchronousDeferredRunTest, |
908 | AsynchronousDeferredRunTestForBrokenTwisted, |
909 | SynchronousDeferredRunTest, |
910 | ) |
911 | -import transaction |
912 | + |
913 | from twisted.internet.defer import ( |
914 | CancelledError, |
915 | DeferredList, |
916 | @@ -23,12 +22,15 @@ |
917 | from twisted.internet.task import Clock |
918 | from twisted.python.failure import Failure |
919 | from twisted.web.client import getPage |
920 | + |
921 | from zope.component import getUtility |
922 | from zope.security.proxy import ( |
923 | isinstance as zope_isinstance, |
924 | removeSecurityProxy, |
925 | ) |
926 | |
927 | +from lpbuildd.slave import BuilderStatus |
928 | + |
929 | from canonical.config import config |
930 | from canonical.database.sqlbase import flush_database_updates |
931 | from canonical.launchpad.webapp.interfaces import ( |
932 | @@ -43,7 +45,6 @@ |
933 | from lp.buildmaster.enums import BuildStatus |
934 | from lp.buildmaster.interfaces.builder import ( |
935 | CannotFetchFile, |
936 | - CannotResumeHost, |
937 | IBuilder, |
938 | IBuilderSet, |
939 | ) |
940 | @@ -51,6 +52,7 @@ |
941 | IBuildFarmJobBehavior, |
942 | ) |
943 | from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet |
944 | +from lp.buildmaster.interfaces.builder import CannotResumeHost |
945 | from lp.buildmaster.model.builder import ( |
946 | BuilderSlave, |
947 | ProxyWithConnectionTimeout, |
948 | @@ -72,8 +74,6 @@ |
949 | TrivialBehavior, |
950 | WaitingSlave, |
951 | ) |
952 | -from lp.registry.interfaces.pocket import PackagePublishingPocket |
953 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
954 | from lp.services.job.interfaces.job import JobStatus |
955 | from lp.services.log.logger import BufferLogger |
956 | from lp.soyuz.enums import ( |
957 | @@ -155,7 +155,7 @@ |
958 | d = lostbuilding_builder.updateStatus(BufferLogger()) |
959 | def check_slave_status(failure): |
960 | self.assertIn('abort', slave.call_log) |
961 | - # 'Fault' comes from the LostBuildingBrokenSlave. This is |
962 | + # 'Fault' comes from the LostBuildingBrokenSlave, this is |
963 | # just testing that the value is passed through. |
964 | self.assertIsInstance(failure.value, xmlrpclib.Fault) |
965 | return d.addBoth(check_slave_status) |
966 | @@ -534,26 +534,6 @@ |
967 | # And the old_candidate is superseded: |
968 | self.assertEqual(BuildStatus.SUPERSEDED, build.status) |
969 | |
970 | - def test_findBuildCandidate_postprocesses_in_read_write_policy(self): |
971 | - # _findBuildCandidate invokes BuildFarmJob.postprocessCandidate, |
972 | - # which may modify the database. This happens in a read-write |
973 | - # transaction even if _findBuildCandidate itself runs in a |
974 | - # read-only transaction policy. |
975 | - |
976 | - # PackageBuildJob.postprocessCandidate will attempt to delete |
977 | - # security builds. |
978 | - pub = self.publisher.getPubSource( |
979 | - sourcename="gedit", status=PackagePublishingStatus.PUBLISHED, |
980 | - archive=self.factory.makeArchive(), |
981 | - pocket=PackagePublishingPocket.SECURITY) |
982 | - pub.createMissingBuilds() |
983 | - transaction.commit() |
984 | - with DatabaseTransactionPolicy(read_only=True): |
985 | - removeSecurityProxy(self.frog_builder)._findBuildCandidate() |
986 | - # The test is that this passes without a "transaction is |
987 | - # read-only" error. |
988 | - transaction.commit() |
989 | - |
990 | def test_acquireBuildCandidate_marks_building(self): |
991 | # acquireBuildCandidate() should call _findBuildCandidate and |
992 | # mark the build as building. |
993 | |
994 | === modified file 'lib/lp/buildmaster/tests/test_manager.py' |
995 | --- lib/lp/buildmaster/tests/test_manager.py 2011-12-08 11:51:59 +0000 |
996 | +++ lib/lp/buildmaster/tests/test_manager.py 2011-12-19 21:48:31 +0000 |
997 | @@ -1,36 +1,40 @@ |
998 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
999 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
1000 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1001 | |
1002 | """Tests for the renovated slave scanner aka BuilddManager.""" |
1003 | |
1004 | -from collections import namedtuple |
1005 | import os |
1006 | import signal |
1007 | import time |
1008 | import xmlrpclib |
1009 | |
1010 | -from lpbuildd.tests import BuilddSlaveTestSetup |
1011 | from testtools.deferredruntest import ( |
1012 | assert_fails_with, |
1013 | AsynchronousDeferredRunTest, |
1014 | ) |
1015 | + |
1016 | import transaction |
1017 | + |
1018 | from twisted.internet import ( |
1019 | defer, |
1020 | reactor, |
1021 | task, |
1022 | ) |
1023 | -from twisted.internet.task import deferLater |
1024 | +from twisted.internet.task import ( |
1025 | + deferLater, |
1026 | + ) |
1027 | from twisted.python.failure import Failure |
1028 | from zope.component import getUtility |
1029 | from zope.security.proxy import removeSecurityProxy |
1030 | |
1031 | +from lpbuildd.tests import BuilddSlaveTestSetup |
1032 | + |
1033 | from canonical.config import config |
1034 | -from canonical.database.constants import UTC_NOW |
1035 | from canonical.launchpad.ftests import ( |
1036 | ANONYMOUS, |
1037 | login, |
1038 | ) |
1039 | +from lp.services.log.logger import BufferLogger |
1040 | from canonical.testing.layers import ( |
1041 | LaunchpadScriptLayer, |
1042 | LaunchpadZopelessLayer, |
1043 | @@ -46,18 +50,14 @@ |
1044 | SlaveScanner, |
1045 | ) |
1046 | from lp.buildmaster.model.builder import Builder |
1047 | -from lp.buildmaster.model.packagebuild import PackageBuild |
1048 | from lp.buildmaster.tests.harness import BuilddManagerTestSetup |
1049 | from lp.buildmaster.tests.mock_slaves import ( |
1050 | BrokenSlave, |
1051 | BuildingSlave, |
1052 | make_publisher, |
1053 | OkSlave, |
1054 | - WaitingSlave, |
1055 | ) |
1056 | from lp.registry.interfaces.distribution import IDistributionSet |
1057 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
1058 | -from lp.services.log.logger import BufferLogger |
1059 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet |
1060 | from lp.testing import ( |
1061 | TestCase, |
1062 | @@ -65,13 +65,10 @@ |
1063 | ) |
1064 | from lp.testing.factory import LaunchpadObjectFactory |
1065 | from lp.testing.fakemethod import FakeMethod |
1066 | -from lp.testing.sampledata import ( |
1067 | - BOB_THE_BUILDER_NAME, |
1068 | - FROG_THE_BUILDER_NAME, |
1069 | - ) |
1070 | - |
1071 | - |
1072 | -class TestSlaveScannerScan(TestCaseWithFactory): |
1073 | +from lp.testing.sampledata import BOB_THE_BUILDER_NAME |
1074 | + |
1075 | + |
1076 | +class TestSlaveScannerScan(TestCase): |
1077 | """Tests `SlaveScanner.scan` method. |
1078 | |
1079 | This method uses the old framework for scanning and dispatching builds. |
1080 | @@ -86,8 +83,6 @@ |
1081 | 'bob' builder. |
1082 | """ |
1083 | super(TestSlaveScannerScan, self).setUp() |
1084 | - self.read_only = DatabaseTransactionPolicy(read_only=True) |
1085 | - |
1086 | # Creating the required chroots needed for dispatching. |
1087 | test_publisher = make_publisher() |
1088 | ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
1089 | @@ -95,15 +90,6 @@ |
1090 | test_publisher.setUpDefaultDistroSeries(hoary) |
1091 | test_publisher.addFakeChroots() |
1092 | |
1093 | - def _enterReadOnly(self): |
1094 | - """Go into read-only transaction policy.""" |
1095 | - self.read_only.__enter__() |
1096 | - self.addCleanup(self._exitReadOnly) |
1097 | - |
1098 | - def _exitReadOnly(self): |
1099 | - """Leave read-only transaction policy.""" |
1100 | - self.read_only.__exit__(None, None, None) |
1101 | - |
1102 | def _resetBuilder(self, builder): |
1103 | """Reset the given builder and its job.""" |
1104 | |
1105 | @@ -114,23 +100,6 @@ |
1106 | |
1107 | transaction.commit() |
1108 | |
1109 | - def getFreshBuilder(self, slave=None, name=BOB_THE_BUILDER_NAME, |
1110 | - failure_count=0): |
1111 | - """Return a builder. |
1112 | - |
1113 | - The builder is taken from sample data, but reset to a usable state. |
1114 | - Be careful: this is not a proper factory method. Identical calls |
1115 | - return (and reset) the same builder. Don't rely on that though; |
1116 | - maybe someday we'll have a proper factory here. |
1117 | - """ |
1118 | - if slave is None: |
1119 | - slave = OkSlave() |
1120 | - builder = getUtility(IBuilderSet)[name] |
1121 | - self._resetBuilder(builder) |
1122 | - builder.setSlaveForTesting(slave) |
1123 | - builder.failure_count = failure_count |
1124 | - return builder |
1125 | - |
1126 | def assertBuildingJob(self, job, builder, logtail=None): |
1127 | """Assert the given job is building on the given builder.""" |
1128 | from lp.services.job.interfaces.job import JobStatus |
1129 | @@ -145,14 +114,14 @@ |
1130 | self.assertEqual(build.status, BuildStatus.BUILDING) |
1131 | self.assertEqual(job.logtail, logtail) |
1132 | |
1133 | - def _getScanner(self, builder_name=None, clock=None): |
1134 | + def _getScanner(self, builder_name=None): |
1135 | """Instantiate a SlaveScanner object. |
1136 | |
1137 | Replace its default logging handler by a testing version. |
1138 | """ |
1139 | if builder_name is None: |
1140 | builder_name = BOB_THE_BUILDER_NAME |
1141 | - scanner = SlaveScanner(builder_name, BufferLogger(), clock=clock) |
1142 | + scanner = SlaveScanner(builder_name, BufferLogger()) |
1143 | scanner.logger.name = 'slave-scanner' |
1144 | |
1145 | return scanner |
1146 | @@ -168,15 +137,17 @@ |
1147 | def testScanDispatchForResetBuilder(self): |
1148 | # A job gets dispatched to the sampledata builder after it's reset. |
1149 | |
1150 | - # Obtain a builder. Initialize failure count to 1 so that |
1151 | - # _checkDispatch can make sure that a successful dispatch resets |
1152 | - # the count to 0. |
1153 | - builder = self.getFreshBuilder(failure_count=1) |
1154 | + # Reset sampledata builder. |
1155 | + builder = getUtility(IBuilderSet)[BOB_THE_BUILDER_NAME] |
1156 | + self._resetBuilder(builder) |
1157 | + builder.setSlaveForTesting(OkSlave()) |
1158 | + # Set this to 1 here so that _checkDispatch can make sure it's |
1159 | + # reset to 0 after a successful dispatch. |
1160 | + builder.failure_count = 1 |
1161 | |
1162 | # Run 'scan' and check its result. |
1163 | self.layer.txn.commit() |
1164 | self.layer.switchDbUser(config.builddmaster.dbuser) |
1165 | - self._enterReadOnly() |
1166 | scanner = self._getScanner() |
1167 | d = defer.maybeDeferred(scanner.scan) |
1168 | d.addCallback(self._checkDispatch, builder) |
1169 | @@ -189,18 +160,20 @@ |
1170 | to the asynchonous dispatcher and the builder remained active |
1171 | and IDLE. |
1172 | """ |
1173 | - self.assertIs(None, slave, "Unexpected slave.") |
1174 | + self.assertTrue(slave is None, "Unexpected slave.") |
1175 | |
1176 | builder = getUtility(IBuilderSet).get(builder.id) |
1177 | self.assertTrue(builder.builderok) |
1178 | - self.assertIs(None, builder.currentjob) |
1179 | + self.assertTrue(builder.currentjob is None) |
1180 | |
1181 | def testNoDispatchForMissingChroots(self): |
1182 | # When a required chroot is not present the `scan` method |
1183 | # should not return any `RecordingSlaves` to be processed |
1184 | # and the builder used should remain active and IDLE. |
1185 | |
1186 | - builder = self.getFreshBuilder() |
1187 | + # Reset sampledata builder. |
1188 | + builder = getUtility(IBuilderSet)[BOB_THE_BUILDER_NAME] |
1189 | + self._resetBuilder(builder) |
1190 | |
1191 | # Remove hoary/i386 chroot. |
1192 | login('foo.bar@canonical.com') |
1193 | @@ -213,7 +186,6 @@ |
1194 | |
1195 | # Run 'scan' and check its result. |
1196 | self.layer.switchDbUser(config.builddmaster.dbuser) |
1197 | - self._enterReadOnly() |
1198 | scanner = self._getScanner() |
1199 | d = defer.maybeDeferred(scanner.singleCycle) |
1200 | d.addCallback(self._checkNoDispatch, builder) |
1201 | @@ -255,7 +227,6 @@ |
1202 | |
1203 | # Run 'scan' and check its result. |
1204 | self.layer.switchDbUser(config.builddmaster.dbuser) |
1205 | - self._enterReadOnly() |
1206 | scanner = self._getScanner() |
1207 | d = defer.maybeDeferred(scanner.scan) |
1208 | d.addCallback(self._checkJobRescued, builder, job) |
1209 | @@ -291,27 +262,25 @@ |
1210 | |
1211 | # Run 'scan' and check its result. |
1212 | self.layer.switchDbUser(config.builddmaster.dbuser) |
1213 | - self._enterReadOnly() |
1214 | scanner = self._getScanner() |
1215 | d = defer.maybeDeferred(scanner.scan) |
1216 | d.addCallback(self._checkJobUpdated, builder, job) |
1217 | return d |
1218 | |
1219 | def test_scan_with_nothing_to_dispatch(self): |
1220 | - builder = self.factory.makeBuilder() |
1221 | + factory = LaunchpadObjectFactory() |
1222 | + builder = factory.makeBuilder() |
1223 | builder.setSlaveForTesting(OkSlave()) |
1224 | - transaction.commit() |
1225 | - self._enterReadOnly() |
1226 | scanner = self._getScanner(builder_name=builder.name) |
1227 | d = scanner.scan() |
1228 | return d.addCallback(self._checkNoDispatch, builder) |
1229 | |
1230 | def test_scan_with_manual_builder(self): |
1231 | # Reset sampledata builder. |
1232 | - builder = self.getFreshBuilder() |
1233 | + builder = getUtility(IBuilderSet)[BOB_THE_BUILDER_NAME] |
1234 | + self._resetBuilder(builder) |
1235 | + builder.setSlaveForTesting(OkSlave()) |
1236 | builder.manual = True |
1237 | - transaction.commit() |
1238 | - self._enterReadOnly() |
1239 | scanner = self._getScanner() |
1240 | d = scanner.scan() |
1241 | d.addCallback(self._checkNoDispatch, builder) |
1242 | @@ -319,10 +288,10 @@ |
1243 | |
1244 | def test_scan_with_not_ok_builder(self): |
1245 | # Reset sampledata builder. |
1246 | - builder = self.getFreshBuilder() |
1247 | + builder = getUtility(IBuilderSet)[BOB_THE_BUILDER_NAME] |
1248 | + self._resetBuilder(builder) |
1249 | + builder.setSlaveForTesting(OkSlave()) |
1250 | builder.builderok = False |
1251 | - transaction.commit() |
1252 | - self._enterReadOnly() |
1253 | scanner = self._getScanner() |
1254 | d = scanner.scan() |
1255 | # Because the builder is not ok, we can't use _checkNoDispatch. |
1256 | @@ -331,27 +300,25 @@ |
1257 | return d |
1258 | |
1259 | def test_scan_of_broken_slave(self): |
1260 | - builder = self.getFreshBuilder(slave=BrokenSlave()) |
1261 | - transaction.commit() |
1262 | - self._enterReadOnly() |
1263 | + builder = getUtility(IBuilderSet)[BOB_THE_BUILDER_NAME] |
1264 | + self._resetBuilder(builder) |
1265 | + builder.setSlaveForTesting(BrokenSlave()) |
1266 | + builder.failure_count = 0 |
1267 | scanner = self._getScanner(builder_name=builder.name) |
1268 | d = scanner.scan() |
1269 | return assert_fails_with(d, xmlrpclib.Fault) |
1270 | |
1271 | def _assertFailureCounting(self, builder_count, job_count, |
1272 | expected_builder_count, expected_job_count): |
1273 | - # Avoid circular imports. |
1274 | - from lp.buildmaster import manager as manager_module |
1275 | - |
1276 | # If scan() fails with an exception, failure_counts should be |
1277 | # incremented. What we do with the results of the failure |
1278 | # counts is tested below separately, this test just makes sure that |
1279 | # scan() is setting the counts. |
1280 | def failing_scan(): |
1281 | return defer.fail(Exception("fake exception")) |
1282 | - |
1283 | scanner = self._getScanner() |
1284 | scanner.scan = failing_scan |
1285 | + from lp.buildmaster import manager as manager_module |
1286 | self.patch(manager_module, 'assessFailureCounts', FakeMethod()) |
1287 | builder = getUtility(IBuilderSet)[scanner.builder_name] |
1288 | |
1289 | @@ -499,60 +466,6 @@ |
1290 | d.addCallback(check_cancelled, builder, buildqueue) |
1291 | return d |
1292 | |
1293 | - def makeFakeFailure(self): |
1294 | - """Produce a fake failure for use with SlaveScanner._scanFailed.""" |
1295 | - FakeFailure = namedtuple('FakeFailure', ['getErrorMessage', 'check']) |
1296 | - return FakeFailure( |
1297 | - FakeMethod(self.factory.getUniqueString()), |
1298 | - FakeMethod(True)) |
1299 | - |
1300 | - def test_interleaved_success_and_failure_do_not_interfere(self): |
1301 | - # It's possible for one builder to fail while another continues |
1302 | - # to function properly. When that happens, the failed builder |
1303 | - # may cause database changes to be rolled back. But that does |
1304 | - # not affect the functioning builder. |
1305 | - clock = task.Clock() |
1306 | - |
1307 | - broken_builder = self.getFreshBuilder( |
1308 | - slave=BrokenSlave(), name=BOB_THE_BUILDER_NAME) |
1309 | - broken_scanner = self._getScanner(builder_name=broken_builder.name) |
1310 | - good_builder = self.getFreshBuilder( |
1311 | - slave=WaitingSlave(), name=FROG_THE_BUILDER_NAME) |
1312 | - good_build = self.factory.makeBinaryPackageBuild( |
1313 | - distroarchseries=self.factory.makeDistroArchSeries()) |
1314 | - |
1315 | - # The good build is being handled by the good builder. |
1316 | - buildqueue = good_build.queueBuild() |
1317 | - buildqueue.builder = good_builder |
1318 | - |
1319 | - removeSecurityProxy(good_build.build_farm_job).date_started = UTC_NOW |
1320 | - |
1321 | - # The good builder requests information from a successful build, |
1322 | - # and up receiving it, updates the build's metadata. |
1323 | - # Our dependencies string goes into the build, and its |
1324 | - # date_finished will be set. |
1325 | - dependencies = self.factory.getUniqueString() |
1326 | - PackageBuild.storeBuildInfo( |
1327 | - good_build, None, {'dependencies': dependencies}) |
1328 | - clock.advance(1) |
1329 | - |
1330 | - # The broken scanner experiences a failure before the good |
1331 | - # scanner is receiving its data. This aborts the ongoing |
1332 | - # transaction. |
1333 | - # As a somewhat weird example, if the builder changed its own |
1334 | - # title, that change will be rolled back. |
1335 | - original_broken_builder_title = broken_builder.title |
1336 | - broken_builder.title = self.factory.getUniqueString() |
1337 | - broken_scanner._scanFailed(self.makeFakeFailure()) |
1338 | - |
1339 | - # The work done by the good scanner is retained. The |
1340 | - # storeBuildInfo code committed it. |
1341 | - self.assertEqual(dependencies, good_build.dependencies) |
1342 | - self.assertIsNot(None, good_build.date_finished) |
1343 | - |
1344 | - # The work done by the broken scanner is rolled back. |
1345 | - self.assertEqual(original_broken_builder_title, broken_builder.title) |
1346 | - |
1347 | |
1348 | class TestCancellationChecking(TestCaseWithFactory): |
1349 | """Unit tests for the checkCancellation method.""" |
1350 | |
1351 | === modified file 'lib/lp/buildmaster/tests/test_packagebuild.py' |
1352 | --- lib/lp/buildmaster/tests/test_packagebuild.py 2011-11-09 11:50:17 +0000 |
1353 | +++ lib/lp/buildmaster/tests/test_packagebuild.py 2011-12-19 21:48:31 +0000 |
1354 | @@ -1,4 +1,4 @@ |
1355 | -# Copyright 2010-2011 Canonical Ltd. This software is licensed under the |
1356 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
1357 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1358 | |
1359 | """Tests for `IPackageBuild`.""" |
1360 | @@ -22,7 +22,9 @@ |
1361 | LaunchpadFunctionalLayer, |
1362 | LaunchpadZopelessLayer, |
1363 | ) |
1364 | -from lp.archiveuploader.uploadprocessor import parse_build_upload_leaf_name |
1365 | +from lp.archiveuploader.uploadprocessor import ( |
1366 | + parse_build_upload_leaf_name, |
1367 | + ) |
1368 | from lp.buildmaster.enums import ( |
1369 | BuildFarmJobType, |
1370 | BuildStatus, |
1371 | @@ -32,11 +34,12 @@ |
1372 | IPackageBuildSet, |
1373 | IPackageBuildSource, |
1374 | ) |
1375 | -from lp.buildmaster.model.builder import BuilderSlave |
1376 | from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
1377 | from lp.buildmaster.model.packagebuild import PackageBuild |
1378 | from lp.buildmaster.tests.mock_slaves import WaitingSlave |
1379 | -from lp.registry.interfaces.pocket import PackagePublishingPocket |
1380 | +from lp.registry.interfaces.pocket import ( |
1381 | + PackagePublishingPocket, |
1382 | + ) |
1383 | from lp.testing import ( |
1384 | login, |
1385 | login_person, |
1386 | @@ -282,7 +285,10 @@ |
1387 | |
1388 | |
1389 | class TestHandleStatusMixin: |
1390 | - """Tests for `IPackageBuild`s handleStatus method.""" |
1391 | + """Tests for `IPackageBuild`s handleStatus method. |
1392 | + |
1393 | + This should be run with a Trial TestCase. |
1394 | + """ |
1395 | |
1396 | layer = LaunchpadZopelessLayer |
1397 | |
1398 | @@ -301,7 +307,7 @@ |
1399 | self.build.buildqueue_record.setDateStarted(UTC_NOW) |
1400 | self.slave = WaitingSlave('BuildStatus.OK') |
1401 | self.slave.valid_file_hashes.append('test_file_hash') |
1402 | - self.patch(BuilderSlave, 'makeBuilderSlave', FakeMethod(self.slave)) |
1403 | + builder.setSlaveForTesting(self.slave) |
1404 | |
1405 | # We overwrite the buildmaster root to use a temp directory. |
1406 | tempdir = tempfile.mkdtemp() |
1407 | @@ -342,7 +348,7 @@ |
1408 | def got_status(ignored): |
1409 | self.assertEqual(BuildStatus.FAILEDTOUPLOAD, self.build.status) |
1410 | self.assertResultCount(0, "failed") |
1411 | - self.assertIs(None, self.build.buildqueue_record) |
1412 | + self.assertIdentical(None, self.build.buildqueue_record) |
1413 | |
1414 | d = self.build.handleStatus('OK', None, { |
1415 | 'filemap': {'/tmp/myfile.py': 'test_file_hash'}, |
1416 | @@ -384,10 +390,14 @@ |
1417 | |
1418 | def got_status(ignored): |
1419 | if expected_notification: |
1420 | - self.assertNotEqual( |
1421 | - 0, len(pop_notifications()), "No notifications received.") |
1422 | + self.failIf( |
1423 | + len(pop_notifications()) == 0, |
1424 | + "No notifications received") |
1425 | else: |
1426 | - self.assertContentEqual([], pop_notifications()) |
1427 | + self.failIf( |
1428 | + len(pop_notifications()) > 0, |
1429 | + "Notifications received") |
1430 | + |
1431 | d = self.build.handleStatus(status, None, {}) |
1432 | return d.addCallback(got_status) |
1433 | |
1434 | |
1435 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py' |
1436 | --- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2011-12-08 16:04:13 +0000 |
1437 | +++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py 2011-12-19 21:48:31 +0000 |
1438 | @@ -13,8 +13,8 @@ |
1439 | |
1440 | from pytz import utc |
1441 | from storm.locals import Store |
1442 | -from testtools.deferredruntest import AsynchronousDeferredRunTest |
1443 | import transaction |
1444 | +from twisted.trial.unittest import TestCase as TrialTestCase |
1445 | from zope.component import getUtility |
1446 | from zope.security.proxy import removeSecurityProxy |
1447 | |
1448 | @@ -28,7 +28,6 @@ |
1449 | from lp.app.errors import NotFoundError |
1450 | from lp.buildmaster.enums import BuildStatus |
1451 | from lp.buildmaster.interfaces.buildqueue import IBuildQueue |
1452 | -from lp.buildmaster.model.builder import BuilderSlave |
1453 | from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
1454 | from lp.buildmaster.model.packagebuild import PackageBuild |
1455 | from lp.buildmaster.tests.mock_slaves import WaitingSlave |
1456 | @@ -589,11 +588,14 @@ |
1457 | self.assertEquals(0, len(notifications)) |
1458 | |
1459 | |
1460 | -class TestBuildNotifications(TestCaseWithFactory): |
1461 | +class TestBuildNotifications(TrialTestCase): |
1462 | |
1463 | layer = LaunchpadZopelessLayer |
1464 | |
1465 | - run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=20) |
1466 | + def setUp(self): |
1467 | + super(TestBuildNotifications, self).setUp() |
1468 | + from lp.testing.factory import LaunchpadObjectFactory |
1469 | + self.factory = LaunchpadObjectFactory() |
1470 | |
1471 | def prepare_build(self, fake_successful_upload=False): |
1472 | queue_record = self.factory.makeSourcePackageRecipeBuildJob() |
1473 | @@ -606,7 +608,7 @@ |
1474 | result=True) |
1475 | queue_record.builder = self.factory.makeBuilder() |
1476 | slave = WaitingSlave('BuildStatus.OK') |
1477 | - self.patch(BuilderSlave, 'makeBuilderSlave', FakeMethod(slave)) |
1478 | + queue_record.builder.setSlaveForTesting(slave) |
1479 | return build |
1480 | |
1481 | def assertDeferredNotifyCount(self, status, build, expected_count): |
1482 | @@ -664,5 +666,5 @@ |
1483 | |
1484 | |
1485 | class TestHandleStatusForSPRBuild( |
1486 | - MakeSPRecipeBuildMixin, TestHandleStatusMixin, TestCaseWithFactory): |
1487 | + MakeSPRecipeBuildMixin, TestHandleStatusMixin, TrialTestCase): |
1488 | """IPackageBuild.handleStatus works with SPRecipe builds.""" |
1489 | |
1490 | === modified file 'lib/lp/services/database/transaction_policy.py' |
1491 | --- lib/lp/services/database/transaction_policy.py 2011-10-10 06:23:12 +0000 |
1492 | +++ lib/lp/services/database/transaction_policy.py 2011-12-19 21:48:31 +0000 |
1493 | @@ -133,11 +133,8 @@ |
1494 | def _isInTransaction(self): |
1495 | """Is our store currently in a transaction?""" |
1496 | pg_connection = self.store._connection._raw_connection |
1497 | - if pg_connection is None: |
1498 | - return False |
1499 | - else: |
1500 | - status = pg_connection.get_transaction_status() |
1501 | - return status != TRANSACTION_STATUS_IDLE |
1502 | + status = pg_connection.get_transaction_status() |
1503 | + return status != TRANSACTION_STATUS_IDLE |
1504 | |
1505 | def _checkNoTransaction(self, error_msg): |
1506 | """Verify that no transaction is ongoing. |
1507 | |
1508 | === modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py' |
1509 | --- lib/lp/soyuz/tests/test_binarypackagebuild.py 2011-11-14 06:36:57 +0000 |
1510 | +++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2011-12-19 21:48:31 +0000 |
1511 | @@ -1,4 +1,4 @@ |
1512 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
1513 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
1514 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1515 | |
1516 | """Test Build features.""" |
1517 | @@ -10,7 +10,7 @@ |
1518 | |
1519 | import pytz |
1520 | from storm.store import Store |
1521 | -from testtools.deferredruntest import AsynchronousDeferredRunTest |
1522 | +from twisted.trial.unittest import TestCase as TrialTestCase |
1523 | from zope.component import getUtility |
1524 | from zope.security.proxy import removeSecurityProxy |
1525 | |
1526 | @@ -25,7 +25,6 @@ |
1527 | from lp.buildmaster.interfaces.builder import IBuilderSet |
1528 | from lp.buildmaster.interfaces.buildqueue import IBuildQueue |
1529 | from lp.buildmaster.interfaces.packagebuild import IPackageBuild |
1530 | -from lp.buildmaster.model.builder import BuilderSlave |
1531 | from lp.buildmaster.model.buildqueue import BuildQueue |
1532 | from lp.buildmaster.tests.mock_slaves import WaitingSlave |
1533 | from lp.buildmaster.tests.test_packagebuild import ( |
1534 | @@ -54,7 +53,6 @@ |
1535 | logout, |
1536 | TestCaseWithFactory, |
1537 | ) |
1538 | -from lp.testing.fakemethod import FakeMethod |
1539 | |
1540 | |
1541 | class TestBinaryPackageBuild(TestCaseWithFactory): |
1542 | @@ -524,9 +522,7 @@ |
1543 | self.build = gedit_src_hist.createMissingBuilds()[0] |
1544 | |
1545 | self.builder = self.factory.makeBuilder() |
1546 | - self.patch( |
1547 | - BuilderSlave, 'makeBuilderSlave', |
1548 | - FakeMethod(WaitingSlave('BuildStatus.OK'))) |
1549 | + self.builder.setSlaveForTesting(WaitingSlave('BuildStatus.OK')) |
1550 | self.build.buildqueue_record.markAsBuilding(self.builder) |
1551 | |
1552 | def testDependencies(self): |
1553 | @@ -572,12 +568,9 @@ |
1554 | |
1555 | |
1556 | class TestHandleStatusForBinaryPackageBuild( |
1557 | - MakeBinaryPackageBuildMixin, TestHandleStatusMixin, TestCaseWithFactory): |
1558 | + MakeBinaryPackageBuildMixin, TestHandleStatusMixin, TrialTestCase): |
1559 | """IPackageBuild.handleStatus works with binary builds.""" |
1560 | |
1561 | - layer = LaunchpadZopelessLayer |
1562 | - run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=20) |
1563 | - |
1564 | |
1565 | class TestBinaryPackageBuildWebservice(TestCaseWithFactory): |
1566 | """Test cases for BinaryPackageBuild on the webservice. |
1567 | |
1568 | === modified file 'lib/lp/translations/model/translationtemplatesbuildbehavior.py' |
1569 | --- lib/lp/translations/model/translationtemplatesbuildbehavior.py 2011-10-20 07:45:52 +0000 |
1570 | +++ lib/lp/translations/model/translationtemplatesbuildbehavior.py 2011-12-19 21:48:31 +0000 |
1571 | @@ -1,4 +1,4 @@ |
1572 | -# Copyright 2010-2011 Canonical Ltd. This software is licensed under the |
1573 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
1574 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1575 | |
1576 | """An `IBuildFarmJobBehavior` for `TranslationTemplatesBuildJob`. |
1577 | @@ -13,10 +13,9 @@ |
1578 | |
1579 | import datetime |
1580 | import os |
1581 | +import pytz |
1582 | import tempfile |
1583 | |
1584 | -import pytz |
1585 | -import transaction |
1586 | from twisted.internet import defer |
1587 | from zope.component import getUtility |
1588 | from zope.interface import implements |
1589 | @@ -29,7 +28,6 @@ |
1590 | ) |
1591 | from lp.buildmaster.model.buildfarmjobbehavior import BuildFarmJobBehaviorBase |
1592 | from lp.registry.interfaces.productseries import IProductSeriesSet |
1593 | -from lp.services.database.transaction_policy import DatabaseTransactionPolicy |
1594 | from lp.translations.interfaces.translationimportqueue import ( |
1595 | ITranslationImportQueue, |
1596 | ) |
1597 | @@ -134,16 +132,13 @@ |
1598 | def storeBuildInfo(build, queue_item, build_status): |
1599 | """See `IPackageBuild`.""" |
1600 | def got_log(lfa_id): |
1601 | - transaction.commit() |
1602 | - with DatabaseTransactionPolicy(read_only=False): |
1603 | - build.build.log = lfa_id |
1604 | - build.build.builder = queue_item.builder |
1605 | - build.build.date_started = queue_item.date_started |
1606 | - # XXX cprov 20060615 bug=120584: Currently buildduration |
1607 | - # includes the scanner latency. It should really be |
1608 | - # asking the slave for the duration spent building locally. |
1609 | - build.build.date_finished = datetime.datetime.now(pytz.UTC) |
1610 | - transaction.commit() |
1611 | + build.build.log = lfa_id |
1612 | + build.build.builder = queue_item.builder |
1613 | + build.build.date_started = queue_item.date_started |
1614 | + # XXX cprov 20060615 bug=120584: Currently buildduration includes |
1615 | + # the scanner latency, it should really be asking the slave for |
1616 | + # the duration spent building locally. |
1617 | + build.build.date_finished = datetime.datetime.now(pytz.UTC) |
1618 | |
1619 | d = build.getLogFromSlave(build, queue_item) |
1620 | return d.addCallback(got_log) |