← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jml/launchpad/buildd-deferred-fo-sho into lp:launchpad/devel

 

Jonathan Lange has proposed merging lp:~jml/launchpad/buildd-deferred-fo-sho into lp:launchpad/devel with lp:~jml/launchpad/buildd-deferred as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


Actually starting to make BuilderSlave have asynchronous APIs.
-- 
https://code.launchpad.net/~jml/launchpad/buildd-deferred-fo-sho/+merge/36037
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jml/launchpad/buildd-deferred-fo-sho into lp:launchpad/devel.
=== modified file 'lib/lp/buildmaster/manager.py'
--- lib/lp/buildmaster/manager.py	2010-09-20 16:17:53 +0000
+++ lib/lp/buildmaster/manager.py	2010-09-20 16:17:54 +0000
@@ -283,15 +283,15 @@
         """Scan the builder and dispatch to it or deal with failures."""
         self.logger.debug("Scanning builder: %s" % self.builder_name)
 
-        try:
-            slave = self.scan()
+        d = self.scan()
+
+        def got_slave(slave):
             if slave is None:
-                self.scheduleNextScanCycle()
+                return self.scheduleNextScanCycle()
             else:
-                # XXX: Ought to return Deferred.
-                self.resumeAndDispatch(slave)
-        except:
-            error = Failure()
+                return self.resumeAndDispatch(slave)
+
+        def disaster(error):
             self.logger.info("Scanning failed with: %s\n%s" %
                 (error.getErrorMessage(), error.getTraceback()))
 
@@ -307,7 +307,11 @@
             assessFailureCounts(builder, error.getErrorMessage())
             transaction.commit()
 
-            self.scheduleNextScanCycle()
+            return self.scheduleNextScanCycle()
+
+        d.addCallback(got_slave)
+        d.addErrback(disaster)
+        return d
 
     @write_transaction
     def scan(self):
@@ -346,7 +350,7 @@
         if self.builder.manual:
             self.logger.debug(
                 '%s is in manual mode, not dispatching.' % self.builder.name)
-            return None
+            return defer.succeed(None)
 
         # If the builder is marked unavailable, don't dispatch anything.
         # Additionaly, because builders can be removed from the pool at
@@ -362,7 +366,7 @@
                     "job" % self.builder.name)
                 job.reset()
                 transaction.commit()
-            return None
+            return defer.succeed(None)
 
         # See if there is a job we can dispatch to the builder slave.
 
@@ -374,15 +378,17 @@
             self.builder.name, self.builder.url, self.builder.vm_host)
         # XXX: Passing buildd_slave=slave overwrites the 'slave' property of
         # self.builder. Not sure why this is needed yet.
-        self.builder.findAndStartJob(buildd_slave=slave)
-        if self.builder.currentjob is not None:
-            # After a successful dispatch we can reset the
-            # failure_count.
-            self.builder.resetFailureCount()
-            transaction.commit()
-            return slave
-
-        return None
+        d = self.builder.findAndStartJob(buildd_slave=slave)
+        def job_started(candidate):
+            if self.builder.currentjob is not None:
+                # After a successful dispatch we can reset the
+                # failure_count.
+                self.builder.resetFailureCount()
+                transaction.commit()
+                return slave
+            else:
+                return None
+        return d.addCallback(job_started)
 
     def resumeAndDispatch(self, slave):
         """Chain the resume and dispatching Deferreds."""

=== modified file 'lib/lp/buildmaster/model/builder.py'
--- lib/lp/buildmaster/model/builder.py	2010-09-20 16:17:53 +0000
+++ lib/lp/buildmaster/model/builder.py	2010-09-20 16:17:54 +0000
@@ -34,6 +34,7 @@
     Count,
     Sum,
     )
+from twisted.internet import defer
 from zope.component import getUtility
 from zope.interface import implements
 
@@ -156,8 +157,10 @@
         return self._server.status()
 
     def ensurepresent(self, sha1sum, url, username, password):
+        # XXX: Nothing external calls this. Make it private.
         """Attempt to ensure the given file is present."""
-        return self._server.ensurepresent(sha1sum, url, username, password)
+        return defer.succeed(
+            self._server.ensurepresent(sha1sum, url, username, password))
 
     def getFile(self, sha_sum):
         """Construct a file-like object to return the named file."""
@@ -196,13 +199,15 @@
         logger.debug("Asking builder on %s to ensure it has file %s "
                      "(%s, %s)" % (self.urlbase, libraryfilealias.filename,
                                    url, libraryfilealias.content.sha1))
-        self.sendFileToSlave(libraryfilealias.content.sha1, url)
+        return self.sendFileToSlave(libraryfilealias.content.sha1, url)
 
     def sendFileToSlave(self, sha1, url, username="", password=""):
         """Helper to send the file at 'url' with 'sha1' to this builder."""
-        present, info = self.ensurepresent(sha1, url, username, password)
-        if not present:
-            raise CannotFetchFile(url, info)
+        d = self.ensurepresent(sha1, url, username, password)
+        def check_present((present, info)):
+            if not present:
+                raise CannotFetchFile(url, info)
+        return d.addCallback(check_present)
 
     def build(self, buildid, builder_type, chroot_sha1, filemap, args):
         """Build a thing on this build slave.
@@ -459,14 +464,19 @@
 
         # Do it.
         build_queue_item.markAsBuilding(self)
-        try:
-            self.current_build_behavior.dispatchBuildToSlave(
-                build_queue_item.id, logger)
-        except BuildSlaveFailure, e:
-            logger.debug("Disabling builder: %s" % self.url, exc_info=1)
+
+        d = self.current_build_behavior.dispatchBuildToSlave(
+            build_queue_item.id, logger)
+
+        def eb_slave_failure(failure):
+            failure.trap(BuildSlaveFailure)
+            e = failure.value
             self.failBuilder(
                 "Exception (%s) when setting up to new job" % (e,))
-        except CannotFetchFile, e:
+
+        def eb_cannot_fetch_file(failure):
+            failure.trap(CannotFetchFile)
+            e = failure.value
             message = """Slave '%s' (%s) was unable to fetch file.
             ****** URL ********
             %s
@@ -475,11 +485,19 @@
             *******************
             """ % (self.name, self.url, e.file_url, e.error_information)
             raise BuildDaemonError(message)
-        except socket.error, e:
+
+        def eb_socket_error(failure):
+            failure.trap(socket.error)
+            e = failure.value
             error_message = "Exception (%s) when setting up new job" % (e,)
             self.handleTimeout(logger, error_message)
             raise BuildSlaveFailure
 
+        d.addErrback(eb_slave_failure)
+        d.addErrback(eb_cannot_fetch_file)
+        d.addErrback(eb_socket_error)
+        return d
+
     def failBuilder(self, reason):
         """See IBuilder"""
         # XXX cprov 2007-04-17: ideally we should be able to notify the
@@ -672,10 +690,13 @@
         :param candidate: The job to dispatch.
         """
         logger = self._getSlaveScannerLogger()
-        try:
-            self.startBuild(candidate, logger)
-        except (BuildSlaveFailure, CannotBuild, BuildBehaviorMismatch), err:
+        d = self.startBuild(candidate, logger)
+        def warn_on_error(failure):
+            failure.trap(
+                BuildSlaveFailure, CannotBuild, BuildBehaviorMismatch)
+            err = failure.value
             logger.warn('Could not build: %s' % err)
+        return d.addErrback(warn_on_error)
 
     def handleTimeout(self, logger, error_message):
         """See IBuilder."""
@@ -716,8 +737,8 @@
         if buildd_slave is not None:
             self.setSlaveForTesting(buildd_slave)
 
-        self._dispatchBuildCandidate(candidate)
-        return candidate
+        d = self._dispatchBuildCandidate(candidate)
+        return d.addCallback(lambda ignored: candidate)
 
     def getBuildQueue(self):
         """See `IBuilder`."""

=== modified file 'lib/lp/buildmaster/tests/test_builder.py'
--- lib/lp/buildmaster/tests/test_builder.py	2010-09-20 16:17:53 +0000
+++ lib/lp/buildmaster/tests/test_builder.py	2010-09-20 16:17:54 +0000
@@ -8,8 +8,8 @@
 import socket
 import xmlrpclib
 
-from testtools.content import Content
-from testtools.content_type import UTF8_TEXT
+from twisted.trial.unittest import TestCase as TrialTestCase
+from twisted.web.client import getPage
 
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -24,10 +24,16 @@
     )
 from canonical.testing.layers import (
     DatabaseFunctionalLayer,
-    LaunchpadZopelessLayer
+    LaunchpadZopelessLayer,
+    TwistedLaunchpadZopelessLayer,
+    TwistedLayer,
     )
 from lp.buildmaster.enums import BuildStatus
-from lp.buildmaster.interfaces.builder import IBuilder, IBuilderSet
+from lp.buildmaster.interfaces.builder import (
+    CannotFetchFile,
+    IBuilder,
+    IBuilderSet,
+    )
 from lp.buildmaster.interfaces.buildfarmjobbehavior import (
     IBuildFarmJobBehavior,
     )
@@ -49,9 +55,12 @@
     )
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import (
-    TestCase,
+    ANONYMOUS,
+    login_as,
+    logout,
     TestCaseWithFactory,
     )
+from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.fakemethod import FakeMethod
 
 
@@ -467,19 +476,11 @@
             self.builder.current_build_behavior, BinaryPackageBuildBehavior)
 
 
-class TestSlave(TestCase):
-    """
-    Integration tests for BuilderSlave that verify how it works against a
-    real slave server.
-    """
-
-    # XXX: JonathanLange 2010-09-20 bug=643521: There are also tests for
-    # BuilderSlave in buildd-slave.txt and in other places. The tests here
-    # ought to become the canonical tests for BuilderSlave vs running buildd
-    # XML-RPC server interaction.
+class SlaveTestHelpers:
 
     # The URL for the XML-RPC service set up by `BuilddSlaveTestSetup`.
-    TEST_URL = 'http://localhost:8221/rpc/'
+    BASE_URL = 'http://localhost:8221'
+    TEST_URL = '%s/rpc/' % (BASE_URL,)
 
     def getServerSlave(self):
         """Set up a test build slave server.
@@ -488,11 +489,14 @@
         """
         tachandler = BuilddSlaveTestSetup()
         tachandler.setUp()
-        def addLogFile(exc_info):
-            self.addDetail(
-                'xmlrpc-log-file',
-                Content(UTF8_TEXT, lambda: open(tachandler.logfile, 'r').read()))
-        self.addOnException(addLogFile)
+        # Basically impossible to do this w/ TrialTestCase. But it would be
+        # really nice to keep it.
+        #
+        # def addLogFile(exc_info):
+        #     self.addDetail(
+        #         'xmlrpc-log-file',
+        #         Content(UTF8_TEXT, lambda: open(tachandler.logfile, 'r').read()))
+        # self.addOnException(addLogFile)
         self.addCleanup(tachandler.tearDown)
         return tachandler
 
