Merge lp:~cjwatson/launchpad-buildd/livefs into lp:launchpad-buildd

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 101
Merged at revision: 98
Proposed branch: lp:~cjwatson/launchpad-buildd/livefs
Merge into: lp:launchpad-buildd
Diff against target: 504 lines (+415/-4)
9 files modified
Makefile (+3/-2)
buildd-slave.tac (+2/-0)
buildlivefs (+167/-0)
debian/changelog (+2/-0)
debian/rules (+1/-1)
debian/upgrade-config (+14/-1)
lpbuildd/livefs.py (+105/-0)
lpbuildd/tests/test_livefs.py (+118/-0)
template-buildd-slave.conf (+3/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad-buildd/livefs
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+193682@code.launchpad.net

Commit message

Add a new "livefs" build manager, based on livecd-rootfs/BuildLiveCD.

Description of the change

This is a first cut at the build slave side of livefs building. It's somewhat based on BuildLiveCD from livecd-rootfs, but refactored into launchpad-buildd style and with a good deal of historical cruft stripped out. I've successfully tested this with an ubuntu-core/saucy/i386 build.

livecd-rootfs notionally has eatmydata support, but I don't think I ever got that deployed and on closer inspection I'm not clear that it ever worked in livecd-rootfs (bad syntax of environment variable handling; libeatmydata.so doesn't exist in the chroot). I've omitted this for the moment, and perhaps we can restore it later.

Note that if you're testing this in an LXC container then you need to use "aa-complain /usr/bin/lxc-start", perhaps due to bug 969299 or at any rate something with similar symptoms (debootstrap fails to unpack udev due to something related to dpkg-divert). This won't be a problem for production deployment.

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

One thing I know needs to be fixed: NOW can't be calculated entirely by the slave because the slave won't know whether there've been previous builds on the same day. This needs to be passed down from the manager.

lp:~cjwatson/launchpad-buildd/livefs updated
98. By Colin Watson

Prepend linux32 to chroot commands on powerpc (from BuildLiveCD).

99. By Colin Watson

Pass datestamp as an option rather than unreliably computing it in buildlivefs.

100. By Colin Watson

Fail the builder if buildlivefs returns an unrecognised exit code.

101. By Colin Watson

Sort tests.

Revision history for this message
Colin Watson (cjwatson) wrote :

I think this is actually ready now.

Revision history for this message
William Grant (wgrant) wrote :

127 + def build(self):
128 + if self.options.locale is not None:

The locale and non-locale cases seem to be almost entirely divergent. Do we really need both?

365 + def gatherResults(self):
366 + """Gather the results of the build and add them to the file cache."""
367 + for entry in sorted(os.listdir(self.build_path)):
368 + if entry.startswith("livecd."):
369 + self._slave.addWaitingFile(
370 + os.path.join(self.build_path, entry))

There's no manifest-like file from which we can parse the build result filenames?

review: Approve (code)
Revision history for this message
Colin Watson (cjwatson) wrote :

On Tue, Nov 26, 2013 at 02:44:16AM -0000, William Grant wrote:
> 127 + def build(self):
> 128 + if self.options.locale is not None:
>
> The locale and non-locale cases seem to be almost entirely divergent. Do we really need both?

For the time being, yes, because the Chinese Edition was still built by
Canonical in precise. That's been superseded by UbuntuKylin now, so
we'll probably be able to get rid of this once we no longer care about
building images for precise (assuming nobody wants us to do a similar
thing again), but not quite yet.

> 365 + def gatherResults(self):
> 366 + """Gather the results of the build and add them to the file cache."""
> 367 + for entry in sorted(os.listdir(self.build_path)):
> 368 + if entry.startswith("livecd."):
> 369 + self._slave.addWaitingFile(
> 370 + os.path.join(self.build_path, entry))
>
> There's no manifest-like file from which we can parse the build result filenames?

Not right now. Do you think that's a problem? If so I'll need to do
some work in livecd-rootfs to set one up.

Perhaps it would be sufficient to just make doubly sure that the build
directory is empty before we start a build? At the moment it looks like
the old system (livecd-rootfs/BuildLiveCD) relies on the previous build
having cleaned things out, and come to think of it I'm not too sure that
I even preserved that in this launchpad-buildd version, so I'll need to
do something there.

Revision history for this message
Colin Watson (cjwatson) wrote :

Oh, actually, I clean out the build directory at the start of each build:

    def initiate(self, files, chroot, extra_args):
        """Initiate a build with a given set of files and chroot."""
        self.build_path = get_build_path(
            self.home, self._buildid, "chroot-autobuild", "build")
        if os.path.isdir(self.build_path):
            shutil.rmtree(self.build_path)

So this should be fine.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2013-10-03 10:39:23 +0000
3+++ Makefile 2013-11-04 11:22:27 +0000
4@@ -31,5 +31,6 @@
5 lpbuildd.tests.test_buildd_slave \
6 lpbuildd.tests.test_check_implicit_pointer_functions \
7 lpbuildd.tests.test_harness \
8- lpbuildd.tests.test_translationtemplatesbuildmanager \
9- lpbuildd.tests.test_sourcepackagerecipe
10+ lpbuildd.tests.test_livefs \
11+ lpbuildd.tests.test_sourcepackagerecipe \
12+ lpbuildd.tests.test_translationtemplatesbuildmanager
13
14=== modified file 'buildd-slave.tac'
15--- buildd-slave.tac 2011-11-09 08:50:17 +0000
16+++ buildd-slave.tac 2013-11-04 11:22:27 +0000
17@@ -8,6 +8,7 @@
18 from twisted.application import service, strports
19 from lpbuildd.slave import XMLRPCBuildDSlave
20 from lpbuildd.binarypackage import BinaryPackageBuildManager
21+from lpbuildd.livefs import LiveFilesystemBuildManager
22 from lpbuildd.sourcepackagerecipe import (
23 SourcePackageRecipeBuildManager)
24 from lpbuildd.translationtemplates import (
25@@ -30,6 +31,7 @@
26 slave.registerBuilder(SourcePackageRecipeBuildManager, "sourcepackagerecipe")
27 slave.registerBuilder(
28 TranslationTemplatesBuildManager, 'translation-templates')
29+slave.registerBuilder(LiveFilesystemBuildManager, "livefs")
30
31 application = service.Application('BuildDSlave')
32 builddslaveService = service.IServiceCollection(application)
33
34=== added file 'buildlivefs'
35--- buildlivefs 1970-01-01 00:00:00 +0000
36+++ buildlivefs 2013-11-04 11:22:27 +0000
37@@ -0,0 +1,167 @@
38+#! /usr/bin/python -u
39+# Copyright 2013 Canonical Ltd. This software is licensed under the
40+# GNU Affero General Public License version 3 (see the file LICENSE).
41+
42+"""A script that builds a live file system."""
43+
44+__metaclass__ = type
45+
46+from optparse import OptionParser
47+import os
48+import re
49+import subprocess
50+import sys
51+import traceback
52+
53+
54+RETCODE_SUCCESS = 0
55+RETCODE_FAILURE_INSTALL = 200
56+RETCODE_FAILURE_BUILD = 201
57+
58+
59+def get_build_path(build_id, *extra):
60+ """Generate a path within the build directory.
61+
62+ :param build_id: the build id to use.
63+ :param extra: the extra path segments within the build directory.
64+ :return: the generated path.
65+ """
66+ return os.path.join(os.environ["HOME"], "build-" + build_id, *extra)
67+
68+
69+non_meta_re = re.compile(r'^[a-zA-Z0-9+,./:=@_-]+$')
70+
71+def shell_escape(arg):
72+ if non_meta_re.match(arg):
73+ return arg
74+ else:
75+ return "'%s'" % arg.replace("'", "'\\''")
76+
77+
78+class LiveFSBuilder:
79+ """Builds a live file system."""
80+
81+ def __init__(self, options):
82+ self.options = options
83+ self.chroot_path = get_build_path(
84+ self.options.build_id, 'chroot-autobuild')
85+
86+ def chroot(self, args, echo=False):
87+ """Run a command in the chroot.
88+
89+ :param args: the command and arguments to run.
90+ """
91+ if self.options.arch == "powerpc":
92+ args = ["linux32"] + args
93+ if echo:
94+ print "Running in chroot: %s" % ' '.join(
95+ "'%s'" % arg for arg in args)
96+ sys.stdout.flush()
97+ subprocess.check_call([
98+ "/usr/bin/sudo", "/usr/sbin/chroot", self.chroot_path] + args)
99+
100+ def run_build_command(self, args, env=None, echo=False):
101+ """Run a build command in the chroot.
102+
103+ This is unpleasant because we need to run it in /build under sudo
104+ chroot, and there's no way to do this without either a helper
105+ program in the chroot or unpleasant quoting. We go for the
106+ unpleasant quoting.
107+
108+ :param args: the command and arguments to run.
109+ :param env: dictionary of additional environment variables to set.
110+ """
111+ args = [shell_escape(arg) for arg in args]
112+ if env:
113+ args = ["env"] + [
114+ "%s=%s" % (key, shell_escape(value))
115+ for key, value in env.items()] + args
116+ command = "cd /build && %s" % " ".join(args)
117+ self.chroot(["/bin/sh", "-c", command], echo=echo)
118+
119+ def install(self):
120+ self.chroot(["apt-get", "-y", "install", "livecd-rootfs"])
121+ if self.options.locale is not None:
122+ self.chroot([
123+ "apt-get", "-y", "--install-recommends", "install",
124+ "ubuntu-defaults-builder",
125+ ])
126+
127+ def build(self):
128+ if self.options.locale is not None:
129+ self.run_build_command([
130+ "ubuntu-defaults-image",
131+ "--locale", self.options.locale,
132+ "--arch", self.options.arch,
133+ "--release", self.options.suite,
134+ ])
135+ else:
136+ self.run_build_command(["rm", "-rf", "auto"])
137+ self.run_build_command(["mkdir", "-p", "auto"])
138+ for lb_script in ("config", "build", "clean"):
139+ lb_script_path = os.path.join(
140+ "/usr/share/livecd-rootfs/live-build/auto", lb_script)
141+ self.run_build_command(["ln", "-s", lb_script_path, "auto/"])
142+ self.run_build_command(["lb", "clean", "--purge"])
143+
144+ base_lb_env = {
145+ "PROJECT": self.options.project,
146+ "ARCH": self.options.arch,
147+ }
148+ if self.options.subproject is not None:
149+ base_lb_env["SUBPROJECT"] = self.options.subproject
150+ if self.options.subarch is not None:
151+ base_lb_env["SUBARCH"] = self.options.subarch
152+ lb_env = dict(base_lb_env)
153+ lb_env["SUITE"] = self.options.suite
154+ if self.options.datestamp is not None:
155+ lb_env["NOW"] = self.options.datestamp
156+ if self.options.image_format is not None:
157+ lb_env["IMAGEFORMAT"] = self.options.image_format
158+ if self.options.proposed:
159+ lb_env["PROPOSED"] = "1"
160+ self.run_build_command(["lb", "config"], env=lb_env)
161+ self.run_build_command(["lb", "build"], env=base_lb_env)
162+
163+
164+def main():
165+ parser = OptionParser()
166+ parser.add_option("--build-id", help="build identifier")
167+ parser.add_option(
168+ "--arch", metavar="ARCH", help="build for architecture ARCH")
169+ parser.add_option(
170+ "--subarch", metavar="SUBARCH",
171+ help="build for subarchitecture SUBARCH")
172+ parser.add_option(
173+ "--project", metavar="PROJECT", help="build for project PROJECT")
174+ parser.add_option(
175+ "--subproject", metavar="SUBPROJECT",
176+ help="build for subproject SUBPROJECT")
177+ parser.add_option("--suite", metavar="SUITE", help="build for suite SUITE")
178+ parser.add_option("--datestamp", help="date stamp")
179+ parser.add_option(
180+ "--image-format", metavar="FORMAT", help="produce an image in FORMAT")
181+ parser.add_option(
182+ "--proposed", default=False, action="store_true",
183+ help="enable use of -proposed pocket")
184+ parser.add_option(
185+ "--locale", metavar="LOCALE",
186+ help="use ubuntu-defaults-image to build an image for LOCALE")
187+ options, _ = parser.parse_args()
188+
189+ builder = LiveFSBuilder(options)
190+ try:
191+ builder.install()
192+ except Exception:
193+ traceback.print_exc()
194+ return RETCODE_FAILURE_INSTALL
195+ try:
196+ builder.build()
197+ except Exception:
198+ traceback.print_exc()
199+ return RETCODE_FAILURE_BUILD
200+ return RETCODE_SUCCESS
201+
202+
203+if __name__ == "__main__":
204+ sys.exit(main())
205
206=== modified file 'debian/changelog'
207--- debian/changelog 2013-10-18 13:08:29 +0000
208+++ debian/changelog 2013-11-04 11:22:27 +0000
209@@ -4,6 +4,8 @@
210 of other environment variables too (including DISPLAY and TERM), in line
211 with Debian buildds.
212 * Make the status XML-RPC method a synonym for status_dict.
213+ * Add a new "livefs" build manager, based on livecd-rootfs/BuildLiveCD
214+ (LP: #1247461).
215
216 -- Colin Watson <cjwatson@ubuntu.com> Fri, 18 Oct 2013 12:24:00 +0100
217
218
219=== modified file 'debian/rules'
220--- debian/rules 2013-10-03 10:57:53 +0000
221+++ debian/rules 2013-11-04 11:22:27 +0000
222@@ -18,7 +18,7 @@
223
224 slavebins = unpack-chroot mount-chroot update-debian-chroot sbuild-package \
225 scan-for-processes umount-chroot remove-build override-sources-list \
226- buildrecipe generate-translation-templates slave-prep
227+ buildrecipe generate-translation-templates slave-prep buildlivefs
228
229 BUILDDUID=65500
230 BUILDDGID=65500
231
232=== modified file 'debian/upgrade-config'
233--- debian/upgrade-config 2013-07-29 17:37:43 +0000
234+++ debian/upgrade-config 2013-11-04 11:22:27 +0000
235@@ -130,6 +130,18 @@
236 in_file.close()
237 out_file.close()
238
239+def upgrade_to_120():
240+ print "Upgrading %s to version 120" % conf_file
241+ subprocess.call(["mv", conf_file, conf_file+"-prev115~"])
242+ in_file = open(conf_file+"-prev120~", "r")
243+ out_file = open(conf_file, "w")
244+ out_file.write(in_file.read())
245+ out_file.write(
246+ "\n[livefilesystemmanager]\n"
247+ "buildlivefspath = /usr/share/launchpad-buildd/slavebin/buildlivefs\n")
248+ in_file.close()
249+ out_file.close()
250+
251 if __name__ == "__main__":
252 old_version = re.sub(r'[~-].*', '', old_version)
253 if int(old_version) < 12:
254@@ -150,4 +162,5 @@
255 upgrade_to_110()
256 if int(old_version) < 115:
257 upgrade_to_115()
258-
259+ if int(old_version) < 120:
260+ upgrade_to_120()
261
262=== added file 'lpbuildd/livefs.py'
263--- lpbuildd/livefs.py 1970-01-01 00:00:00 +0000
264+++ lpbuildd/livefs.py 2013-11-04 11:22:27 +0000
265@@ -0,0 +1,105 @@
266+# Copyright 2013 Canonical Ltd. This software is licensed under the
267+# GNU Affero General Public License version 3 (see the file LICENSE).
268+
269+__metaclass__ = type
270+
271+import os
272+import shutil
273+
274+from lpbuildd.debian import (
275+ DebianBuildManager,
276+ DebianBuildState,
277+ get_build_path,
278+ )
279+
280+
281+RETCODE_SUCCESS = 0
282+RETCODE_FAILURE_INSTALL = 200
283+RETCODE_FAILURE_BUILD = 201
284+
285+
286+class LiveFilesystemBuildState(DebianBuildState):
287+ BUILD_LIVEFS = "BUILD_LIVEFS"
288+
289+
290+class LiveFilesystemBuildManager(DebianBuildManager):
291+ """Build a live filesystem."""
292+
293+ initial_build_state = LiveFilesystemBuildState.BUILD_LIVEFS
294+
295+ def __init__(self, slave, buildid, **kwargs):
296+ DebianBuildManager.__init__(self, slave, buildid, **kwargs)
297+ self.build_livefs_path = slave._config.get(
298+ "livefilesystemmanager", "buildlivefspath")
299+
300+ def initiate(self, files, chroot, extra_args):
301+ """Initiate a build with a given set of files and chroot."""
302+ self.build_path = get_build_path(
303+ self.home, self._buildid, "chroot-autobuild", "build")
304+ if os.path.isdir(self.build_path):
305+ shutil.rmtree(self.build_path)
306+
307+ self.subarch = extra_args.get("subarch")
308+ self.project = extra_args["project"]
309+ self.subproject = extra_args.get("subproject")
310+ self.suite = extra_args["suite"]
311+ self.datestamp = extra_args.get("datestamp")
312+ self.image_format = extra_args.get("image_format")
313+ self.proposed = extra_args.get("proposed", False)
314+ self.locale = extra_args.get("locale")
315+
316+ super(LiveFilesystemBuildManager, self).initiate(
317+ files, chroot, extra_args)
318+
319+ def doRunBuild(self):
320+ """Run the process to build the live filesystem."""
321+ args = [
322+ "buildlivefs",
323+ "--build-id", self._buildid,
324+ "--arch", self.arch_tag,
325+ ]
326+ if self.subarch:
327+ args.extend(["--subarch", self.subarch])
328+ args.extend(["--project", self.project])
329+ if self.subproject:
330+ args.extend(["--subproject", self.subproject])
331+ args.extend(["--suite", self.suite])
332+ if self.datestamp:
333+ args.extend(["--datestamp", self.datestamp])
334+ if self.image_format:
335+ args.extend(["--image-format", self.image_format])
336+ if self.proposed:
337+ args.append("--proposed")
338+ if self.locale:
339+ args.extend(["--locale", self.locale])
340+ self.runSubProcess(self.build_livefs_path, args)
341+
342+ def iterate_BUILD_LIVEFS(self, retcode):
343+ """Finished building the live filesystem."""
344+ if retcode == RETCODE_SUCCESS:
345+ self.gatherResults()
346+ print("Returning build status: OK")
347+ elif (retcode >= RETCODE_FAILURE_INSTALL and
348+ retcode <= RETCODE_FAILURE_BUILD):
349+ if not self.alreadyfailed:
350+ self._slave.buildFail()
351+ print("Returning build status: Build failed.")
352+ self.alreadyfailed = True
353+ else:
354+ if not self.alreadyfailed:
355+ self._slave.builderFail()
356+ print("Returning build status: Builder failed.")
357+ self.alreadyfailed = True
358+ self.doReapProcesses(self._state)
359+
360+ def iterateReap_BUILD_LIVEFS(self, retcode):
361+ """Finished reaping after building the live filesystem."""
362+ self._state = DebianBuildState.UMOUNT
363+ self.doUnmounting()
364+
365+ def gatherResults(self):
366+ """Gather the results of the build and add them to the file cache."""
367+ for entry in sorted(os.listdir(self.build_path)):
368+ if entry.startswith("livecd."):
369+ self._slave.addWaitingFile(
370+ os.path.join(self.build_path, entry))
371
372=== added file 'lpbuildd/tests/test_livefs.py'
373--- lpbuildd/tests/test_livefs.py 1970-01-01 00:00:00 +0000
374+++ lpbuildd/tests/test_livefs.py 2013-11-04 11:22:27 +0000
375@@ -0,0 +1,118 @@
376+# Copyright 2013 Canonical Ltd. This software is licensed under the
377+# GNU Affero General Public License version 3 (see the file LICENSE).
378+
379+__metaclass__ = type
380+
381+import os
382+import shutil
383+import tempfile
384+
385+from testtools import TestCase
386+
387+from lpbuildd.livefs import (
388+ LiveFilesystemBuildManager,
389+ LiveFilesystemBuildState,
390+ )
391+from lpbuildd.tests.fakeslave import FakeSlave
392+
393+
394+class MockBuildManager(LiveFilesystemBuildManager):
395+ def __init__(self, *args, **kwargs):
396+ super(MockBuildManager, self).__init__(*args, **kwargs)
397+ self.commands = []
398+ self.iterators = []
399+
400+ def runSubProcess(self, path, command, iterate=None):
401+ self.commands.append([path] + command)
402+ if iterate is None:
403+ iterate = self.iterate
404+ self.iterators.append(iterate)
405+ return 0
406+
407+
408+class TestLiveFilesystemBuildManagerIteration(TestCase):
409+ """Run LiveFilesystemBuildManager through its iteration steps."""
410+ def setUp(self):
411+ super(TestLiveFilesystemBuildManagerIteration, self).setUp()
412+ self.working_dir = tempfile.mkdtemp()
413+ self.addCleanup(lambda: shutil.rmtree(self.working_dir))
414+ slave_dir = os.path.join(self.working_dir, "slave")
415+ home_dir = os.path.join(self.working_dir, "home")
416+ for dir in (slave_dir, home_dir):
417+ os.mkdir(dir)
418+ self.slave = FakeSlave(slave_dir)
419+ self.buildid = "123"
420+ self.buildmanager = MockBuildManager(self.slave, self.buildid)
421+ self.buildmanager.home = home_dir
422+ self.buildmanager._cachepath = self.slave._cachepath
423+ self.build_dir = os.path.join(
424+ home_dir, "build-%s" % self.buildid, "chroot-autobuild", "build")
425+
426+ def getState(self):
427+ """Retrieve build manager's state."""
428+ return self.buildmanager._state
429+
430+ def startBuild(self):
431+ # The build manager's iterate() kicks off the consecutive states
432+ # after INIT.
433+ extra_args = {
434+ "project": "ubuntu",
435+ "suite": "saucy",
436+ "arch_tag": "i386",
437+ }
438+ self.buildmanager.initiate({}, "chroot.tar.gz", extra_args)
439+
440+ # Skip states that are done in DebianBuildManager to the state
441+ # directly before BUILD_LIVEFS.
442+ self.buildmanager._state = LiveFilesystemBuildState.UPDATE
443+
444+ # BUILD_LIVEFS: Run the slave's payload to build the live filesystem.
445+ self.buildmanager.iterate(0)
446+ self.assertEqual(
447+ LiveFilesystemBuildState.BUILD_LIVEFS, self.getState())
448+ expected_command = [
449+ "buildlivefspath", "buildlivefs", "--build-id", self.buildid,
450+ "--arch", "i386", "--project", "ubuntu", "--suite", "saucy",
451+ ]
452+ self.assertEqual(expected_command, self.buildmanager.commands[-1])
453+ self.assertEqual(
454+ self.buildmanager.iterate, self.buildmanager.iterators[-1])
455+ self.assertFalse(self.slave.wasCalled("chrootFail"))
456+
457+ def test_iterate(self):
458+ # The build manager iterates a normal build from start to finish.
459+ self.startBuild()
460+
461+ log_path = os.path.join(self.buildmanager._cachepath, "buildlog")
462+ log = open(log_path, "w")
463+ log.write("I am a build log.")
464+ log.close()
465+
466+ os.makedirs(self.build_dir)
467+ manifest_path = os.path.join(self.build_dir, "livecd.ubuntu.manifest")
468+ manifest = open(manifest_path, "w")
469+ manifest.write("I am a manifest file.")
470+ manifest.close()
471+
472+ # After building the package, reap processes.
473+ self.buildmanager.iterate(0)
474+ expected_command = [
475+ "processscanpath", "scan-for-processes", self.buildid,
476+ ]
477+ self.assertEqual(
478+ LiveFilesystemBuildState.BUILD_LIVEFS, self.getState())
479+ self.assertEqual(expected_command, self.buildmanager.commands[-1])
480+ self.assertNotEqual(
481+ self.buildmanager.iterate, self.buildmanager.iterators[-1])
482+ self.assertFalse(self.slave.wasCalled("buildFail"))
483+ self.assertEqual(
484+ [((manifest_path,), {})], self.slave.addWaitingFile.calls)
485+
486+ # Control returns to the DebianBuildManager in the UMOUNT state.
487+ self.buildmanager.iterateReap(self.getState(), 0)
488+ expected_command = ["umountpath", "umount-chroot", self.buildid]
489+ self.assertEqual(LiveFilesystemBuildState.UMOUNT, self.getState())
490+ self.assertEqual(expected_command, self.buildmanager.commands[-1])
491+ self.assertEqual(
492+ self.buildmanager.iterate, self.buildmanager.iterators[-1])
493+ self.assertFalse(self.slave.wasCalled("buildFail"))
494
495=== modified file 'template-buildd-slave.conf'
496--- template-buildd-slave.conf 2013-07-24 10:42:05 +0000
497+++ template-buildd-slave.conf 2013-11-04 11:22:27 +0000
498@@ -31,3 +31,6 @@
499 [translationtemplatesmanager]
500 generatepath = /usr/share/launchpad-buildd/slavebin/generate-translation-templates
501 resultarchive = translation-templates.tar.gz
502+
503+[livefilesystemmanager]
504+buildlivefspath = /usr/share/launchpad-buildd/slavebin/buildlivefs

Subscribers

People subscribed via source and target branches

to all changes: