Merge lp:~cjwatson/launchpad/unicode-logtail into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18103
Proposed branch: lp:~cjwatson/launchpad/unicode-logtail
Merge into: lp:launchpad
Diff against target: 89 lines (+38/-5)
2 files modified
lib/lp/buildmaster/interactor.py (+2/-3)
lib/lp/buildmaster/tests/test_manager.py (+36/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/unicode-logtail
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+296550@code.launchpad.net

Commit message

Always decode logtail as UTF-8 rather than guessing.

Description of the change

Always decode logtail as UTF-8 rather than guessing. We serve completed build logs as UTF-8 as of r17490, so we might as well do the same thing with partial ones.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
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 2016-05-26 13:45:24 +0000
3+++ lib/lp/buildmaster/interactor.py 2016-06-06 12:57:06 +0000
4@@ -42,7 +42,6 @@
5 from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
6 IBuildFarmJobBehaviour,
7 )
8-from lp.services import encoding
9 from lp.services.config import config
10 from lp.services.twistedsupport import cancel_on_timeout
11 from lp.services.twistedsupport.processmonitor import ProcessWithTimeout
12@@ -539,8 +538,8 @@
13 builder_status = slave_status['builder_status']
14 if builder_status == 'BuilderStatus.BUILDING':
15 # Build still building, collect the logtail.
16- vitals.build_queue.logtail = encoding.guess(
17- str(slave_status.get('logtail')))
18+ vitals.build_queue.logtail = str(
19+ slave_status.get('logtail')).decode('UTF-8', errors='replace')
20 transaction.commit()
21 elif builder_status == 'BuilderStatus.ABORTING':
22 # Build is being aborted.
23
24=== modified file 'lib/lp/buildmaster/tests/test_manager.py'
25--- lib/lp/buildmaster/tests/test_manager.py 2015-10-05 17:21:45 +0000
26+++ lib/lp/buildmaster/tests/test_manager.py 2016-06-06 12:57:06 +0000
27@@ -1,3 +1,7 @@
28+# -*- coding: utf-8 -*-
29+# NOTE: The first line above must stay first; do not move the copyright
30+# notice to the top. See http://www.python.org/dev/peps/pep-0263/.
31+#
32 # Copyright 2009-2014 Canonical Ltd. This software is licensed under the
33 # GNU Affero General Public License version 3 (see the file LICENSE).
34
35@@ -230,7 +234,8 @@
36 self.assertIs(None, builder.currentjob)
37 self._checkJobRescued(slave, builder, job)
38
39- def _checkJobUpdated(self, slave, builder, job):
40+ def _checkJobUpdated(self, slave, builder, job,
41+ logtail='This is a build log: 0'):
42 """`SlaveScanner.scan` updates legitimate jobs.
43
44 Job is kept assigned to the active builder and its 'logtail' is
45@@ -242,7 +247,7 @@
46 self.assertTrue(builder.builderok)
47
48 job = getUtility(IBuildQueueSet).get(job.id)
49- self.assertBuildingJob(job, builder, logtail='This is a build log: 0')
50+ self.assertBuildingJob(job, builder, logtail=logtail)
51
52 def testScanUpdatesBuildingJobs(self):
53 # Enable sampledata builder attached to an appropriate testing
54@@ -312,6 +317,35 @@
55 d = scanner.scan()
56 return assert_fails_with(d, xmlrpclib.Fault)
57
58+ def test_scan_of_partial_utf8_logtail(self):
59+ # The builder returns a fixed number of bytes from the tail of the
60+ # log, so the first character can easily end up being invalid UTF-8.
61+ class BrokenUTF8Slave(BuildingSlave):
62+ @defer.inlineCallbacks
63+ def status(self):
64+ status = yield super(BrokenUTF8Slave, self).status()
65+ status["logtail"] = xmlrpclib.Binary(
66+ u"───".encode("UTF-8")[1:])
67+ defer.returnValue(status)
68+
69+ builder = getUtility(IBuilderSet)[BOB_THE_BUILDER_NAME]
70+ login('foo.bar@canonical.com')
71+ builder.builderok = True
72+ self.patch(
73+ BuilderSlave, 'makeBuilderSlave',
74+ FakeMethod(BrokenUTF8Slave(build_id='PACKAGEBUILD-8')))
75+ transaction.commit()
76+ login(ANONYMOUS)
77+
78+ job = builder.currentjob
79+ self.assertBuildingJob(job, builder)
80+
81+ switch_dbuser(config.builddmaster.dbuser)
82+ scanner = self._getScanner()
83+ d = defer.maybeDeferred(scanner.scan)
84+ d.addCallback(
85+ self._checkJobUpdated, builder, job, logtail=u"\uFFFD\uFFFD──")
86+
87 @defer.inlineCallbacks
88 def test_scan_calls_builder_factory_prescanUpdate(self):
89 # SlaveScanner.scan() starts by calling