@@ -527,7 +531,7 @@
         :return: The build id returned by the slave.
         """
         if build_id is None:
-            build_id = self.getUniqueString()
+            build_id = 'random-build-id'
         tachandler = self.getServerSlave()
         chroot_file = 'fake-chroot'
         dsc_file = 'thing'
@@ -537,6 +541,20 @@
             build_id, 'debian', chroot_file, {'.dsc': dsc_file},
             {'ogrecomponent': 'main'})
 
+
+class TestSlave(TrialTestCase, SlaveTestHelpers):
+    """
+    Integration tests for BuilderSlave that verify how it works against a
+    real slave server.
+    """
+
+    layer = TwistedLayer
+
+    # XXX: JonathanLange 2010-09-20 bug=643521: There are also tests for
+    # BuilderSlave in buildd-slave.txt and in other places. The tests here
+    # ought to become the canonical tests for BuilderSlave vs running buildd
+    # XML-RPC server interaction.
+
     def test_abort(self):
         slave = self.getClientSlave()
         # We need to be in a BUILDING state before we can abort.
@@ -600,13 +618,78 @@
         # ensurepresent checks to see if a file is there.
         self.getServerSlave()
         slave = self.getClientSlave()
-        result = slave.ensurepresent('blahblah', None, None, None)
-        self.assertEqual([False, 'No URL'], result)
+        d = slave.ensurepresent('blahblah', None, None, None)
+        d.addCallback(self.assertEqual, [False, 'No URL'])
+        return d
 
     def test_ensurepresent_actually_there(self):
         # ensurepresent checks to see if a file is there.
         tachandler = self.getServerSlave()
         slave = self.getClientSlave()
         self.makeCacheFile(tachandler, 'blahblah')
-        result = slave.ensurepresent('blahblah', None, None, None)
-        self.assertEqual([True, 'No URL'], result)
+        d = slave.ensurepresent('blahblah', None, None, None)
+        d.addCallback(self.assertEqual, [True, 'No URL'])
+        return d
+
+    def test_sendFileToSlave_not_there(self):
+        self.getServerSlave()
+        slave = self.getClientSlave()
+        d = slave.sendFileToSlave('blahblah', None, None, None)
+        return self.assertFailure(d, CannotFetchFile)
+
+    def test_sendFileToSlave_actually_there(self):
+        tachandler = self.getServerSlave()
+        slave = self.getClientSlave()
+        self.makeCacheFile(tachandler, 'blahblah')
+        d = slave.sendFileToSlave('blahblah', None, None, None)
+        def check_present(ignored):
+            d = slave.ensurepresent('blahblah', None, None, None)
+            return d.addCallback(self.assertEqual, [True, 'No URL'])
+        d.addCallback(check_present)
+        return d
+
+
+class TestSlaveWithLibrarian(TrialTestCase, SlaveTestHelpers):
+    """Tests that need more of Launchpad to run."""
+
+    layer = TwistedLaunchpadZopelessLayer
+
+    def setUp(self):
+        super(TestSlaveWithLibrarian, self)
+        self.factory = LaunchpadObjectFactory()
+        login_as(ANONYMOUS)
+        self.addCleanup(logout)
+
+    def test_ensurepresent_librarian(self):
+        # ensurepresent, when given an http URL for a file will download the
+        # file from that URL and report that the file is present, and it was
+        # downloaded.
+
+        # Use the Librarian because it's a "convenient" web server.
+        lf = self.factory.makeLibraryFileAlias(
+            'HelloWorld.txt', content="Hello World")
+        self.layer.txn.commit()
+        self.getServerSlave()
+        slave = self.getClientSlave()
+        d = slave.ensurepresent(
+            lf.content.sha1, lf.http_url, "", "")
+        d.addCallback(self.assertEqual, [True, 'Download'])
+        return d
+
+    def test_retrieve_files_from_filecache(self):
+        # Files that are present on the slave can be downloaded with a
+        # filename made from the sha1 of the content underneath the
+        # 'filecache' directory.
+        content = "Hello World"
+        lf = self.factory.makeLibraryFileAlias(
+            'HelloWorld.txt', content=content)
+        self.layer.txn.commit()
+        expected_url = '%s/filecache/%s' % (self.BASE_URL, lf.content.sha1)
+        self.getServerSlave()
+        slave = self.getClientSlave()
+        d = slave.ensurepresent(
+            lf.content.sha1, lf.http_url, "", "")
+        def check_file(ignored):
+            d = getPage(expected_url.encode('utf8'))
+            return d.addCallback(self.assertEqual, content)
+        return d.addCallback(check_file)

=== modified file 'lib/lp/code/model/recipebuilder.py'
--- lib/lp/code/model/recipebuilder.py	2010-08-20 20:31:18 +0000
+++ lib/lp/code/model/recipebuilder.py	2010-09-20 16:17:54 +0000
@@ -122,33 +122,36 @@
         if chroot is None:
             raise CannotBuild("Unable to find a chroot for %s" %
                               distroarchseries.displayname)
-        self._builder.slave.cacheFile(logger, chroot)
-
-        # Generate a string which can be used to cross-check when obtaining
-        # results so we know we are referring to the right database object in
-        # subsequent runs.
-        buildid = "%s-%s" % (self.build.id, build_queue_id)
-        cookie = self.buildfarmjob.generateSlaveBuildCookie()
-        chroot_sha1 = chroot.content.sha1
-        logger.debug(
-            "Initiating build %s on %s" % (buildid, self._builder.url))
-
-        args = self._extraBuildArgs(distroarchseries, logger)
-        status, info = self._builder.slave.build(
-            cookie, "sourcepackagerecipe", chroot_sha1, {}, args)
-        message = """%s (%s):
-        ***** RESULT *****
-        %s
-        %s: %s
-        ******************
-        """ % (
-            self._builder.name,
-            self._builder.url,
-            args,
-            status,
-            info,
-            )
-        logger.info(message)
+        d = self._builder.slave.cacheFile(logger, chroot)
+
+        def got_cache_file(ignored):
+            # Generate a string which can be used to cross-check when obtaining
+            # results so we know we are referring to the right database object in
+            # subsequent runs.
+            buildid = "%s-%s" % (self.build.id, build_queue_id)
+            cookie = self.buildfarmjob.generateSlaveBuildCookie()
+            chroot_sha1 = chroot.content.sha1
+            logger.debug(
+                "Initiating build %s on %s" % (buildid, self._builder.url))
+
+            args = self._extraBuildArgs(distroarchseries, logger)
+            # XXX: Soon to be async
+            status, info = self._builder.slave.build(
+                cookie, "sourcepackagerecipe", chroot_sha1, {}, args)
+            message = """%s (%s):
+            ***** RESULT *****
+            %s
+            %s: %s
+            ******************
+            """ % (
+                self._builder.name,
+                self._builder.url,
+                args,
+                status,
+                info,
+                )
+            logger.info(message)
+        return d.addCallback(got_cache_file)
 
     def verifyBuildRequest(self, logger):
         """Assert some pre-build checks.

