Merge lp:~wgrant/launchpad/builderinteractor-updateBuild into lp:launchpad

Proposed by William Grant
Status: Merged
Merged at revision: 16743
Proposed branch: lp:~wgrant/launchpad/builderinteractor-updateBuild
Merge into: lp:launchpad
Diff against target: 579 lines (+188/-189)
8 files modified
lib/lp/buildmaster/interactor.py (+131/-1)
lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py (+6/-7)
lib/lp/buildmaster/model/buildfarmjobbehavior.py (+1/-137)
lib/lp/buildmaster/tests/test_builder.py (+17/-0)
lib/lp/buildmaster/tests/test_buildfarmjobbehavior.py (+0/-17)
lib/lp/code/model/recipebuilder.py (+0/-2)
lib/lp/translations/model/translationtemplatesbuildbehavior.py (+9/-6)
lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py (+24/-19)
To merge this branch: bzr merge lp:~wgrant/launchpad/builderinteractor-updateBuild
Reviewer Review Type Date Requested Status
Steve Kowalik (community) code Approve
Review via email: mp+182600@code.launchpad.net

Commit message

Move updateBuild from BuildFarmJobBehavior to BuilderInteractor.

Description of the change

Move updateBuild from BuildFarmJobBehavior to BuilderInteractor. BuildFarmJobBehaviors should override handleStatus instead, as all but TranslationTemplatesBuildBehavior already did.

This gets us closer to running BuilderInteractor without touching the database most of the time.

To post a comment you must log in.
Revision history for this message
Steve Kowalik (stevenk) wrote :

