Merge ~cjwatson/launchpad:refactor-updateBuild into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 115222a6e4c30765a4ee17254bca1a14b269a566
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:refactor-updateBuild
Merge into: launchpad:master
Prerequisite: ~cjwatson/launchpad:rename-builder-interactor-slave
Diff against target: 844 lines (+262/-168)
8 files modified
lib/lp/buildmaster/interactor.py (+10/-27)
lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py (+3/-4)
lib/lp/buildmaster/model/buildfarmjobbehaviour.py (+66/-41)
lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py (+96/-32)
lib/lp/buildmaster/tests/test_interactor.py (+1/-16)
lib/lp/code/model/tests/test_recipebuilder.py (+6/-2)
lib/lp/oci/tests/test_ocirecipebuildbehaviour.py (+75/-27)
lib/lp/translations/tests/test_translationtemplatesbuildbehaviour.py (+5/-19)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+414070@code.launchpad.net

Commit message

Push more of BuilderInteractor.updateBuild down to behaviours

Description of the change

For upcoming work on CI jobs, we want to be able to do some build-type-specific work on non-terminal build states, namely incrementally fetching output files from builders. This requires `updateBuild` to call something build-type-specific in the `BUILDING` state. Since there's a small amount of code in common between `BUILDING`/`ABORTING` and `WAITING` already (the `updateStatus` call and the following commit), push this all down to `BuildFarmJobBehaviour.handleStatus`, which now handles everything except fetching the logtail.

