Merge lp:~danilo/launchpad/bug-817398 into lp:launchpad

Proposed by Данило Шеган
Status: Merged
Approved by: Данило Шеган
Approved revision: no longer in the source branch.
Merged at revision: 14171
Proposed branch: lp:~danilo/launchpad/bug-817398
Merge into: lp:launchpad
Diff against target: 349 lines (+99/-19)
10 files modified
configs/development/launchpad-lazr.conf (+1/-0)
lib/canonical/buildd/debian/changelog (+6/-0)
lib/canonical/buildd/generate-translation-templates (+3/-2)
lib/lp/poppy/tests/test_poppy.py (+3/-3)
lib/lp/translations/configure.zcml (+7/-1)
lib/lp/translations/model/translationtemplatesbuild.py (+12/-2)
lib/lp/translations/model/translationtemplatesbuildbehavior.py (+50/-9)
lib/lp/translations/stories/buildfarm/xx-build-summary.txt (+1/-1)
lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py (+13/-0)
utilities/make-lp-user (+3/-1)
To merge this branch: bzr merge lp:~danilo/launchpad/bug-817398
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+79571@code.launchpad.net

Commit message

[r=jtv][bug=817398] Pass the path pottery is installed into as PYTHONPATH when running generate_translation_templates.py on the buildd slaves. Also link all the relevant bits for TranslationTemplateBuilds to show up in the builder history web pages together with their log files. Oh yeah, finally.

Description of the change

= Bug 817398: template generation (WIP) =

Template generation has stopped working when buildd default image has switched to Ubuntu Natty (as the development version). That included Python 2.7 by default and generate-translation-templates hard-coded Python 2.6 module path.

This is easy to fix, but this has been broken for quite some time (maybe a full year even) because there is no proper logging for TranslationTemplatesBuild jobs. I decided to introduce that to avoid further breakage going unnoticed for this long.

== Implementation details ==

1. configs/development/launchpad-lazr.conf: change the default development poppy port so it doesn't conflict with codehosting port, thus allowing both to be run locally simultaneously
2. utilities/make-lp-user fails for me with no default timeout function set, so setting one makes it work
3. generate-translation-templates is an actual fix for the bug 817398; as an extra measure of protection, I pass PYTHONPATH in the ultimate script run to ensure modules are found even if Python 2.8 ever comes out for instance
4. configure.zcml copies a similar declaration that SourcePackageRecipeBuild is using (otherwise, a removeSecurityProxy would have to be used inside buildmaster/model/manager.py when .status is being set, and in other places introduced in this branch)
5. translationtemplatebuild.py provides a nicer title for TranslationTemplateBuilds compared to the default, and provides a direct link to librarian files for logs from the builds (since we do not support private branches for template generation, no need to proxy these URLs).
6. translationtemplatesbuildbehavior.py introduces fetching of the log files and saving them like PackageBuildDerived does

I've only did the minimal test changes. I don't think I can spend more time on this branch, but I did spend a lot of time trying stuff out locally. Ideally, most of this code would be refactored to use the "generic" infrastructure provided by things like PackageBuildDerived, but that's more involved than it sounds.

== Tests ==

bin/test -vvt translationtemplatesbuild -t xx-build-behavior.txt -t TestPoppy