My only niggle would be for a comment for the one circular import you've added, but if it's only temporary, then it's fine.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/buildmaster/interactor.py'
2--- lib/lp/buildmaster/interactor.py 2013-08-27 11:35:09 +0000
3+++ lib/lp/buildmaster/interactor.py 2013-08-28 11:11:24 +0000
4@@ -15,6 +15,7 @@
5 from urlparse import urlparse
6 import xmlrpclib
7
8+import transaction
9 from twisted.internet import defer
10 from twisted.web import xmlrpc
11 from twisted.web.client import downloadPage
12@@ -35,9 +36,12 @@
13 IBuildFarmJobBehavior,
14 )
15 from lp.buildmaster.model.buildfarmjobbehavior import IdleBuildBehavior
16+from lp.services import encoding
17 from lp.services.config import config
18 from lp.services.helpers import filenameToContentType
19+from lp.services.job.interfaces.job import JobStatus
20 from lp.services.librarian.interfaces import ILibraryFileAliasSet
21+from lp.services.librarian.interfaces.client import ILibrarianClient
22 from lp.services.librarian.utils import copy_and_close
23 from lp.services.twistedsupport import cancel_on_timeout
24 from lp.services.twistedsupport.processmonitor import ProcessWithTimeout
25@@ -610,7 +614,133 @@
26
27 :return: A Deferred that fires when the slave dialog is finished.
28 """
29- return self._current_build_behavior.updateBuild(queueItem)
30+ logger = logging.getLogger('slave-scanner')
31+
32+ d = self.slaveStatus()
33+
34+ def got_failure(failure):
35+ failure.trap(xmlrpclib.Fault, socket.error)
36+ info = failure.value
37+ info = ("Could not contact the builder %s, caught a (%s)"
38+ % (queueItem.builder.url, info))
39+ raise BuildSlaveFailure(info)
40+
41+ def got_status(slave_status):
42+ builder_status_handlers = {
43+ 'BuilderStatus.IDLE': self.updateBuild_IDLE,
44+ 'BuilderStatus.BUILDING': self.updateBuild_BUILDING,
45+ 'BuilderStatus.ABORTING': self.updateBuild_ABORTING,
46+ 'BuilderStatus.ABORTED': self.updateBuild_ABORTED,
47+ 'BuilderStatus.WAITING': self.updateBuild_WAITING,
48+ }
49+
50+ builder_status = slave_status['builder_status']
51+ if builder_status not in builder_status_handlers:
52+ logger.critical(
53+ "Builder on %s returned unknown status %s, failing it"
54+ % (self.builder.url, builder_status))
55+ self.builder.failBuilder(
56+ "Unknown status code (%s) returned from status() probe."
57+ % builder_status)
58+ # XXX: This will leave the build and job in a bad state, but
59+ # should never be possible, since our builder statuses are
60+ # known.
61+ queueItem._builder = None
62+ queueItem.setDateStarted(None)
63+ transaction.commit()
64+ return
65+
66+ # Since logtail is a xmlrpclib.Binary container and it is
67+ # returned from the IBuilder content class, it arrives
68+ # protected by a Zope Security Proxy, which is not declared,
69+ # thus empty. Before passing it to the status handlers we
70+ # will simply remove the proxy.
71+ logtail = removeSecurityProxy(slave_status.get('logtail'))
72+
73+ method = builder_status_handlers[builder_status]
74+ return defer.maybeDeferred(
75+ method, queueItem, slave_status, logtail, logger)
76+
77+ d.addErrback(got_failure)
78+ d.addCallback(got_status)
79+ return d
80+
81+ def updateBuild_IDLE(self, queueItem, slave_status, logtail, logger):
82+ """Somehow the builder forgot about the build job.
83+
84+ Log this and reset the record.
85+ """
86+ logger.warn(
87+ "Builder %s forgot about buildqueue %d -- resetting buildqueue "
88+ "record" % (queueItem.builder.url, queueItem.id))
89+ queueItem.reset()
90+ transaction.commit()
91+
92+ def updateBuild_BUILDING(self, queueItem, slave_status, logtail, logger):
93+ """Build still building, collect the logtail"""
94+ if queueItem.job.status != JobStatus.RUNNING:
95+ queueItem.job.start()
96+ queueItem.logtail = encoding.guess(str(logtail))
97+ transaction.commit()
98+
99+ def updateBuild_ABORTING(self, queueItem, slave_status, logtail, logger):
100+ """Build was ABORTED.
101+
102+ Master-side should wait until the slave finish the process correctly.
103+ """
104+ queueItem.logtail = "Waiting for slave process to be terminated"
105+ transaction.commit()
106+
107+ def updateBuild_ABORTED(self, queueItem, slave_status, logtail, logger):
108+ """ABORTING process has successfully terminated.
109+
110+ Clean the builder for another jobs.
111+ """
112+ d = self.cleanSlave()
113+
114+ def got_cleaned(ignored):
115+ queueItem.builder = None
116+ if queueItem.job.status != JobStatus.FAILED:
117+ queueItem.job.fail()
118+ queueItem.specific_job.jobAborted()
119+ transaction.commit()
120+ return d.addCallback(got_cleaned)
121+
122+ def extractBuildStatus(self, slave_status):
123+ """Read build status name.
124+
125+ :param slave_status: build status dict as passed to the
126+ updateBuild_* methods.
127+ :return: the unqualified status name, e.g. "OK".
128+ """
129+ status_string = slave_status['build_status']
130+ lead_string = 'BuildStatus.'
131+ assert status_string.startswith(lead_string), (
132+ "Malformed status string: '%s'" % status_string)
133+
134+ return status_string[len(lead_string):]
135+
136+ def updateBuild_WAITING(self, queueItem, slave_status, logtail, logger):
137+ """Perform the actions needed for a slave in a WAITING state
138+
139+ Buildslave can be WAITING in five situations:
140+
141+ * Build has failed, no filemap is received (PACKAGEFAIL, DEPFAIL,
142+ CHROOTFAIL, BUILDERFAIL,
143+ ABORTED)
144+
145+ * Build has been built successfully (BuildStatus.OK), in this case
146+ we have a 'filemap', so we can retrieve those files and store in
147+ Librarian with getFileFromSlave() and then pass the binaries to
148+ the uploader for processing.
149+ """
150+ librarian = getUtility(ILibrarianClient)
151+ build_status = self.extractBuildStatus(slave_status)
152+
153+ # XXX: dsilvers 2005-03-02: Confirm the builder has the right build?
154+ d = self._current_build_behavior.handleStatus(
155+ build_status, librarian, slave_status)
156+ return d
157
158 def transferSlaveFileToLibrarian(self, file_sha1, filename, private):
159 """Transfer a file from the slave to the librarian.
160
161=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py'
162--- lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2013-08-22 04:07:24 +0000
163+++ lib/lp/buildmaster/interfaces/buildfarmjobbehavior.py 2013-08-28 11:11:24 +0000
164@@ -66,11 +66,10 @@
165 supposed to be.
166 """
167
168- def updateBuild(queueItem):
169- """Verify the current build job status.
170-
171- Perform the required actions for each state.
172-
173- :param queueItem: The `BuildQueue` for the build.
174- :return: A Deferred that fires when the update is done.
175+ def handleStatus(status, librarian, slave_status):
176+ """Update the build from a WAITING slave result.
177+
178+ :param status: The tail of the BuildStatus (eg. OK or PACKAGEFAIL).
179+ :param librarian: An `ILibrarianClient` to use for file uploads.
180+ :param slave_status: Slave status from `BuilderInteractor.slaveStatus`.
181 """
182
183=== modified file 'lib/lp/buildmaster/model/buildfarmjobbehavior.py'
184--- lib/lp/buildmaster/model/buildfarmjobbehavior.py 2013-08-28 04:40:32 +0000
185+++ lib/lp/buildmaster/model/buildfarmjobbehavior.py 2013-08-28 11:11:24 +0000
186@@ -13,14 +13,10 @@
187 import datetime
188 import logging
189 import os.path
190-import socket
191-import xmlrpclib
192
193 import transaction
194 from twisted.internet import defer
195-from zope.component import getUtility
196 from zope.interface import implements
197-from zope.security.proxy import removeSecurityProxy
198
199 from lp.buildmaster.enums import (
200 BuildFarmJobType,
201@@ -34,10 +30,7 @@
202 BuildBehaviorMismatch,
203 IBuildFarmJobBehavior,
204 )
205-from lp.services import encoding
206 from lp.services.config import config
207-from lp.services.job.interfaces.job import JobStatus
208-from lp.services.librarian.interfaces.client import ILibrarianClient
209
210
211 SLAVE_LOG_FILENAME = 'buildlog'
212@@ -105,135 +98,6 @@
213 self.build.setLog(lfa_id)
214 transaction.commit()
215
216- def updateBuild(self, queueItem):
217- """See `IBuildFarmJobBehavior`."""
218- logger = logging.getLogger('slave-scanner')
219-
220- d = self._interactor.slaveStatus()
221-
222- def got_failure(failure):
223- failure.trap(xmlrpclib.Fault, socket.error)
224- info = failure.value
225- info = ("Could not contact the builder %s, caught a (%s)"
226- % (queueItem.builder.url, info))
227- raise BuildSlaveFailure(info)
228-
229- def got_status(slave_status):
230- builder_status_handlers = {
231- 'BuilderStatus.IDLE': self.updateBuild_IDLE,
232- 'BuilderStatus.BUILDING': self.updateBuild_BUILDING,
233- 'BuilderStatus.ABORTING': self.updateBuild_ABORTING,
234- 'BuilderStatus.ABORTED': self.updateBuild_ABORTED,
235- 'BuilderStatus.WAITING': self.updateBuild_WAITING,
236- }
237-
238- builder_status = slave_status['builder_status']
239- if builder_status not in builder_status_handlers:
240- logger.critical(
241- "Builder on %s returned unknown status %s, failing it"
242- % (self._builder.url, builder_status))
243- self._builder.failBuilder(
244- "Unknown status code (%s) returned from status() probe."
245- % builder_status)
246- # XXX: This will leave the build and job in a bad state, but
247- # should never be possible, since our builder statuses are
248- # known.
249- queueItem._builder = None
250- queueItem.setDateStarted(None)
251- transaction.commit()
252- return
253-
254- # Since logtail is a xmlrpclib.Binary container and it is
255- # returned from the IBuilder content class, it arrives
256- # protected by a Zope Security Proxy, which is not declared,
257- # thus empty. Before passing it to the status handlers we
258- # will simply remove the proxy.
259- logtail = removeSecurityProxy(slave_status.get('logtail'))
260-
261- method = builder_status_handlers[builder_status]
262- return defer.maybeDeferred(
263- method, queueItem, slave_status, logtail, logger)
264-
265- d.addErrback(got_failure)
266- d.addCallback(got_status)
267- return d
268-
269- def updateBuild_IDLE(self, queueItem, slave_status, logtail, logger):
270- """Somehow the builder forgot about the build job.
271-
272- Log this and reset the record.
273- """
274- logger.warn(
275- "Builder %s forgot about buildqueue %d -- resetting buildqueue "
276- "record" % (queueItem.builder.url, queueItem.id))
277- queueItem.reset()
278- transaction.commit()
279-
280- def updateBuild_BUILDING(self, queueItem, slave_status, logtail, logger):
281- """Build still building, collect the logtail"""
282- if queueItem.job.status != JobStatus.RUNNING:
283- queueItem.job.start()
284- queueItem.logtail = encoding.guess(str(logtail))
285- transaction.commit()
286-
287- def updateBuild_ABORTING(self, queueItem, slave_status, logtail, logger):
288- """Build was ABORTED.
289-
290- Master-side should wait until the slave finish the process correctly.
291- """
292- queueItem.logtail = "Waiting for slave process to be terminated"
293- transaction.commit()
294-
295- def updateBuild_ABORTED(self, queueItem, slave_status, logtail, logger):
296- """ABORTING process has successfully terminated.
297-
298- Clean the builder for another jobs.
299- """
300- d = self._interactor.cleanSlave()
301-
302- def got_cleaned(ignored):
303- queueItem.builder = None
304- if queueItem.job.status != JobStatus.FAILED:
305- queueItem.job.fail()
306- queueItem.specific_job.jobAborted()
307- transaction.commit()
308- return d.addCallback(got_cleaned)
309-
310- def extractBuildStatus(self, slave_status):
311- """Read build status name.
312-
313- :param slave_status: build status dict as passed to the
314- updateBuild_* methods.
315- :return: the unqualified status name, e.g. "OK".
316- """
317- status_string = slave_status['build_status']
318- lead_string = 'BuildStatus.'
319- assert status_string.startswith(lead_string), (
320- "Malformed status string: '%s'" % status_string)
321-
322- return status_string[len(lead_string):]
323-
324- def updateBuild_WAITING(self, queueItem, slave_status, logtail, logger):
325- """Perform the actions needed for a slave in a WAITING state
326-
327- Buildslave can be WAITING in five situations:
328-
329- * Build has failed, no filemap is received (PACKAGEFAIL, DEPFAIL,
330- CHROOTFAIL, BUILDERFAIL,
331- ABORTED)
332-
333- * Build has been built successfully (BuildStatus.OK), in this case
334- we have a 'filemap', so we can retrieve those files and store in
335- Librarian with getFileFromSlave() and then pass the binaries to
336- the uploader for processing.
337- """
338- librarian = getUtility(ILibrarianClient)
339- build_status = self.extractBuildStatus(slave_status)
340-
341- # XXX: dsilvers 2005-03-02: Confirm the builder has the right build?
342- d = self.handleStatus(build_status, librarian, slave_status)
343- return d
344-
345 # The list of build status values for which email notifications are
346 # allowed to be sent. It is up to each callback as to whether it will
347 # consider sending a notification but it won't do so if the status is not
348@@ -241,7 +105,7 @@
349 ALLOWED_STATUS_NOTIFICATIONS = ['OK', 'PACKAGEFAIL', 'CHROOTFAIL']
350
351 def handleStatus(self, status, librarian, slave_status):
352- """See `IPackageBuild`."""
353+ """See `IBuildFarmJobBehavior`."""
354 from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME
355 logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME)
356 send_notification = status in self.ALLOWED_STATUS_NOTIFICATIONS
357
358=== modified file 'lib/lp/buildmaster/tests/test_builder.py'
359--- lib/lp/buildmaster/tests/test_builder.py 2013-08-27 10:33:01 +0000
360+++ lib/lp/buildmaster/tests/test_builder.py 2013-08-28 11:11:24 +0000
361@@ -251,6 +251,23 @@
362
363 run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=10)
364
365+ def test_extractBuildStatus_baseline(self):
366+ # extractBuildStatus picks the name of the build status out of a
367+ # dict describing the slave's status.
368+ slave_status = {'build_status': 'BuildStatus.BUILDING'}
369+ interactor = BuilderInteractor(MockBuilder())
370+ self.assertEqual(
371+ BuildStatus.BUILDING.name,
372+ interactor.extractBuildStatus(slave_status))
373+
374+ def test_extractBuildStatus_malformed(self):
375+ # extractBuildStatus errors out when the status string is not
376+ # of the form it expects.
377+ slave_status = {'build_status': 'BUILDING'}
378+ interactor = BuilderInteractor(MockBuilder())
379+ self.assertRaises(
380+ AssertionError, interactor.extractBuildStatus, slave_status)
381+
382 def test_updateStatus_aborts_lost_and_broken_slave(self):
383 # A slave that's 'lost' should be aborted; when the slave is
384 # broken then abort() should also throw a fault.
385
386=== modified file 'lib/lp/buildmaster/tests/test_buildfarmjobbehavior.py'
387--- lib/lp/buildmaster/tests/test_buildfarmjobbehavior.py 2013-08-27 11:35:09 +0000
388+++ lib/lp/buildmaster/tests/test_buildfarmjobbehavior.py 2013-08-28 11:11:24 +0000
389@@ -78,23 +78,6 @@
390 """Create a `BuildQueue` object."""
391 return self.factory.makeSourcePackageRecipeBuildJob()
392
393- def test_extractBuildStatus_baseline(self):
394- # extractBuildStatus picks the name of the build status out of a
395- # dict describing the slave's status.
396- slave_status = {'build_status': 'BuildStatus.BUILDING'}
397- behavior = self._makeBehavior()
398- self.assertEqual(
399- BuildStatus.BUILDING.name,
400- behavior.extractBuildStatus(slave_status))
401-
402- def test_extractBuildStatus_malformed(self):
403- # extractBuildStatus errors out when the status string is not
404- # of the form it expects.
405- slave_status = {'build_status': 'BUILDING'}
406- behavior = self._makeBehavior()
407- self.assertRaises(
408- AssertionError, behavior.extractBuildStatus, slave_status)
409-
410 def test_cookie_baseline(self):
411 buildfarmjob = self.factory.makeTranslationTemplatesBuildJob()
412
413
414=== modified file 'lib/lp/code/model/recipebuilder.py'
415--- lib/lp/code/model/recipebuilder.py 2013-08-19 23:23:19 +0000
416+++ lib/lp/code/model/recipebuilder.py 2013-08-28 11:11:24 +0000
417@@ -43,8 +43,6 @@
418 ALLOWED_STATUS_NOTIFICATIONS = [
419 'OK', 'PACKAGEFAIL', 'DEPFAIL', 'CHROOTFAIL']
420
421- status = None
422-
423 @property
424 def build(self):
425 return self.buildfarmjob.build
426
427=== modified file 'lib/lp/translations/model/translationtemplatesbuildbehavior.py'
428--- lib/lp/translations/model/translationtemplatesbuildbehavior.py 2013-08-19 23:23:19 +0000
429+++ lib/lp/translations/model/translationtemplatesbuildbehavior.py 2013-08-28 11:11:24 +0000
430@@ -11,6 +11,7 @@
431 'TranslationTemplatesBuildBehavior',
432 ]
433
434+import logging
435 import os
436 import tempfile
437
438@@ -118,8 +119,8 @@
439 status['filemap'] = raw_slave_status[3]
440
441 @defer.inlineCallbacks
442- def updateBuild_WAITING(self, queue_item, slave_status, logtail, logger):
443- """Deal with a finished ("WAITING" state, perversely) build job.
444+ def handleStatus(self, status, librarian, slave_status, queue_item=None):
445+ """Deal with a finished build job.
446
447 Retrieves tarball and logs from the slave, then cleans up the
448 slave so it's ready for a next job and destroys the queue item.
449@@ -127,15 +128,17 @@
450 If this fails for whatever unforeseen reason, a future run will
451 retry it.
452 """
453- build_status = self.extractBuildStatus(slave_status)
454-
455+ from lp.buildmaster.manager import BUILDD_MANAGER_LOG_NAME
456+ logger = logging.getLogger(BUILDD_MANAGER_LOG_NAME)
457+ if queue_item is None:
458+ queue_item = self.build.buildqueue_record
459 logger.info(
460 "Templates generation job %s for %s finished with status %s." % (
461 queue_item.specific_job.getName(),
462 queue_item.specific_job.branch.bzr_identity,
463- build_status))
464+ status))
465
466- if build_status == 'OK':
467+ if status == 'OK':
468 self.build.updateStatus(
469 BuildStatus.UPLOADING, builder=queue_item.builder)
470 transaction.commit()
471
472=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py'
473--- lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py 2013-08-27 09:17:07 +0000
474+++ lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py 2013-08-28 11:11:24 +0000
475@@ -188,7 +188,7 @@
476 d = behavior._readTarball(buildqueue, {path: path}, logging)
477 return d.addCallback(got_tarball)
478
479- def test_updateBuild_WAITING_OK(self):
480+ def test_handleStatus_OK(self):
481 # Hopefully, a build will succeed and produce a tarball.
482 behavior = self.makeBehavior()
483 behavior._uploadTarball = FakeMethod()
484@@ -212,8 +212,11 @@
485 'build_status': status[1],
486 'filemap': {'translation-templates.tar.gz': 'foo'},
487 }
488- return behavior.updateBuild_WAITING(
489- queue_item, slave_status, None, logging), slave_call_log
490+ return (
491+ behavior.handleStatus(
492+ behavior._interactor.extractBuildStatus(slave_status),
493+ None, slave_status, queue_item=queue_item),
494+ slave_call_log)
495
496 def build_updated(ignored):
497 self.assertEqual(BuildStatus.FULLYBUILT, behavior.build.status)
498@@ -229,7 +232,7 @@
499 d.addCallback(build_updated)
500 return d
501
502- def test_updateBuild_WAITING_failed(self):
503+ def test_handleStatus_failed(self):
504 # Builds may also fail (and produce no tarball).
505 behavior = self.makeBehavior()
506 behavior._uploadTarball = FakeMethod()
507@@ -247,15 +250,15 @@
508 'BuildStatus.FAILEDTOBUILD',
509 status[2],
510 )
511- status_dict = {
512+ slave_status = {
513 'builder_status': raw_status[0],
514 'build_status': raw_status[1],
515 }
516- behavior.updateSlaveStatus(raw_status, status_dict)
517- self.assertNotIn('filemap', status_dict)
518-
519- return behavior.updateBuild_WAITING(
520- queue_item, status_dict, None, logging)
521+ behavior.updateSlaveStatus(raw_status, slave_status)
522+ self.assertNotIn('filemap', slave_status)
523+ return behavior.handleStatus(
524+ behavior._interactor.extractBuildStatus(slave_status),
525+ None, slave_status, queue_item=queue_item),
526
527 def build_updated(ignored):
528 self.assertEqual(BuildStatus.FAILEDTOBUILD, behavior.build.status)
529@@ -270,7 +273,7 @@
530 d.addCallback(build_updated)
531 return d
532
533- def test_updateBuild_WAITING_notarball(self):
534+ def test_handleStatus_notarball(self):
535 # Even if the build status is "OK," absence of a tarball will
536 # not faze the Behavior class.
537 behavior = self.makeBehavior()
538@@ -288,14 +291,15 @@
539 'BuildStatus.OK',
540 status[2],
541 )
542- status_dict = {
543+ slave_status = {
544 'builder_status': raw_status[0],
545 'build_status': raw_status[1],
546 }
547- behavior.updateSlaveStatus(raw_status, status_dict)
548- self.assertFalse('filemap' in status_dict)
549- return behavior.updateBuild_WAITING(
550- queue_item, status_dict, None, logging)
551+ behavior.updateSlaveStatus(raw_status, slave_status)
552+ self.assertFalse('filemap' in slave_status)
553+ return behavior.handleStatus(
554+ behavior._interactor.extractBuildStatus(slave_status),
555+ None, slave_status, queue_item=queue_item),
556
557 def build_updated(ignored):
558 self.assertEqual(BuildStatus.FULLYBUILT, behavior.build.status)
559@@ -308,7 +312,7 @@
560 d.addCallback(build_updated)
561 return d
562
563- def test_updateBuild_WAITING_uploads(self):
564+ def test_handleStatus_uploads(self):
565 productseries = self.makeProductSeriesWithBranchForTranslation()
566 branch = productseries.branch
567 behavior = self.makeBehavior(branch=branch)
568@@ -336,8 +340,9 @@
569 'build_id': status[2],
570 }
571 behavior.updateSlaveStatus(status, slave_status)
572- return behavior.updateBuild_WAITING(
573- queue_item, slave_status, None, logging)
574+ return behavior.handleStatus(
575+ behavior._interactor.extractBuildStatus(slave_status),
576+ None, slave_status, queue_item=queue_item),
577
578 def build_updated(ignored):
579 self.assertEqual(BuildStatus.FULLYBUILT, behavior.build.status)