=== modified file 'lib/lp/soyuz/doc/buildd-slave.txt'
--- lib/lp/soyuz/doc/buildd-slave.txt	2010-04-30 10:00:34 +0000
+++ lib/lp/soyuz/doc/buildd-slave.txt	2010-09-20 16:17:54 +0000
@@ -13,43 +13,6 @@
   >>> from canonical.buildd.tests import BuilddSlaveTestSetup
   >>> BuilddSlaveTestSetup().setUp()
 
-Use simple xmlrpclib client to certify the BuildSlave is running
-
-  >>> import xmlrpclib
-  >>> slave = xmlrpclib.Server('http://localhost:8221/rpc/')
-  >>> slave.echo('Hello World')
-  ['Hello World']
-
-With slave protocol v1.0new, the only way to get files to the slave is to
-put them in the librarian first...
-
-  >>> from canonical.librarian.client import LibrarianClient
-  >>> from StringIO import StringIO
-  >>> from canonical.launchpad.database import LibraryFileAlias
-  >>> import transaction
-  >>> lc = LibrarianClient()
-  >>> helloworld = "Hello World"
-  >>> hw_sio = StringIO(helloworld)
-  >>> alias = lc.addFile("HelloWorld.txt", len(helloworld),
-  ...                    hw_sio, "text/plain")
-  >>> transaction.commit()
-  >>> lf = LibraryFileAlias.get(alias)
-  >>> present, info = slave.ensurepresent(
-  ...     lf.content.sha1, lf.http_url, "", "")
-  >>> present, info
-  (True, 'Download')
-
-As of slave protocol v1.0new, /filecache/SHA1SUM is *THE* way
-to retrieve files from the slave. Verify it works...
-
-  >>> from urllib2 import urlopen
-  >>> f = urlopen("http://localhost:8221/filecache/"; + lf.content.sha1)
-  >>> hw_str = f.read()
-  >>> f.close()
-  >>> hw_str == helloworld
-  True
-
-
 == BuilderSet polling operations ==
 
     >>> import logging
@@ -90,6 +53,7 @@
 
 At this point the buildd-slave is not accessible anymore.
 
+  >>> import xmlrpclib
   >>> s = xmlrpclib.Server('http://localhost:8221/rpc/')
   >>> s.info()
   Traceback (most recent call last):

=== modified file 'lib/lp/soyuz/model/binarypackagebuildbehavior.py'
--- lib/lp/soyuz/model/binarypackagebuildbehavior.py	2010-08-23 16:51:11 +0000
+++ lib/lp/soyuz/model/binarypackagebuildbehavior.py	2010-09-20 16:17:54 +0000
@@ -11,6 +11,7 @@
     'BinaryPackageBuildBehavior',
     ]
 
+from twisted.internet import defer
 from zope.interface import implements
 
 from canonical.launchpad.webapp import urlappend
@@ -38,56 +39,67 @@
         logger.info("startBuild(%s, %s, %s, %s)", self._builder.url,
                     spr.name, spr.version, self.build.pocket.title)
 
-    def dispatchBuildToSlave(self, build_queue_id, logger):
-        """See `IBuildFarmJobBehavior`."""
-
-        # Start the binary package build on the slave builder. First
-        # we send the chroot.
-        chroot = self.build.distro_arch_series.getChroot()
-        self._builder.slave.cacheFile(logger, chroot)
-
+    def _buildFilemapStructure(self, logger):
         # Build filemap structure with the files required in this build
         # and send them to the slave.
         # If the build is private we tell the slave to get the files from the
         # archive instead of the librarian because the slaves cannot
         # access the restricted librarian.
+        dl = []
         private = self.build.archive.private
         if private:
-            self._cachePrivateSourceOnSlave(logger)
+            dl.extend(self._cachePrivateSourceOnSlave(logger))
         filemap = {}
         for source_file in self.build.source_package_release.files:
             lfa = source_file.libraryfile
             filemap[lfa.filename] = lfa.content.sha1
             if not private:
-                self._builder.slave.cacheFile(logger, source_file.libraryfile)
-
-        # Generate a string which can be used to cross-check when obtaining
-        # results so we know we are referring to the right database object in
-        # subsequent runs.
-        buildid = "%s-%s" % (self.build.id, build_queue_id)
-        cookie = self.buildfarmjob.generateSlaveBuildCookie()
-        chroot_sha1 = chroot.content.sha1
-        logger.debug(
-            "Initiating build %s on %s" % (buildid, self._builder.url))
-
-        args = self._extraBuildArgs(self.build)
-        status, info = self._builder.slave.build(
-            cookie, "binarypackage", chroot_sha1, filemap, args)
-        message = """%s (%s):
-        ***** RESULT *****
-        %s
-        %s
-        %s: %s
-        ******************
-        """ % (
-            self._builder.name,
-            self._builder.url,
-            filemap,
-            args,
-            status,
-            info,
-            )
-        logger.info(message)
+                dl.append(
+                    self._builder.slave.cacheFile(
+                        logger, source_file.libraryfile))
+        d = defer.gatherResults(dl)
+        return d.addCallback(lambda ignored: filemap)
+
+    def dispatchBuildToSlave(self, build_queue_id, logger):
+        """See `IBuildFarmJobBehavior`."""
+
+        # Start the binary package build on the slave builder. First
+        # we send the chroot.
+        chroot = self.build.distro_arch_series.getChroot()
+        self._builder.slave.cacheFile(logger, chroot)
+
+        d = self._buildFilemapStructure(logger)
+
+        def got_filemap(filemap):
+            # Generate a string which can be used to cross-check when
+            # obtaining results so we know we are referring to the right
+            # database object in subsequent runs.
+            buildid = "%s-%s" % (self.build.id, build_queue_id)
+            cookie = self.buildfarmjob.generateSlaveBuildCookie()
+            chroot_sha1 = chroot.content.sha1
+            logger.debug(
+                "Initiating build %s on %s" % (buildid, self._builder.url))
+
+            args = self._extraBuildArgs(self.build)
+            status, info = self._builder.slave.build(
+                cookie, "binarypackage", chroot_sha1, filemap, args)
+            message = """%s (%s):
+            ***** RESULT *****
+            %s
+            %s
+            %s: %s
+            ******************
+            """ % (
+                self._builder.name,
+                self._builder.url,
+                filemap,
+                args,
+                status,
+                info,
+                )
+            logger.info(message)
+
+        return d.addCallback(got_filemap)
 
     def verifyBuildRequest(self, logger):
         """Assert some pre-build checks.
@@ -153,6 +165,8 @@
         """Ask the slave to download source files for a private build.
 
         :param logger: A logger used for providing debug information.
