Merge lp:~cjwatson/launchpad/fix-buildmaster-twisted-agent into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18071
Proposed branch: lp:~cjwatson/launchpad/fix-buildmaster-twisted-agent
Merge into: lp:launchpad
Diff against target: 141 lines (+41/-9)
5 files modified
lib/lp/buildmaster/interactor.py (+14/-5)
lib/lp/buildmaster/manager.py (+2/-1)
lib/lp/buildmaster/model/buildfarmjobbehaviour.py (+4/-3)
lib/lp/buildmaster/tests/mock_slaves.py (+5/-0)
lib/lp/buildmaster/tests/test_interactor.py (+16/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/fix-buildmaster-twisted-agent
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+295833@code.launchpad.net

Commit message

Fix BuilderSlave.getFiles to accept file-like objects again. Transferring build logs from slaves requires this.

Description of the change

Fix BuilderSlave.getFiles to accept file-like objects again. Transferring build logs from slaves requires this.

I also improved the logging a bit, so that we can see builder-side URLs again. The new downloader makes this a bit harder, since a factory is no longer associated with a single URL, but the results of this are reasonably tolerable on dogfood.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/buildmaster/interactor.py'
--- lib/lp/buildmaster/interactor.py 2016-05-25 15:31:48 +0000
+++ lib/lp/buildmaster/interactor.py 2016-05-26 14:04:28 +0000
@@ -57,10 +57,14 @@
57class FileWritingProtocol(Protocol):57class FileWritingProtocol(Protocol):
58 """A protocol that saves data to a file."""58 """A protocol that saves data to a file."""
5959
60 def __init__(self, finished, filename):60 def __init__(self, finished, file_to_write):
61 self.finished = finished61 self.finished = finished
62 self.filename = filename62 if isinstance(file_to_write, (bytes, unicode)):
63 self.file = None63 self.filename = file_to_write
64 self.file = None
65 else:
66 self.filename = None
67 self.file = file_to_write
6468
65 def dataReceived(self, data):69 def dataReceived(self, data):
66 if self.file is None:70 if self.file is None:
@@ -77,7 +81,8 @@
7781
78 def connectionLost(self, reason):82 def connectionLost(self, reason):
79 try:83 try:
80 self.file.close()84 if self.file is not None:
85 self.file.close()
81 except IOError:86 except IOError:
82 self.finished.errback()87 self.finished.errback()
83 else:88 else:
@@ -224,6 +229,10 @@
224 'ensurepresent', sha1sum, url, username, password),229 'ensurepresent', sha1sum, url, username, password),
225 self.timeout * 5)230 self.timeout * 5)
226231
232 def getURL(self, sha1):
233 """Get the URL for a file on the builder with a given SHA-1."""
234 return urlappend(self._file_cache_url, sha1).encode('utf8')
235
227 def getFile(self, sha_sum, file_to_write):236 def getFile(self, sha_sum, file_to_write):
228 """Fetch a file from the builder.237 """Fetch a file from the builder.
229238
@@ -234,7 +243,7 @@
234 :return: A Deferred that calls back when the download is done, or243 :return: A Deferred that calls back when the download is done, or
235 errback with the error string.244 errback with the error string.
236 """245 """
237 file_url = urlappend(self._file_cache_url, sha_sum).encode('utf8')246 file_url = self.getURL(sha_sum)
238 d = Agent(self.reactor, pool=self.pool).request("GET", file_url)247 d = Agent(self.reactor, pool=self.pool).request("GET", file_url)
239248
240 def got_response(response):249 def got_response(response):
241250
=== modified file 'lib/lp/buildmaster/manager.py'
--- lib/lp/buildmaster/manager.py 2015-02-17 07:57:31 +0000
+++ lib/lp/buildmaster/manager.py 2016-05-26 14:04:28 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2014 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 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"""Soyuz buildd slave manager logic."""4"""Soyuz buildd slave manager logic."""
@@ -8,6 +8,7 @@
8__all__ = [8__all__ = [
9 'BuilddManager',9 'BuilddManager',
10 'BUILDD_MANAGER_LOG_NAME',10 'BUILDD_MANAGER_LOG_NAME',
11 'SlaveScanner',
11 ]12 ]
1213
13import datetime14import datetime
1415
=== modified file 'lib/lp/buildmaster/model/buildfarmjobbehaviour.py'
--- lib/lp/buildmaster/model/buildfarmjobbehaviour.py 2015-09-23 21:44:36 +0000
+++ lib/lp/buildmaster/model/buildfarmjobbehaviour.py 2016-05-26 14:04:28 +0000
@@ -291,8 +291,9 @@
291 os.makedirs(upload_path)291 os.makedirs(upload_path)
292292
293 filenames_to_download = []293 filenames_to_download = []
294 for filename in filemap:294 for filename, sha1 in filemap.items():
295 logger.info("Grabbing file: %s" % filename)295 logger.info("Grabbing file: %s (%s)" % (
296 filename, self._slave.getURL(sha1)))
296 out_file_name = os.path.join(upload_path, filename)297 out_file_name = os.path.join(upload_path, filename)
297 # If the evaluated output file name is not within our298 # If the evaluated output file name is not within our
298 # upload path, then we don't try to copy this or any299 # upload path, then we don't try to copy this or any
@@ -300,7 +301,7 @@
300 if not os.path.realpath(out_file_name).startswith(upload_path):301 if not os.path.realpath(out_file_name).startswith(upload_path):
301 raise BuildDaemonError(302 raise BuildDaemonError(
302 "Build returned a file named %r." % filename)303 "Build returned a file named %r." % filename)
303 filenames_to_download.append((filemap[filename], out_file_name))304 filenames_to_download.append((sha1, out_file_name))
304 yield self._slave.getFiles(filenames_to_download)305 yield self._slave.getFiles(filenames_to_download)
305306
306 transaction.commit()307 transaction.commit()
307308
=== modified file 'lib/lp/buildmaster/tests/mock_slaves.py'
--- lib/lp/buildmaster/tests/mock_slaves.py 2016-05-25 15:31:48 +0000
+++ lib/lp/buildmaster/tests/mock_slaves.py 2016-05-26 14:04:28 +0000
@@ -37,6 +37,7 @@
37from lp.buildmaster.interactor import BuilderSlave37from lp.buildmaster.interactor import BuilderSlave
38from lp.buildmaster.interfaces.builder import CannotFetchFile38from lp.buildmaster.interfaces.builder import CannotFetchFile
39from lp.services.config import config39from lp.services.config import config
40from lp.services.webapp import urlappend
40from lp.testing.sampledata import I386_ARCHITECTURE_NAME41from lp.testing.sampledata import I386_ARCHITECTURE_NAME
4142
4243
@@ -136,6 +137,10 @@
136137
137 return d.addCallback(check_present)138 return d.addCallback(check_present)
138139
140 def getURL(self, sha1):
141 return urlappend(
142 'http://localhost:8221/filecache/', sha1).encode('utf8')
143
139 def getFiles(self, files):144 def getFiles(self, files):
140 dl = defer.gatherResults([145 dl = defer.gatherResults([
141 self.getFile(builder_file, local_file)146 self.getFile(builder_file, local_file)
142147
=== modified file 'lib/lp/buildmaster/tests/test_interactor.py'
--- lib/lp/buildmaster/tests/test_interactor.py 2016-05-25 15:31:48 +0000
+++ lib/lp/buildmaster/tests/test_interactor.py 2016-05-26 14:04:28 +0000
@@ -862,3 +862,19 @@
862 dl.append(d)862 dl.append(d)
863863
864 return defer.DeferredList(dl).addCallback(finished_uploading)864 return defer.DeferredList(dl).addCallback(finished_uploading)
865
866 @defer.inlineCallbacks
867 def test_getFiles_with_file_objects(self):
868 # getFiles works with file-like objects as well as file names.
869 self.slave_helper.getServerSlave()
870 slave = self.slave_helper.getClientSlave()
871 temp_fd, temp_name = tempfile.mkstemp()
872 self.addCleanup(os.remove, temp_name)
873 lf = self.factory.makeLibraryFileAlias(
874 'content.txt', content='content')
875 self.layer.txn.commit()
876 yield slave.ensurepresent(lf.content.sha1, lf.http_url, "", "")
877 yield slave.getFiles([(lf.content.sha1, os.fdopen(temp_fd, "w"))])
878 with open(temp_name) as f:
879 self.assertEqual('content', f.read())
880 yield slave.pool.closeCachedConnections()