== Demo and Q/A ==

 * Set up translation import for a project (translations.l.n/<project/+configure-translations and translations.l.n/<project>/trunk/+translations-settings
 * Push a branch which should generate translation template (eg. lp:eog) to staging, link it to the trunk series (what was set-up above)
 * Run cronscripts/scan-branches.py to scan the branch and generate translation templates build job
 * Wait for build-manager to pick it up and build it (you may need to ask for it to be run on staging as "bin/twistd --logile ... --pidfile ... -y daemons/buildd-manager.tac -n" — -n to not daemonize it so you could see any exceptions if they happen)
 * When the job finishes, it should keep the log in the builder history available from the builder page on launchpad.net/builders

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  configs/development/launchpad-lazr.conf
  lib/canonical/buildd/generate-translation-templates
  lib/canonical/buildd/debian/changelog
  lib/lp/poppy/tests/test_poppy.py
  lib/lp/translations/configure.zcml
  lib/lp/translations/model/translationtemplatesbuild.py
  lib/lp/translations/model/translationtemplatesbuildbehavior.py
  lib/lp/translations/stories/buildfarm/xx-build-summary.txt
  lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py
  utilities/make-lp-user

./configs/development/launchpad-lazr.conf
      92: Line exceeds 78 characters.
     111: Line exceeds 78 characters.
     125: Line exceeds 78 characters.
./lib/canonical/buildd/generate-translation-templates
      59: Line exceeds 78 characters.
./lib/canonical/buildd/debian/changelog
     232: Line exceeds 78 characters.
     298: Line exceeds 78 characters.
     488: Line exceeds 78 characters.
     495: Line exceeds 78 characters.
     516: Line exceeds 78 characters.
     522: Line exceeds 78 characters.
     528: Line exceeds 78 characters.
     534: Line exceeds 78 characters.
     541: Line exceeds 78 characters.
     549: Line exceeds 78 characters.
     556: Line exceeds 78 characters.
     570: Line exceeds 78 characters.
     576: Line exceeds 78 characters.
     582: Line exceeds 78 characters.
     590: Line exceeds 78 characters.
     596: Line exceeds 78 characters.
     603: Line exceeds 78 characters.
     614: Line exceeds 78 characters.
     623: Line exceeds 78 characters.
     630: Line exceeds 78 characters.
     636: Line exceeds 78 characters.
     643: Line exceeds 78 characters.
     649: Line exceeds 78 characters.
     655: Line exceeds 78 characters.
     662: Line exceeds 78 characters.
     669: Line exceeds 78 characters.
     676: Line exceeds 78 characters.
     683: Line exceeds 78 characters.
     689: Line exceeds 78 characters.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Looks good. As agreed on IRC, please make test_poppy.py get poppy's port number from the config so you don't have to hard-code matching port numbers.

It's a good thing I'm reviewing this branch. It is going to clash with one I have pending, which puts buildd-manager in a read-only transaction policy by default. Build-master callbacks that need to write to the database will then have to open an explicit write transaction, make their changes, and commit before returning. Otherwise there seems to be a risk of another builder committing or aborting your changes while your builder is waiting for a slave XMLRPC call to return (and we've had reports of problems that seem to be symptoms of this actually happening).

Thanks for fixing this. Lack of monitoring of these jobs has been a thorn in my side for years now!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configs/development/launchpad-lazr.conf'
2--- configs/development/launchpad-lazr.conf 2011-09-30 16:01:42 +0000
3+++ configs/development/launchpad-lazr.conf 2011-10-19 12:27:49 +0000
4@@ -239,6 +239,7 @@
5 authentication_endpoint: http://xmlrpc-private.launchpad.dev:8087/authserver
6 host_key_private=lib/lp/poppy/tests/poppy-sftp
7 host_key_public=lib/lp/poppy/tests/poppy-sftp.pub
8+port: tcp:5023
9
10 [rabbitmq]
11 launch: True
12
13=== modified file 'lib/canonical/buildd/debian/changelog'
14--- lib/canonical/buildd/debian/changelog 2011-09-26 06:30:07 +0000
15+++ lib/canonical/buildd/debian/changelog 2011-10-19 12:27:49 +0000
16@@ -1,3 +1,9 @@
17+launchpad-buildd (81) hardy-cat; urgency=low
18+
19+ * generate-translation-templates: switch to Python 2.7.
20+
21+ -- Danilo Šegan <danilo@canonical.com> Mon, 17 Oct 2011 14:46:13 +0200
22+
23 launchpad-buildd (80) hardy-cat; urgency=low
24
25 * binfmt-support demonstrated umount ordering issues for us. LP: #851934
26
27=== modified file 'lib/canonical/buildd/generate-translation-templates'
28--- lib/canonical/buildd/generate-translation-templates 2010-12-21 17:52:32 +0000
29+++ lib/canonical/buildd/generate-translation-templates 2011-10-19 12:27:49 +0000
30@@ -41,7 +41,7 @@
31 BUILDD_PACKAGE=canonical/buildd
32 POTTERY=$BUILDD_PACKAGE/pottery
33 # The script should be smarter about detecting the python version.
34-PYMODULES=/usr/lib/pymodules/python2.6
35+PYMODULES=/usr/lib/pymodules/python2.7
36 echo -n "Default Python in the chroot is: "
37 $BUILD_CHROOT/usr/bin/python --version
38
39@@ -62,4 +62,5 @@
40
41 # Enter chroot, switch back to unprivileged user, execute the generate script.
42 $SUDO $CHROOT $BUILD_CHROOT \
43- $SU - $USER -c "$GENERATE_SCRIPT $BRANCH_URL $RESULT_NAME"
44+ $SU - $USER \
45+ -c "PYTHONPATH=$PYMODULES $GENERATE_SCRIPT $BRANCH_URL $RESULT_NAME"
46
47=== modified file 'lib/lp/poppy/tests/test_poppy.py'
48--- lib/lp/poppy/tests/test_poppy.py 2011-02-24 17:38:19 +0000
49+++ lib/lp/poppy/tests/test_poppy.py 2011-10-19 12:27:49 +0000
50@@ -83,7 +83,7 @@
51 def __init__(self, root_dir, factory):
52 self.root_dir = root_dir
53 self._factory = factory
54- self.port = 5022
55+ self.port = int(config.poppy.port.partition(':')[2])
56
57 def addSSHKey(self, person, public_key_path):
58 f = open(public_key_path, 'r')
59@@ -221,7 +221,7 @@
60
61 if transport is None:
62 transport = self.server.getTransport()
63- transport.stat('foo/bar') # .stat will implicity chdir for us
64+ transport.stat('foo/bar') # .stat will implicity chdir for us
65
66 self.server.disconnect(transport)
67 self.server.waitForClose()
68@@ -400,7 +400,7 @@
69 self.assertRaises(
70 ftplib.error_perm,
71 f.storbinary,
72- 'STOR '+'foo_source.changes',
73+ 'STOR ' + 'foo_source.changes',
74 fake_file)
75
76
77
78=== modified file 'lib/lp/translations/configure.zcml'
79--- lib/lp/translations/configure.zcml 2011-08-29 00:13:22 +0000
80+++ lib/lp/translations/configure.zcml 2011-10-19 12:27:49 +0000
81@@ -659,7 +659,13 @@
82 <!-- TranslationTemplatesBuild -->
83 <class
84 class="lp.translations.model.translationtemplatesbuild.TranslationTemplatesBuild">
85- <allow interface="lp.translations.interfaces.translationtemplatesbuild.ITranslationTemplatesBuild"/>
86+ <require permission="launchpad.View" interface="lp.translations.interfaces.translationtemplatesbuild.ITranslationTemplatesBuild"/>
87+ <!-- This is needed for BuildManager to run. The permission isn't
88+ important; launchpad.Edit isn't actually held by anybody.
89+ Inspired by the similar change for SourcePackageRecipeBuild. -->
90+ <require permission="launchpad.Edit"
91+ set_attributes="builder date_finished date_started log status" />
92+
93 </class>
94 <securedutility
95 component="lp.translations.model.translationtemplatesbuild.TranslationTemplatesBuild"
96
97=== modified file 'lib/lp/translations/model/translationtemplatesbuild.py'
98--- lib/lp/translations/model/translationtemplatesbuild.py 2011-05-04 04:10:58 +0000
99+++ lib/lp/translations/model/translationtemplatesbuild.py 2011-10-19 12:27:49 +0000
100@@ -13,12 +13,10 @@
101 Reference,
102 Storm,
103 )
104-from storm.store import Store
105 from zope.interface import (
106 classProvides,
107 implements,
108 )
109-from zope.security.proxy import ProxyFactory
110
111 from canonical.launchpad.interfaces.lpstorm import IStore
112 from lp.buildmaster.model.buildfarmjob import BuildFarmJobDerived
113@@ -49,6 +47,11 @@
114 branch_id = Int(name='branch', allow_none=False)
115 branch = Reference(branch_id, 'Branch.id')
116
117+ @property
118+ def title(self):
119+ return u'Translation template build for %s' % (
120+ self.branch.displayname)
121+
122 def __init__(self, build_farm_job, branch):
123 super(TranslationTemplatesBuild, self).__init__()
124 self.build_farm_job = build_farm_job
125@@ -110,3 +113,10 @@
126 return store.find(
127 TranslationTemplatesBuild,
128 TranslationTemplatesBuild.branch == branch)
129+
130+ @property
131+ def log_url(self):
132+ """See `IBuildFarmJob`."""
133+ if self.log is None:
134+ return None
135+ return self.log.http_url
136
137=== modified file 'lib/lp/translations/model/translationtemplatesbuildbehavior.py'
138--- lib/lp/translations/model/translationtemplatesbuildbehavior.py 2011-09-07 15:10:38 +0000
139+++ lib/lp/translations/model/translationtemplatesbuildbehavior.py 2011-10-19 12:27:49 +0000
140@@ -11,7 +11,9 @@
141 'TranslationTemplatesBuildBehavior',
142 ]
143
144+import datetime
145 import os
146+import pytz
147 import tempfile
148
149 from twisted.internet import defer
150@@ -20,6 +22,7 @@
151 from zope.security.proxy import removeSecurityProxy
152
153 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
154+from lp.buildmaster.enums import BuildStatus
155 from lp.buildmaster.interfaces.buildfarmjobbehavior import (
156 IBuildFarmJobBehavior,
157 )
158@@ -111,6 +114,35 @@
159 if len(raw_slave_status) >= 4:
160 status['filemap'] = raw_slave_status[3]
161
162+ def setBuildStatus(self, status):
163+ self.build.status = status
164+
165+ @staticmethod
166+ def getLogFromSlave(templates_build, queue_item):
167+ """See `IPackageBuild`."""
168+ SLAVE_LOG_FILENAME = 'buildlog'
169+ builder = queue_item.builder
170+ d = builder.transferSlaveFileToLibrarian(
171+ SLAVE_LOG_FILENAME,
172+ templates_build.buildfarmjob.getLogFileName(),
173+ False)
174+ return d
175+
176+ @staticmethod
177+ def storeBuildInfo(build, queue_item, build_status):
178+ """See `IPackageBuild`."""
179+ def got_log(lfa_id):
180+ build.build.log = lfa_id
181+ build.build.builder = queue_item.builder
182+ build.build.date_started = queue_item.date_started
183+ # XXX cprov 20060615 bug=120584: Currently buildduration includes
184+ # the scanner latency, it should really be asking the slave for
185+ # the duration spent building locally.
186+ build.build.date_finished = datetime.datetime.now(pytz.UTC)
187+
188+ d = build.getLogFromSlave(build, queue_item)
189+ return d.addCallback(got_log)
190+
191 def updateBuild_WAITING(self, queue_item, slave_status, logtail, logger):
192 """Deal with a finished ("WAITING" state, perversely) build job.
193
194@@ -139,6 +171,7 @@
195 # dangerous.
196 if filename is None:
197 logger.error("Build produced no tarball.")
198+ self.setBuildStatus(BuildStatus.FULLYBUILT)
199 return
200
201 tarball_file = open(filename)
202@@ -152,15 +185,23 @@
203 queue_item.specific_job.branch, tarball, logger)
204 logger.debug("Upload complete.")
205 finally:
206+ self.setBuildStatus(BuildStatus.FULLYBUILT)
207 tarball_file.close()
208 os.remove(filename)
209
210- if build_status == 'OK':
211- logger.debug("Processing successful templates build.")
212- filemap = slave_status.get('filemap')
213- d = self._readTarball(queue_item, filemap, logger)
214- d.addCallback(got_tarball)
215- d.addCallback(clean_slave)
216- return d
217-
218- return clean_slave(None)
219+ def build_info_stored(ignored):
220+ if build_status == 'OK':
221+ self.setBuildStatus(BuildStatus.UPLOADING)
222+ logger.debug("Processing successful templates build.")
223+ filemap = slave_status.get('filemap')
224+ d = self._readTarball(queue_item, filemap, logger)
225+ d.addCallback(got_tarball)
226+ d.addCallback(clean_slave)
227+ return d
228+
229+ self.setBuildStatus(BuildStatus.FAILEDTOBUILD)
230+ return clean_slave(None)
231+
232+ d = self.storeBuildInfo(self, queue_item, build_status)
233+ d.addCallback(build_info_stored)
234+ return d
235
236=== modified file 'lib/lp/translations/stories/buildfarm/xx-build-summary.txt'
237--- lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2011-07-01 19:17:03 +0000
238+++ lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2011-10-19 12:27:49 +0000
239@@ -157,5 +157,5 @@
240 Build history for ...
241 1 ... 1 of 1 result
242 ...
243- Translation template build
244+ Translation template build for lp://dev/qblark
245 Build started ... and finished ... taking 5 minutes
246
247=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py'
248--- lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py 2011-05-27 21:12:25 +0000
249+++ lib/lp/translations/tests/test_translationtemplatesbuildbehavior.py 2011-10-19 12:27:49 +0000
250@@ -3,8 +3,10 @@
251
252 """Unit tests for TranslationTemplatesBuildBehavior."""
253
254+import datetime
255 import logging
256 import os
257+import pytz
258
259 from testtools.deferredruntest import AsynchronousDeferredRunTest
260 import transaction
261@@ -17,6 +19,7 @@
262 from canonical.librarian.utils import copy_and_close
263 from canonical.testing.layers import LaunchpadZopelessLayer
264 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
265+from lp.buildmaster.enums import BuildStatus
266 from lp.buildmaster.interfaces.buildfarmjobbehavior import (
267 IBuildFarmJobBehavior,
268 )
269@@ -57,6 +60,7 @@
270 """
271 self.builder = behavior._builder
272 self.specific_job = behavior.buildfarmjob
273+ self.date_started = datetime.datetime.now(pytz.UTC)
274 self.destroySelf = FakeMethod()
275
276
277@@ -161,6 +165,7 @@
278 path = behavior.templates_tarball_path
279 # Poke the file we're expecting into the mock slave.
280 behavior._builder.slave.valid_file_hashes.append(path)
281+
282 def got_tarball(filename):
283 tarball = open(filename, 'r')
284 try:
285@@ -201,6 +206,9 @@
286 queue_item, slave_status, None, logging), slave_call_log
287
288 def build_updated(ignored):
289+ self.assertEqual(BuildStatus.FULLYBUILT, behavior.build.status)
290+ # Log file is stored.
291+ self.assertIsNotNone(behavior.build.log)
292 slave_call_log = behavior._builder.slave.call_log
293 self.assertEqual(1, queue_item.destroySelf.call_count)
294 self.assertIn('clean', slave_call_log)
295@@ -240,6 +248,9 @@
296 queue_item, status_dict, None, logging)
297
298 def build_updated(ignored):
299+ self.assertEqual(BuildStatus.FAILEDTOBUILD, behavior.build.status)
300+ # Log file is stored.
301+ self.assertIsNotNone(behavior.build.log)
302 self.assertEqual(1, queue_item.destroySelf.call_count)
303 slave_call_log = behavior._builder.slave.call_log
304 self.assertIn('clean', slave_call_log)
305@@ -278,6 +289,7 @@
306 queue_item, status_dict, None, logging)
307
308 def build_updated(ignored):
309+ self.assertEqual(BuildStatus.FULLYBUILT, behavior.build.status)
310 self.assertEqual(1, queue_item.destroySelf.call_count)
311 slave_call_log = behavior._builder.slave.call_log
312 self.assertIn('clean', slave_call_log)
313@@ -321,6 +333,7 @@
314 queue_item, slave_status, None, logging)
315
316 def build_updated(ignored):
317+ self.assertEqual(BuildStatus.FULLYBUILT, behavior.build.status)
318 entries = getUtility(
319 ITranslationImportQueue).getAllEntries(target=productseries)
320 expected_templates = [
321
322=== modified file 'utilities/make-lp-user'
323--- utilities/make-lp-user 2011-07-11 03:49:12 +0000
324+++ utilities/make-lp-user 2011-10-19 12:27:49 +0000
325@@ -44,6 +44,7 @@
326
327 from canonical.launchpad.interfaces.gpghandler import IGPGHandler
328 from canonical.launchpad.scripts import execute_zcml_for_scripts
329+from canonical.lazr.timeout import set_default_timeout_function
330 from lp.registry.interfaces.gpg import GPGKeyAlgorithm, IGPGKeySet
331 from lp.registry.interfaces.person import IPersonSet
332 from lp.registry.interfaces.ssh import ISSHKeySet
333@@ -57,6 +58,7 @@
334 DEFAULT_PASSWORD = 'test'
335 factory = LaunchpadObjectFactory()
336
337+set_default_timeout_function(lambda: 100)
338
339 def make_person(username, email):
340 """Create and return a person with the given username.
341@@ -119,7 +121,7 @@
342 print 'Registered SSH key: %s' % (filename,)
343 break
344 else:
345- print 'No SSH key files found in %s' % ssh_dir
346+ print 'No SSH key files found in %s' % ssh_dir
347
348
349 def parse_fingerprints(gpg_output):