+        :return: A list of Deferreds, each of which represents a request
+            to cache a file.
         """
         # The URL to the file in the archive consists of these parts:
         # archive_url / makePoolPath() / filename
@@ -164,6 +178,7 @@
         archive = self.build.archive
         archive_url = archive.archive_url
         component_name = self.build.current_component.name
+        dl = []
         for source_file in self.build.source_package_release.files:
             file_name = source_file.libraryfile.filename
             sha1 = source_file.libraryfile.content.sha1
@@ -174,8 +189,10 @@
             logger.debug("Asking builder on %s to ensure it has file %s "
                          "(%s, %s)" % (
                             self._builder.url, file_name, url, sha1))
-            self._builder.slave.sendFileToSlave(
-                sha1, url, "buildd", archive.buildd_secret)
+            dl.append(
+                self._builder.slave.sendFileToSlave(
+                    sha1, url, "buildd", archive.buildd_secret))
+        return dl
 
     def _extraBuildArgs(self, build):
         """

=== modified file 'lib/lp/soyuz/scripts/buildd.py'
--- lib/lp/soyuz/scripts/buildd.py	2010-08-27 11:19:54 +0000
+++ lib/lp/soyuz/scripts/buildd.py	2010-09-20 16:17:54 +0000
@@ -276,6 +276,8 @@
             self.txn.commit()
 
 
+# XXX: This is the old slave scanner. Julian says it's not running on
+# production. We should either delete it or update it to use the async apis.
 class SlaveScanner(LaunchpadCronScript):
 
     def main(self):

=== modified file 'lib/lp/soyuz/tests/soyuzbuilddhelpers.py'
--- lib/lp/soyuz/tests/soyuzbuilddhelpers.py	2010-09-20 16:17:53 +0000
+++ lib/lp/soyuz/tests/soyuzbuilddhelpers.py	2010-09-20 16:17:54 +0000
@@ -31,6 +31,8 @@
     )
 from lp.testing.sampledata import I386_ARCHITECTURE_NAME
 
+# XXX: Almost everything in this module will need to be revisited & perhaps
+# deleted now that we have new APIs for BuilderSlave.
 
 class MockBuilder:
     """Emulates a IBuilder class."""

=== modified file 'lib/lp/translations/model/translationtemplatesbuildbehavior.py'
--- lib/lp/translations/model/translationtemplatesbuildbehavior.py	2010-08-20 20:31:18 +0000
+++ lib/lp/translations/model/translationtemplatesbuildbehavior.py	2010-09-20 16:17:54 +0000
@@ -41,16 +41,18 @@
         """See `IBuildFarmJobBehavior`."""
         chroot = self._getChroot()
         chroot_sha1 = chroot.content.sha1
-        self._builder.slave.cacheFile(logger, chroot)
-        cookie = self.buildfarmjob.generateSlaveBuildCookie()
-
-        args = {'arch_tag': self._getDistroArchSeries().architecturetag}
-        args.update(self.buildfarmjob.metadata)
-
-        filemap = {}
-
-        self._builder.slave.build(
-            cookie, self.build_type, chroot_sha1, filemap, args)
+        d = self._builder.slave.cacheFile(logger, chroot)
+        def got_cache_file(ignored):
+            cookie = self.buildfarmjob.generateSlaveBuildCookie()
+
+            args = {'arch_tag': self._getDistroArchSeries().architecturetag}
+            args.update(self.buildfarmjob.metadata)
+
+            filemap = {}
+
+            return self._builder.slave.build(
+                cookie, self.build_type, chroot_sha1, filemap, args)
+        return d.addCallback(got_cache_file)
 
     def _getChroot(self):
         return self._getDistroArchSeries().getChroot()


Follow ups