No functional change is intended from this refactoring, although a number of tests need to change because `handleStatus` now requires a more complete version of launchpad-buildd's `status` response; previously some tests could get away with omitting some fields.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/buildmaster/interactor.py b/lib/lp/buildmaster/interactor.py
index e8a1d7f..679d5c6 100644
--- a/lib/lp/buildmaster/interactor.py
+++ b/lib/lp/buildmaster/interactor.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2021 Canonical Ltd. This software is licensed under the1# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__all__ = [4__all__ = [
@@ -526,19 +526,6 @@ class BuilderInteractor:
526 return candidate526 return candidate
527527
528 @staticmethod528 @staticmethod
529 def extractBuildStatus(worker_status):
530 """Read build status name.
531
532 :param worker_status: build status dict from BuilderWorker.status.
533 :return: the unqualified status name, e.g. "OK".
534 """
535 status_string = worker_status['build_status']
536 lead_string = 'BuildStatus.'
537 assert status_string.startswith(lead_string), (
538 "Malformed status string: '%s'" % status_string)
539 return status_string[len(lead_string):]
540
541 @staticmethod
542 def extractLogTail(worker_status):529 def extractLogTail(worker_status):
543 """Extract the log tail from a builder status response.530 """Extract the log tail from a builder status response.
544531
@@ -580,24 +567,20 @@ class BuilderInteractor:
580 # matches the DB, and this method isn't called unless the DB567 # matches the DB, and this method isn't called unless the DB
581 # says there's a job.568 # says there's a job.
582 builder_status = worker_status['builder_status']569 builder_status = worker_status['builder_status']
570 if builder_status not in (
571 'BuilderStatus.BUILDING', 'BuilderStatus.ABORTING',
572 'BuilderStatus.WAITING'):
573 raise AssertionError("Unknown status %s" % builder_status)
574 builder = builder_factory[vitals.name]
575 behaviour = behaviour_factory(vitals.build_queue, builder, worker)
583 if builder_status in (576 if builder_status in (
584 'BuilderStatus.BUILDING', 'BuilderStatus.ABORTING'):577 'BuilderStatus.BUILDING', 'BuilderStatus.ABORTING'):
585 logtail = cls.extractLogTail(worker_status)578 logtail = cls.extractLogTail(worker_status)
586 if logtail is not None:579 if logtail is not None:
587 manager.addLogTail(vitals.build_queue.id, logtail)580 manager.addLogTail(vitals.build_queue.id, logtail)
588 vitals.build_queue.specific_build.updateStatus(581 # Delegate the remaining handling to the build behaviour, which will
589 vitals.build_queue.specific_build.status,582 # commit the transaction.
590 worker_status=worker_status)583 yield behaviour.handleStatus(vitals.build_queue, worker_status)
591 transaction.commit()
592 elif builder_status == 'BuilderStatus.WAITING':
593 # Build has finished. Delegate handling to the build itself.
594 builder = builder_factory[vitals.name]
595 behaviour = behaviour_factory(vitals.build_queue, builder, worker)
596 yield behaviour.handleStatus(
597 vitals.build_queue, cls.extractBuildStatus(worker_status),
598 worker_status)
599 else:
600 raise AssertionError("Unknown status %s" % builder_status)
601584
602 @staticmethod585 @staticmethod
603 def _getWorkerScannerLogger():586 def _getWorkerScannerLogger():
diff --git a/lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py b/lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py
index 2d0eed4..c529327 100644
--- a/lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py
+++ b/lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2021 Canonical Ltd. This software is licensed under the1# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Interface for build farm job behaviours."""4"""Interface for build farm job behaviours."""
@@ -88,10 +88,9 @@ class IBuildFarmJobBehaviour(Interface):
88 def verifySuccessfulBuild():88 def verifySuccessfulBuild():
89 """Check that we are allowed to collect this successful build."""89 """Check that we are allowed to collect this successful build."""
9090
91 def handleStatus(bq, status, worker_status):91 def handleStatus(bq, worker_status):
92 """Update the build from a WAITING worker result.92 """Update the build from a worker's status response.
9393
94 :param bq: The `BuildQueue` currently being processed.94 :param bq: The `BuildQueue` currently being processed.
95 :param status: The tail of the BuildStatus (eg. OK or PACKAGEFAIL).
96 :param worker_status: Worker status dict from `BuilderWorker.status`.95 :param worker_status: Worker status dict from `BuilderWorker.status`.
97 """96 """
diff --git a/lib/lp/buildmaster/model/buildfarmjobbehaviour.py b/lib/lp/buildmaster/model/buildfarmjobbehaviour.py
index 0315b52..a7537d3 100644
--- a/lib/lp/buildmaster/model/buildfarmjobbehaviour.py
+++ b/lib/lp/buildmaster/model/buildfarmjobbehaviour.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2021 Canonical Ltd. This software is licensed under the1# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Base and idle BuildFarmJobBehaviour classes."""4"""Base and idle BuildFarmJobBehaviour classes."""
@@ -33,6 +33,7 @@ from lp.services.config import config
33from lp.services.helpers import filenameToContentType33from lp.services.helpers import filenameToContentType
34from lp.services.librarian.interfaces import ILibraryFileAliasSet34from lp.services.librarian.interfaces import ILibraryFileAliasSet
35from lp.services.librarian.utils import copy_and_close35from lp.services.librarian.utils import copy_and_close
36from lp.services.propertycache import cachedproperty
36from lp.services.statsd.interfaces.statsd_client import IStatsdClient37from lp.services.statsd.interfaces.statsd_client import IStatsdClient
37from lp.services.utils import sanitise_urls38from lp.services.utils import sanitise_urls
38from lp.services.webapp import canonical_url39from lp.services.webapp import canonical_url
@@ -53,7 +54,10 @@ class BuildFarmJobBehaviourBase:
53 """Store a reference to the job_type with which we were created."""54 """Store a reference to the job_type with which we were created."""
54 self.build = build55 self.build = build
55 self._builder = None56 self._builder = None
56 self._authserver = xmlrpc.Proxy(57
58 @cachedproperty
59 def _authserver(self):
60 return xmlrpc.Proxy(
57 config.builddmaster.authentication_endpoint.encode('UTF-8'),61 config.builddmaster.authentication_endpoint.encode('UTF-8'),
58 connectTimeout=config.builddmaster.authentication_timeout)62 connectTimeout=config.builddmaster.authentication_timeout)
5963
@@ -255,6 +259,19 @@ class BuildFarmJobBehaviourBase:
255 "%s (%s) can not be built for pocket %s in %s: illegal status"259 "%s (%s) can not be built for pocket %s in %s: illegal status"
256 % (build.title, build.id, build.pocket.name, build.archive))260 % (build.title, build.id, build.pocket.name, build.archive))
257261
262 @staticmethod
263 def extractBuildStatus(worker_status):
264 """Read build status name.
265
266 :param worker_status: build status dict from BuilderWorker.status.
267 :return: the unqualified status name, e.g. "OK".
268 """
269 status_string = worker_status['build_status']
270 lead_string = 'BuildStatus.'
271 assert status_string.startswith(lead_string), (
272 "Malformed status string: '%s'" % status_string)
273 return status_string[len(lead_string):]
274
258 # The list of build status values for which email notifications are275 # The list of build status values for which email notifications are
259 # allowed to be sent. It is up to each callback as to whether it will276 # allowed to be sent. It is up to each callback as to whether it will
260 # consider sending a notification but it won't do so if the status is not277 # consider sending a notification but it won't do so if the status is not
@@ -262,57 +279,65 @@ class BuildFarmJobBehaviourBase:
262 ALLOWED_STATUS_NOTIFICATIONS = ['PACKAGEFAIL', 'CHROOTFAIL']279 ALLOWED_STATUS_NOTIFICATIONS = ['PACKAGEFAIL', 'CHROOTFAIL']
263280
264 @defer.inlineCallbacks281 @defer.inlineCallbacks
265 def handleStatus(self, bq, status, worker_status):282 def handleStatus(self, bq, worker_status):
266 """See `IBuildFarmJobBehaviour`."""283 """See `IBuildFarmJobBehaviour`."""
267 if bq != self.build.buildqueue_record:284 if bq != self.build.buildqueue_record:
268 raise AssertionError(285 raise AssertionError(
269 "%r != %r" % (bq, self.build.buildqueue_record))286 "%r != %r" % (bq, self.build.buildqueue_record))
270 from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME287 from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME
271 logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME)288 logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME)
272 notify = status in self.ALLOWED_STATUS_NOTIFICATIONS289 builder_status = worker_status["builder_status"]
273 fail_status_map = {290
274 'PACKAGEFAIL': BuildStatus.FAILEDTOBUILD,291 if builder_status == "BuilderStatus.WAITING":
275 'DEPFAIL': BuildStatus.MANUALDEPWAIT,292 # Build has finished.
276 'CHROOTFAIL': BuildStatus.CHROOTWAIT,293 status = self.extractBuildStatus(worker_status)
277 }294 notify = status in self.ALLOWED_STATUS_NOTIFICATIONS
278 if self.build.status == BuildStatus.CANCELLING:295 fail_status_map = {
279 fail_status_map['ABORTED'] = BuildStatus.CANCELLED296 'PACKAGEFAIL': BuildStatus.FAILEDTOBUILD,
280297 'DEPFAIL': BuildStatus.MANUALDEPWAIT,
281 logger.info(298 'CHROOTFAIL': BuildStatus.CHROOTWAIT,
282 'Processing finished job %s (%s) from builder %s: %s'299 }
283 % (self.build.build_cookie, self.build.title,300 if self.build.status == BuildStatus.CANCELLING:
284 self.build.buildqueue_record.builder.name, status))301 fail_status_map['ABORTED'] = BuildStatus.CANCELLED
285 build_status = None302
286 if status == 'OK':303 logger.info(
287 yield self.storeLogFromWorker()304 'Processing finished job %s (%s) from builder %s: %s'
288 # handleSuccess will sometimes perform write operations305 % (self.build.build_cookie, self.build.title,
289 # outside the database transaction, so a failure between306 self.build.buildqueue_record.builder.name, status))
290 # here and the commit can cause duplicated results. For307 build_status = None
291 # example, a BinaryPackageBuild will end up in the upload308 if status == 'OK':
292 # queue twice if notify() crashes.309 yield self.storeLogFromWorker()
293 build_status = yield self.handleSuccess(worker_status, logger)310 # handleSuccess will sometimes perform write operations
294 elif status in fail_status_map:311 # outside the database transaction, so a failure between
295 # XXX wgrant: The builder should be set long before here, but312 # here and the commit can cause duplicated results. For
296 # currently isn't.313 # example, a BinaryPackageBuild will end up in the upload
297 yield self.storeLogFromWorker()314 # queue twice if notify() crashes.
298 build_status = fail_status_map[status]315 build_status = yield self.handleSuccess(worker_status, logger)
316 elif status in fail_status_map:
317 yield self.storeLogFromWorker()
318 build_status = fail_status_map[status]
319 else:
320 raise BuildDaemonError(
321 "Build returned unexpected status: %r" % status)
299 else:322 else:
300 raise BuildDaemonError(323 # The build status remains unchanged.
301 "Build returned unexpected status: %r" % status)324 build_status = bq.specific_build.status
302325
303 # Set the status and dequeue the build atomically. Setting the326 # Set the status and (if the build has finished) dequeue the build
304 # status to UPLOADING constitutes handoff to process-upload, so327 # atomically. Setting the status to UPLOADING constitutes handoff to
305 # doing that before we've removed the BuildQueue causes races.328 # process-upload, so doing that before we've removed the BuildQueue
329 # causes races.
306330
307 # XXX wgrant: The builder should be set long before here, but331 # XXX wgrant: The builder should be set long before here, but
308 # currently isn't.332 # currently isn't.
309 self.build.updateStatus(333 self.build.updateStatus(
310 build_status,334 build_status, builder=bq.builder, worker_status=worker_status)
311 builder=self.build.buildqueue_record.builder,335
312 worker_status=worker_status)336 if builder_status == "BuilderStatus.WAITING":
313 if notify:337 if notify:
314 self.build.notify()338 self.build.notify()
315 self.build.buildqueue_record.destroySelf()339 self.build.buildqueue_record.destroySelf()
340
316 transaction.commit()341 transaction.commit()
317342
318 @defer.inlineCallbacks343 @defer.inlineCallbacks
diff --git a/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py b/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py
index ad05908..f36750e 100644
--- a/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py
+++ b/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py
@@ -1,4 +1,4 @@
1# Copyright 2010-2020 Canonical Ltd. This software is licensed under the1# Copyright 2010-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Unit tests for BuildFarmJobBehaviourBase."""4"""Unit tests for BuildFarmJobBehaviourBase."""
@@ -30,6 +30,7 @@ from lp.buildmaster.interfaces.builder import BuildDaemonError
30from lp.buildmaster.interfaces.buildfarmjobbehaviour import (30from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
31 IBuildFarmJobBehaviour,31 IBuildFarmJobBehaviour,
32 )32 )
33from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
33from lp.buildmaster.interfaces.processor import IProcessorSet34from lp.buildmaster.interfaces.processor import IProcessorSet
34from lp.buildmaster.model.buildfarmjobbehaviour import (35from lp.buildmaster.model.buildfarmjobbehaviour import (
35 BuildFarmJobBehaviourBase,36 BuildFarmJobBehaviourBase,
@@ -154,6 +155,22 @@ class TestBuildFarmJobBehaviourBase(TestCaseWithFactory):
154 behaviour.setBuilder(self.factory.makeBuilder(virtualized=False), None)155 behaviour.setBuilder(self.factory.makeBuilder(virtualized=False), None)
155 self.assertIs(False, behaviour.extraBuildArgs()["fast_cleanup"])156 self.assertIs(False, behaviour.extraBuildArgs()["fast_cleanup"])
156157
158 def test_extractBuildStatus_baseline(self):
159 # extractBuildStatus picks the name of the build status out of a
160 # dict describing the worker's status.
161 worker_status = {"build_status": "BuildStatus.BUILDING"}
162 self.assertEqual(
163 "BUILDING",
164 BuildFarmJobBehaviourBase.extractBuildStatus(worker_status))
165
166 def test_extractBuildStatus_malformed(self):
167 # extractBuildStatus errors out when the status string is not
168 # of the form it expects.
169 worker_status = {"build_status": "BUILDING"}
170 self.assertRaises(
171 AssertionError, BuildFarmJobBehaviourBase.extractBuildStatus,
172 worker_status)
173
157174
158class TestDispatchBuildToWorker(StatsMixin, TestCase):175class TestDispatchBuildToWorker(StatsMixin, TestCase):
159176
@@ -383,19 +400,44 @@ class TestHandleStatusMixin:
383 1, len(os.listdir(os.path.join(self.upload_root, result))))400 1, len(os.listdir(os.path.join(self.upload_root, result))))
384401
385 @defer.inlineCallbacks402 @defer.inlineCallbacks
386 def test_handleStatus_OK_normal_file(self):403 def test_handleStatus_BUILDING(self):
404 # If the builder is BUILDING (or any status other than WAITING),
405 # then the behaviour calls updateStatus but doesn't do anything
406 # else.
407 initial_status = self.build.status
408 bq_id = self.build.buildqueue_record.id
409 worker_status = {"builder_status": "BuilderStatus.BUILDING"}
410 removeSecurityProxy(self.build).updateStatus = FakeMethod()
411 with dbuser(config.builddmaster.dbuser):
412 yield self.behaviour.handleStatus(
413 self.build.buildqueue_record, worker_status)
414 self.assertEqual(None, self.build.log)
415 self.assertEqual(0, len(os.listdir(self.upload_root)))
416 self.assertEqual(
417 [((initial_status,),
418 {"builder": self.builder, "worker_status": worker_status})],
419 removeSecurityProxy(self.build).updateStatus.calls)
420 self.assertEqual(0, len(pop_notifications()), "Notifications received")
421 self.assertEqual(
422 self.build.buildqueue_record,
423 getUtility(IBuildQueueSet).get(bq_id))
424
425 @defer.inlineCallbacks
426 def test_handleStatus_WAITING_OK_normal_file(self):
387 # A filemap with plain filenames should not cause a problem.427 # A filemap with plain filenames should not cause a problem.
388 # The call to handleStatus will attempt to get the file from428 # The call to handleStatus will attempt to get the file from
389 # the worker resulting in a URL error in this test case.429 # the worker resulting in a URL error in this test case.
390 with dbuser(config.builddmaster.dbuser):430 with dbuser(config.builddmaster.dbuser):
391 yield self.behaviour.handleStatus(431 yield self.behaviour.handleStatus(
392 self.build.buildqueue_record, 'OK',432 self.build.buildqueue_record,
393 {'filemap': {'myfile.py': 'test_file_hash'}})433 {'builder_status': 'BuilderStatus.WAITING',
434 'build_status': 'BuildStatus.OK',
435 'filemap': {'myfile.py': 'test_file_hash'}})
394 self.assertEqual(BuildStatus.UPLOADING, self.build.status)436 self.assertEqual(BuildStatus.UPLOADING, self.build.status)
395 self.assertResultCount(1, "incoming")437 self.assertResultCount(1, "incoming")
396438
397 @defer.inlineCallbacks439 @defer.inlineCallbacks
398 def test_handleStatus_OK_absolute_filepath(self):440 def test_handleStatus_WAITING_OK_absolute_filepath(self):
399 # A filemap that tries to write to files outside of the upload441 # A filemap that tries to write to files outside of the upload
400 # directory will not be collected.442 # directory will not be collected.
401 with ExpectedException(443 with ExpectedException(
@@ -403,11 +445,13 @@ class TestHandleStatusMixin:
403 "Build returned a file named '/tmp/myfile.py'."):445 "Build returned a file named '/tmp/myfile.py'."):
404 with dbuser(config.builddmaster.dbuser):446 with dbuser(config.builddmaster.dbuser):
405 yield self.behaviour.handleStatus(447 yield self.behaviour.handleStatus(
406 self.build.buildqueue_record, 'OK',448 self.build.buildqueue_record,
407 {'filemap': {'/tmp/myfile.py': 'test_file_hash'}})449 {'builder_status': 'BuilderStatus.WAITING',
450 'build_status': 'BuildStatus.OK',
451 'filemap': {'/tmp/myfile.py': 'test_file_hash'}})
408452
409 @defer.inlineCallbacks453 @defer.inlineCallbacks
410 def test_handleStatus_OK_relative_filepath(self):454 def test_handleStatus_WAITING_OK_relative_filepath(self):
411 # A filemap that tries to write to files outside of455 # A filemap that tries to write to files outside of
412 # the upload directory will not be collected.456 # the upload directory will not be collected.
413 with ExpectedException(457 with ExpectedException(
@@ -415,21 +459,25 @@ class TestHandleStatusMixin:
415 "Build returned a file named '../myfile.py'."):459 "Build returned a file named '../myfile.py'."):
416 with dbuser(config.builddmaster.dbuser):460 with dbuser(config.builddmaster.dbuser):
417 yield self.behaviour.handleStatus(461 yield self.behaviour.handleStatus(
418 self.build.buildqueue_record, 'OK',462 self.build.buildqueue_record,
419 {'filemap': {'../myfile.py': 'test_file_hash'}})463 {'builder_status': 'BuilderStatus.WAITING',
464 'build_status': 'BuildStatus.OK',
465 'filemap': {'../myfile.py': 'test_file_hash'}})
420466
421 @defer.inlineCallbacks467 @defer.inlineCallbacks
422 def test_handleStatus_OK_sets_build_log(self):468 def test_handleStatus_WAITING_OK_sets_build_log(self):
423 # The build log is set during handleStatus.469 # The build log is set during handleStatus.
424 self.assertEqual(None, self.build.log)470 self.assertEqual(None, self.build.log)
425 with dbuser(config.builddmaster.dbuser):471 with dbuser(config.builddmaster.dbuser):
426 yield self.behaviour.handleStatus(472 yield self.behaviour.handleStatus(
427 self.build.buildqueue_record, 'OK',473 self.build.buildqueue_record,
428 {'filemap': {'myfile.py': 'test_file_hash'}})474 {'builder_status': 'BuilderStatus.WAITING',
475 'build_status': 'BuildStatus.OK',
476 'filemap': {'myfile.py': 'test_file_hash'}})
429 self.assertNotEqual(None, self.build.log)477 self.assertNotEqual(None, self.build.log)
430478
431 @defer.inlineCallbacks479 @defer.inlineCallbacks
432 def _test_handleStatus_notifies(self, status):480 def _test_handleStatus_WAITING_notifies(self, status):
433 # An email notification is sent for a given build status if481 # An email notification is sent for a given build status if
434 # notifications are allowed for that status.482 # notifications are allowed for that status.
435 expected_notification = (483 expected_notification = (
@@ -437,7 +485,9 @@ class TestHandleStatusMixin:
437485
438 with dbuser(config.builddmaster.dbuser):486 with dbuser(config.builddmaster.dbuser):
439 yield self.behaviour.handleStatus(487 yield self.behaviour.handleStatus(
440 self.build.buildqueue_record, status, {})488 self.build.buildqueue_record,
489 {'builder_status': 'BuilderStatus.WAITING',
490 'build_status': 'BuildStatus.%s' % status})
441491
442 if expected_notification:492 if expected_notification:
443 self.assertNotEqual(493 self.assertNotEqual(
@@ -446,26 +496,28 @@ class TestHandleStatusMixin:
446 self.assertEqual(496 self.assertEqual(
447 0, len(pop_notifications()), "Notifications received")497 0, len(pop_notifications()), "Notifications received")
448498
449 def test_handleStatus_DEPFAIL_notifies(self):499 def test_handleStatus_WAITING_DEPFAIL_notifies(self):
450 return self._test_handleStatus_notifies("DEPFAIL")500 return self._test_handleStatus_WAITING_notifies("DEPFAIL")
451501
452 def test_handleStatus_CHROOTFAIL_notifies(self):502 def test_handleStatus_WAITING_CHROOTFAIL_notifies(self):
453 return self._test_handleStatus_notifies("CHROOTFAIL")503 return self._test_handleStatus_WAITING_notifies("CHROOTFAIL")
454504
455 def test_handleStatus_PACKAGEFAIL_notifies(self):505 def test_handleStatus_WAITING_PACKAGEFAIL_notifies(self):
456 return self._test_handleStatus_notifies("PACKAGEFAIL")506 return self._test_handleStatus_WAITING_notifies("PACKAGEFAIL")
457507
458 @defer.inlineCallbacks508 @defer.inlineCallbacks
459 def test_handleStatus_ABORTED_cancels_cancelling(self):509 def test_handleStatus_WAITING_ABORTED_cancels_cancelling(self):
460 with dbuser(config.builddmaster.dbuser):510 with dbuser(config.builddmaster.dbuser):
461 self.build.updateStatus(BuildStatus.CANCELLING)511 self.build.updateStatus(BuildStatus.CANCELLING)
462 yield self.behaviour.handleStatus(512 yield self.behaviour.handleStatus(
463 self.build.buildqueue_record, "ABORTED", {})513 self.build.buildqueue_record,
514 {"builder_status": "BuilderStatus.WAITING",
515 "build_status": "BuildStatus.ABORTED"})
464 self.assertEqual(0, len(pop_notifications()), "Notifications received")516 self.assertEqual(0, len(pop_notifications()), "Notifications received")
465 self.assertEqual(BuildStatus.CANCELLED, self.build.status)517 self.assertEqual(BuildStatus.CANCELLED, self.build.status)
466518
467 @defer.inlineCallbacks519 @defer.inlineCallbacks
468 def test_handleStatus_ABORTED_illegal_when_building(self):520 def test_handleStatus_WAITING_ABORTED_illegal_when_building(self):
469 self.builder.vm_host = "fake_vm_host"521 self.builder.vm_host = "fake_vm_host"
470 self.behaviour = self.interactor.getBuildBehaviour(522 self.behaviour = self.interactor.getBuildBehaviour(
471 self.build.buildqueue_record, self.builder, self.worker)523 self.build.buildqueue_record, self.builder, self.worker)
@@ -475,16 +527,20 @@ class TestHandleStatusMixin:
475 BuildDaemonError,527 BuildDaemonError,
476 "Build returned unexpected status: %r" % 'ABORTED'):528 "Build returned unexpected status: %r" % 'ABORTED'):
477 yield self.behaviour.handleStatus(529 yield self.behaviour.handleStatus(
478 self.build.buildqueue_record, "ABORTED", {})530 self.build.buildqueue_record,
531 {"builder_status": "BuilderStatus.WAITING",
532 "build_status": "BuildStatus.ABORTED"})
479533
480 @defer.inlineCallbacks534 @defer.inlineCallbacks
481 def test_handleStatus_ABORTED_cancelling_sets_build_log(self):535 def test_handleStatus_WAITING_ABORTED_cancelling_sets_build_log(self):
482 # If a build is intentionally cancelled, the build log is set.536 # If a build is intentionally cancelled, the build log is set.
483 self.assertEqual(None, self.build.log)537 self.assertEqual(None, self.build.log)
484 with dbuser(config.builddmaster.dbuser):538 with dbuser(config.builddmaster.dbuser):
485 self.build.updateStatus(BuildStatus.CANCELLING)539 self.build.updateStatus(BuildStatus.CANCELLING)
486 yield self.behaviour.handleStatus(540 yield self.behaviour.handleStatus(
487 self.build.buildqueue_record, "ABORTED", {})541 self.build.buildqueue_record,
542 {"builder_status": "BuilderStatus.WAITING",
543 "build_status": "BuildStatus.ABORTED"})
488 self.assertNotEqual(None, self.build.log)544 self.assertNotEqual(None, self.build.log)
489545
490 @defer.inlineCallbacks546 @defer.inlineCallbacks
@@ -493,8 +549,10 @@ class TestHandleStatusMixin:
493 self.assertEqual(None, self.build.date_finished)549 self.assertEqual(None, self.build.date_finished)
494 with dbuser(config.builddmaster.dbuser):550 with dbuser(config.builddmaster.dbuser):
495 yield self.behaviour.handleStatus(551 yield self.behaviour.handleStatus(
496 self.build.buildqueue_record, 'OK',552 self.build.buildqueue_record,
497 {'filemap': {'myfile.py': 'test_file_hash'}})553 {'builder_status': 'BuilderStatus.WAITING',
554 'build_status': 'BuildStatus.OK',
555 'filemap': {'myfile.py': 'test_file_hash'}})
498 self.assertNotEqual(None, self.build.date_finished)556 self.assertNotEqual(None, self.build.date_finished)
499557
500 @defer.inlineCallbacks558 @defer.inlineCallbacks
@@ -504,7 +562,9 @@ class TestHandleStatusMixin:
504 "Build returned unexpected status: %r" % 'GIVENBACK'):562 "Build returned unexpected status: %r" % 'GIVENBACK'):
505 with dbuser(config.builddmaster.dbuser):563 with dbuser(config.builddmaster.dbuser):
506 yield self.behaviour.handleStatus(564 yield self.behaviour.handleStatus(
507 self.build.buildqueue_record, "GIVENBACK", {})565 self.build.buildqueue_record,
566 {"builder_status": "BuilderStatus.WAITING",
567 "build_status": "BuildStatus.GIVENBACK"})
508568
509 @defer.inlineCallbacks569 @defer.inlineCallbacks
510 def test_builderfail_collection(self):570 def test_builderfail_collection(self):
@@ -513,7 +573,9 @@ class TestHandleStatusMixin:
513 "Build returned unexpected status: %r" % 'BUILDERFAIL'):573 "Build returned unexpected status: %r" % 'BUILDERFAIL'):
514 with dbuser(config.builddmaster.dbuser):574 with dbuser(config.builddmaster.dbuser):
515 yield self.behaviour.handleStatus(575 yield self.behaviour.handleStatus(
516 self.build.buildqueue_record, "BUILDERFAIL", {})576 self.build.buildqueue_record,
577 {"builder_status": "BuilderStatus.WAITING",
578 "build_status": "BuildStatus.BUILDERFAIL"})
517579
518 @defer.inlineCallbacks580 @defer.inlineCallbacks
519 def test_invalid_status_collection(self):581 def test_invalid_status_collection(self):
@@ -522,4 +584,6 @@ class TestHandleStatusMixin:
522 "Build returned unexpected status: %r" % 'BORKED'):584 "Build returned unexpected status: %r" % 'BORKED'):
523 with dbuser(config.builddmaster.dbuser):585 with dbuser(config.builddmaster.dbuser):
524 yield self.behaviour.handleStatus(586 yield self.behaviour.handleStatus(
525 self.build.buildqueue_record, "BORKED", {})587 self.build.buildqueue_record,
588 {"builder_status": "BuilderStatus.WAITING",
589 "build_status": "BuildStatus.BORKED"})
diff --git a/lib/lp/buildmaster/tests/test_interactor.py b/lib/lp/buildmaster/tests/test_interactor.py
index 6e4a790..9aa025c 100644
--- a/lib/lp/buildmaster/tests/test_interactor.py
+++ b/lib/lp/buildmaster/tests/test_interactor.py
@@ -1,4 +1,4 @@
1# Copyright 2009-2020 Canonical Ltd. This software is licensed under the1# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test BuilderInteractor features."""4"""Test BuilderInteractor features."""
@@ -121,21 +121,6 @@ class TestBuilderInteractor(TestCase):
121 super().setUp()121 super().setUp()
122 self.addCleanup(shut_down_default_process_pool)122 self.addCleanup(shut_down_default_process_pool)
123123
124 def test_extractBuildStatus_baseline(self):
125 # extractBuildStatus picks the name of the build status out of a
126 # dict describing the worker's status.
127 worker_status = {'build_status': 'BuildStatus.BUILDING'}
128 self.assertEqual(
129 'BUILDING', BuilderInteractor.extractBuildStatus(worker_status))
130
131 def test_extractBuildStatus_malformed(self):
132 # extractBuildStatus errors out when the status string is not
133 # of the form it expects.
134 worker_status = {'build_status': 'BUILDING'}
135 self.assertRaises(
136 AssertionError, BuilderInteractor.extractBuildStatus,
137 worker_status)
138
139 def resumeWorkerHost(self, builder):124 def resumeWorkerHost(self, builder):
140 vitals = extract_vitals_from_db(builder)125 vitals = extract_vitals_from_db(builder)
141 return BuilderInteractor.resumeWorkerHost(126 return BuilderInteractor.resumeWorkerHost(
diff --git a/lib/lp/code/model/tests/test_recipebuilder.py b/lib/lp/code/model/tests/test_recipebuilder.py
index 9077a08..9c75d68 100644
--- a/lib/lp/code/model/tests/test_recipebuilder.py
+++ b/lib/lp/code/model/tests/test_recipebuilder.py
@@ -1,4 +1,4 @@
1# Copyright 2010-2020 Canonical Ltd. This software is licensed under the1# Copyright 2010-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test RecipeBuildBehaviour."""4"""Test RecipeBuildBehaviour."""
@@ -418,7 +418,11 @@ class TestBuildNotifications(TestCaseWithFactory):
418 self.queue_record, self.queue_record.builder, worker)418 self.queue_record, self.queue_record.builder, worker)
419419
420 def assertDeferredNotifyCount(self, status, behaviour, expected_count):420 def assertDeferredNotifyCount(self, status, behaviour, expected_count):
421 d = behaviour.handleStatus(self.queue_record, status, {'filemap': {}})421 d = behaviour.handleStatus(
422 self.queue_record,
423 {'builder_status': 'BuilderStatus.WAITING',
424 'build_status': 'BuildStatus.%s' % status,
425 'filemap': {}})
422426
423 def cb(result):427 def cb(result):
424 self.assertEqual(expected_count, len(pop_notifications()))428 self.assertEqual(expected_count, len(pop_notifications()))
diff --git a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
index 7804583..cb5438f 100644
--- a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
+++ b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
@@ -1,4 +1,4 @@
1# Copyright 2015-2021 Canonical Ltd. This software is licensed under the1# Copyright 2015-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for `OCIRecipeBuildBehaviour`."""4"""Tests for `OCIRecipeBuildBehaviour`."""
@@ -55,6 +55,7 @@ from lp.buildmaster.interfaces.builder import (
55from lp.buildmaster.interfaces.buildfarmjobbehaviour import (55from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
56 IBuildFarmJobBehaviour,56 IBuildFarmJobBehaviour,
57 )57 )
58from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
58from lp.buildmaster.interfaces.processor import IProcessorSet59from lp.buildmaster.interfaces.processor import IProcessorSet
59from lp.buildmaster.tests.builderproxy import (60from lp.buildmaster.tests.builderproxy import (
60 InProcessProxyAuthAPIFixture,61 InProcessProxyAuthAPIFixture,
@@ -773,15 +774,40 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
773 1, len(os.listdir(os.path.join(self.upload_root, result))))774 1, len(os.listdir(os.path.join(self.upload_root, result))))
774775
775 @defer.inlineCallbacks776 @defer.inlineCallbacks
776 def test_handleStatus_OK_normal_image(self):777 def test_handleStatus_BUILDING(self):
778 # If the builder is BUILDING (or any status other than WAITING),
779 # then the behaviour calls updateStatus but doesn't do anything
780 # else.
781 initial_status = self.build.status
782 bq_id = self.build.buildqueue_record.id
783 worker_status = {"builder_status": "BuilderStatus.BUILDING"}
784 removeSecurityProxy(self.build).updateStatus = FakeMethod()
785 with dbuser(config.builddmaster.dbuser):
786 yield self.behaviour.handleStatus(
787 self.build.buildqueue_record, worker_status)
788 self.assertEqual(None, self.build.log)
789 self.assertEqual(0, len(os.listdir(self.upload_root)))
790 self.assertEqual(
791 [((initial_status,),
792 {"builder": self.builder, "worker_status": worker_status})],
793 removeSecurityProxy(self.build).updateStatus.calls)
794 self.assertEqual(0, len(pop_notifications()), "Notifications received")
795 self.assertEqual(
796 self.build.buildqueue_record,
797 getUtility(IBuildQueueSet).get(bq_id))
798
799 @defer.inlineCallbacks
800 def test_handleStatus_WAITING_OK_normal_image(self):
777 now = datetime.now()801 now = datetime.now()
778 mock_datetime = self.useFixture(MockPatch(802 mock_datetime = self.useFixture(MockPatch(
779 'lp.buildmaster.model.buildfarmjobbehaviour.datetime')).mock803 'lp.buildmaster.model.buildfarmjobbehaviour.datetime')).mock
780 mock_datetime.now = lambda: now804 mock_datetime.now = lambda: now
781 with dbuser(config.builddmaster.dbuser):805 with dbuser(config.builddmaster.dbuser):
782 yield self.behaviour.handleStatus(806 yield self.behaviour.handleStatus(
783 self.build.buildqueue_record, 'OK',807 self.build.buildqueue_record,
784 {'filemap': self.filemap})808 {'builder_status': 'BuilderStatus.WAITING',
809 'build_status': 'BuildStatus.OK',
810 'filemap': self.filemap})
785 self.assertEqual(811 self.assertEqual(
786 ['buildlog', 'manifest_hash', 'digests_hash', 'config_1_hash',812 ['buildlog', 'manifest_hash', 'digests_hash', 'config_1_hash',
787 'layer_2_hash'],813 'layer_2_hash'],
@@ -805,7 +831,7 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
805 self.assertEqual(contents, b'retrieved from librarian')831 self.assertEqual(contents, b'retrieved from librarian')
806832
807 @defer.inlineCallbacks833 @defer.inlineCallbacks
808 def test_handleStatus_OK_reuse_from_other_build(self):834 def test_handleStatus_WAITING_OK_reuse_from_other_build(self):
809 """We should be able to reuse a layer file from a separate build."""835 """We should be able to reuse a layer file from a separate build."""
810 oci_file = self.factory.makeOCIFile(836 oci_file = self.factory.makeOCIFile(
811 layer_file_digest='digest_2',837 layer_file_digest='digest_2',
@@ -820,8 +846,10 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
820 mock_oci_datetime.now = lambda tzinfo=None: now846 mock_oci_datetime.now = lambda tzinfo=None: now
821 with dbuser(config.builddmaster.dbuser):847 with dbuser(config.builddmaster.dbuser):
822 yield self.behaviour.handleStatus(848 yield self.behaviour.handleStatus(
823 self.build.buildqueue_record, 'OK',849 self.build.buildqueue_record,
824 {'filemap': self.filemap})850 {'builder_status': 'BuilderStatus.WAITING',
851 'build_status': 'BuildStatus.OK',
852 'filemap': self.filemap})
825 self.assertEqual(853 self.assertEqual(
826 ['buildlog', 'manifest_hash', 'digests_hash', 'config_1_hash'],854 ['buildlog', 'manifest_hash', 'digests_hash', 'config_1_hash'],
827 self.worker._got_file_record)855 self.worker._got_file_record)
@@ -844,7 +872,7 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
844 self.assertEqual(now, oci_file.date_last_used)872 self.assertEqual(now, oci_file.date_last_used)
845873
846 @defer.inlineCallbacks874 @defer.inlineCallbacks
847 def test_handleStatus_OK_absolute_filepath(self):875 def test_handleStatus_WAITING_OK_absolute_filepath(self):
848876
849 self._createTestFile(877 self._createTestFile(
850 'manifest.json',878 'manifest.json',
@@ -862,11 +890,13 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
862 "'/notvalid/config_file_1.json'."):890 "'/notvalid/config_file_1.json'."):
863 with dbuser(config.builddmaster.dbuser):891 with dbuser(config.builddmaster.dbuser):
864 yield self.behaviour.handleStatus(892 yield self.behaviour.handleStatus(
865 self.build.buildqueue_record, 'OK',893 self.build.buildqueue_record,
866 {'filemap': self.filemap})894 {'builder_status': 'BuilderStatus.WAITING',
895 'build_status': 'BuildStatus.OK',
896 'filemap': self.filemap})
867897
868 @defer.inlineCallbacks898 @defer.inlineCallbacks
869 def test_handleStatus_OK_relative_filepath(self):899 def test_handleStatus_WAITING_OK_relative_filepath(self):
870900
871 self._createTestFile(901 self._createTestFile(
872 'manifest.json',902 'manifest.json',
@@ -882,30 +912,36 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
882 "Build returned a file named '../config_file_1.json'."):912 "Build returned a file named '../config_file_1.json'."):
883 with dbuser(config.builddmaster.dbuser):913 with dbuser(config.builddmaster.dbuser):
884 yield self.behaviour.handleStatus(914 yield self.behaviour.handleStatus(
885 self.build.buildqueue_record, 'OK',915 self.build.buildqueue_record,
886 {'filemap': self.filemap})916 {'builder_status': 'BuilderStatus.WAITING',
917 'build_status': 'BuildStatus.OK',
918 'filemap': self.filemap})
887919
888 @defer.inlineCallbacks920 @defer.inlineCallbacks
889 def test_handleStatus_OK_sets_build_log(self):921 def test_handleStatus_WAITING_OK_sets_build_log(self):
890 # The build log is set during handleStatus.922 # The build log is set during handleStatus.
891 self.assertEqual(None, self.build.log)923 self.assertEqual(None, self.build.log)
892 with dbuser(config.builddmaster.dbuser):924 with dbuser(config.builddmaster.dbuser):
893 yield self.behaviour.handleStatus(925 yield self.behaviour.handleStatus(
894 self.build.buildqueue_record, 'OK',926 self.build.buildqueue_record,
895 {'filemap': self.filemap})927 {'builder_status': 'BuilderStatus.WAITING',
928 'build_status': 'BuildStatus.OK',
929 'filemap': self.filemap})
896 self.assertNotEqual(None, self.build.log)930 self.assertNotEqual(None, self.build.log)
897931
898 @defer.inlineCallbacks932 @defer.inlineCallbacks
899 def test_handleStatus_ABORTED_cancels_cancelling(self):933 def test_handleStatus_WAITING_ABORTED_cancels_cancelling(self):
900 with dbuser(config.builddmaster.dbuser):934 with dbuser(config.builddmaster.dbuser):
901 self.build.updateStatus(BuildStatus.CANCELLING)935 self.build.updateStatus(BuildStatus.CANCELLING)
902 yield self.behaviour.handleStatus(936 yield self.behaviour.handleStatus(
903 self.build.buildqueue_record, "ABORTED", {})937 self.build.buildqueue_record,
938 {"builder_status": "BuilderStatus.WAITING",
939 "build_status": "BuildStatus.ABORTED"})
904 self.assertEqual(0, len(pop_notifications()), "Notifications received")940 self.assertEqual(0, len(pop_notifications()), "Notifications received")
905 self.assertEqual(BuildStatus.CANCELLED, self.build.status)941 self.assertEqual(BuildStatus.CANCELLED, self.build.status)
906942
907 @defer.inlineCallbacks943 @defer.inlineCallbacks
908 def test_handleStatus_ABORTED_illegal_when_building(self):944 def test_handleStatus_WAITING_ABORTED_illegal_when_building(self):
909 self.builder.vm_host = "fake_vm_host"945 self.builder.vm_host = "fake_vm_host"
910 self.behaviour = self.interactor.getBuildBehaviour(946 self.behaviour = self.interactor.getBuildBehaviour(
911 self.build.buildqueue_record, self.builder, self.worker)947 self.build.buildqueue_record, self.builder, self.worker)
@@ -915,16 +951,20 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
915 BuildDaemonError,951 BuildDaemonError,
916 "Build returned unexpected status: %r" % 'ABORTED'):952 "Build returned unexpected status: %r" % 'ABORTED'):
917 yield self.behaviour.handleStatus(953 yield self.behaviour.handleStatus(
918 self.build.buildqueue_record, "ABORTED", {})954 self.build.buildqueue_record,
955 {"builder_status": "BuilderStatus.WAITING",
956 "build_status": "BuildStatus.ABORTED"})
919957
920 @defer.inlineCallbacks958 @defer.inlineCallbacks
921 def test_handleStatus_ABORTED_cancelling_sets_build_log(self):959 def test_handleStatus_WAITING_ABORTED_cancelling_sets_build_log(self):
922 # If a build is intentionally cancelled, the build log is set.960 # If a build is intentionally cancelled, the build log is set.
923 self.assertEqual(None, self.build.log)961 self.assertEqual(None, self.build.log)
924 with dbuser(config.builddmaster.dbuser):962 with dbuser(config.builddmaster.dbuser):
925 self.build.updateStatus(BuildStatus.CANCELLING)963 self.build.updateStatus(BuildStatus.CANCELLING)
926 yield self.behaviour.handleStatus(964 yield self.behaviour.handleStatus(
927 self.build.buildqueue_record, "ABORTED", {})965 self.build.buildqueue_record,
966 {"builder_status": "BuilderStatus.WAITING",
967 "build_status": "BuildStatus.ABORTED"})
928 self.assertNotEqual(None, self.build.log)968 self.assertNotEqual(None, self.build.log)
929969
930 @defer.inlineCallbacks970 @defer.inlineCallbacks
@@ -933,8 +973,10 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
933 self.assertEqual(None, self.build.date_finished)973 self.assertEqual(None, self.build.date_finished)
934 with dbuser(config.builddmaster.dbuser):974 with dbuser(config.builddmaster.dbuser):
935 yield self.behaviour.handleStatus(975 yield self.behaviour.handleStatus(
936 self.build.buildqueue_record, 'OK',976 self.build.buildqueue_record,
937 {'filemap': self.filemap})977 {'builder_status': 'BuilderStatus.WAITING',
978 'build_status': 'BuildStatus.OK',
979 'filemap': self.filemap})
938 self.assertNotEqual(None, self.build.date_finished)980 self.assertNotEqual(None, self.build.date_finished)
939981
940 @defer.inlineCallbacks982 @defer.inlineCallbacks
@@ -944,7 +986,9 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
944 "Build returned unexpected status: %r" % 'GIVENBACK'):986 "Build returned unexpected status: %r" % 'GIVENBACK'):
945 with dbuser(config.builddmaster.dbuser):987 with dbuser(config.builddmaster.dbuser):
946 yield self.behaviour.handleStatus(988 yield self.behaviour.handleStatus(
947 self.build.buildqueue_record, "GIVENBACK", {})989 self.build.buildqueue_record,
990 {"builder_status": "BuilderStatus.WAITING",
991 "build_status": "BuildStatus.GIVENBACK"})
948992
949 @defer.inlineCallbacks993 @defer.inlineCallbacks
950 def test_builderfail_collection(self):994 def test_builderfail_collection(self):
@@ -953,7 +997,9 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
953 "Build returned unexpected status: %r" % 'BUILDERFAIL'):997 "Build returned unexpected status: %r" % 'BUILDERFAIL'):
954 with dbuser(config.builddmaster.dbuser):998 with dbuser(config.builddmaster.dbuser):
955 yield self.behaviour.handleStatus(999 yield self.behaviour.handleStatus(
956 self.build.buildqueue_record, "BUILDERFAIL", {})1000 self.build.buildqueue_record,
1001 {"builder_status": "BuilderStatus.WAITING",
1002 "build_status": "BuildStatus.BUILDERFAIL"})
9571003
958 @defer.inlineCallbacks1004 @defer.inlineCallbacks
959 def test_invalid_status_collection(self):1005 def test_invalid_status_collection(self):
@@ -962,7 +1008,9 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
962 "Build returned unexpected status: %r" % 'BORKED'):1008 "Build returned unexpected status: %r" % 'BORKED'):
963 with dbuser(config.builddmaster.dbuser):1009 with dbuser(config.builddmaster.dbuser):
964 yield self.behaviour.handleStatus(1010 yield self.behaviour.handleStatus(
965 self.build.buildqueue_record, "BORKED", {})1011 self.build.buildqueue_record,
1012 {"builder_status": "BuilderStatus.WAITING",
1013 "build_status": "BuildStatus.BORKED"})
9661014
9671015
968class TestGetUploadMethodsForOCIRecipeBuild(1016class TestGetUploadMethodsForOCIRecipeBuild(
diff --git a/lib/lp/translations/tests/test_translationtemplatesbuildbehaviour.py b/lib/lp/translations/tests/test_translationtemplatesbuildbehaviour.py
index 634ed1a..5b532e5 100644
--- a/lib/lp/translations/tests/test_translationtemplatesbuildbehaviour.py
+++ b/lib/lp/translations/tests/test_translationtemplatesbuildbehaviour.py
@@ -1,4 +1,4 @@
1# Copyright 2010-2020 Canonical Ltd. This software is licensed under the1# Copyright 2010-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Unit tests for TranslationTemplatesBuildBehaviour."""4"""Unit tests for TranslationTemplatesBuildBehaviour."""
@@ -14,7 +14,6 @@ from zope.component import getUtility
1414
15from lp.app.interfaces.launchpad import ILaunchpadCelebrities15from lp.app.interfaces.launchpad import ILaunchpadCelebrities
16from lp.buildmaster.enums import BuildStatus16from lp.buildmaster.enums import BuildStatus
17from lp.buildmaster.interactor import BuilderInteractor
18from lp.buildmaster.interfaces.buildfarmjobbehaviour import (17from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
19 IBuildFarmJobBehaviour,18 IBuildFarmJobBehaviour,
20 )19 )
@@ -164,11 +163,7 @@ class TestTranslationTemplatesBuildBehaviour(
164 d = worker.status()163 d = worker.status()
165164
166 def got_status(status):165 def got_status(status):
167 return (166 return behaviour.handleStatus(queue_item, status), worker.call_log
168 behaviour.handleStatus(
169 queue_item, BuilderInteractor.extractBuildStatus(status),
170 status),
171 worker.call_log)
172167
173 def build_updated(ignored):168 def build_updated(ignored):
174 self.assertEqual(BuildStatus.FULLYBUILT, behaviour.build.status)169 self.assertEqual(BuildStatus.FULLYBUILT, behaviour.build.status)
@@ -193,10 +188,7 @@ class TestTranslationTemplatesBuildBehaviour(
193188
194 def got_status(status):189 def got_status(status):
195 del status['filemap']190 del status['filemap']
196 return behaviour.handleStatus(191 return behaviour.handleStatus(queue_item, status),
197 queue_item,
198 BuilderInteractor.extractBuildStatus(status),
199 status),
200192
201 def build_updated(ignored):193 def build_updated(ignored):
202 self.assertEqual(BuildStatus.FAILEDTOBUILD, behaviour.build.status)194 self.assertEqual(BuildStatus.FAILEDTOBUILD, behaviour.build.status)
@@ -222,10 +214,7 @@ class TestTranslationTemplatesBuildBehaviour(
222214
223 def got_status(status):215 def got_status(status):
224 del status['filemap']216 del status['filemap']
225 return behaviour.handleStatus(217 return behaviour.handleStatus(queue_item, status),
226 queue_item,
227 BuilderInteractor.extractBuildStatus(status),
228 status),
229218
230 def build_updated(ignored):219 def build_updated(ignored):
231 self.assertEqual(BuildStatus.FULLYBUILT, behaviour.build.status)220 self.assertEqual(BuildStatus.FULLYBUILT, behaviour.build.status)
@@ -257,10 +246,7 @@ class TestTranslationTemplatesBuildBehaviour(
257 d = worker.status()246 d = worker.status()
258247
259 def got_status(status):248 def got_status(status):
260 return behaviour.handleStatus(249 return behaviour.handleStatus(queue_item, status),
261 queue_item,
262 BuilderInteractor.extractBuildStatus(status),
263 status),
264250
265 def build_updated(ignored):251 def build_updated(ignored):
266 self.assertEqual(BuildStatus.FULLYBUILT, behaviour.build.status)252 self.assertEqual(BuildStatus.FULLYBUILT, behaviour.build.status)

Subscribers

People subscribed via source and target branches

to status/vote changes: