Merge lp:~racb/ubuntu/saucy/autopkgtest/lxc into lp:ubuntu/saucy/autopkgtest

Proposed by Robie Basak
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~racb/ubuntu/saucy/autopkgtest/lxc
Merge into: lp:ubuntu/saucy/autopkgtest
Diff against target: 362 lines (+342/-0)
3 files modified
Makefile (+1/-0)
virt-subproc/adt-virt-lxc (+217/-0)
virt-subproc/adt-virt-lxc.1 (+124/-0)
To merge this branch: bzr merge lp:~racb/ubuntu/saucy/autopkgtest/lxc
Reviewer Review Type Date Requested Status
Martin Pitt Approve
Jean-Baptiste Lallement (community) Needs Fixing
Ubuntu branches Pending
Review via email: mp+172856@code.launchpad.net

Commit message

Add LXC support: adt-virt-lxc

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

Hey Robie, many thanks for working on this! That's a nice addition.

Some things which I noticed during first spot-check:

+# autopkgtest is Copyright (C) 2006-2007 Canonical Ltd.

Please add a copyright for you, you wrote that file after all.

127 + lxc_container_name = 'adt-virt-lxc-' + ''.join([random.choice(string.ascii_lowercase) for i in range(6)])
128 + vsp.execute('sudo lxc-start-ephemeral -n %s --keep-data -d -o' % lxc_container_name,[lxc_template], downp=False, outp=True)

I'm not entirely sure what that does, but it looks like lxc_container_name is a temporary file name of some sort? Would it be more adequate to use the standard mkstemp() function, or is that really just a name? In the latter case, how does this make sure that it doesn't use an existing name?

Revision history for this message
James Hunt (jamesodhunt) wrote :

Great! This might go some way towards providing the facility outlined on bug 1158391.

Revision history for this message
Jean-Baptiste Lallement (jibel) wrote :

Thanks for your work!
I tested the following scenarios:
- i386 / amd64
- test package from dsc and source tree
- unbuilt tree and binary package
- clone mode, as well as options ephemeral and eatmydata

Overall it works fine but:
1. If the base container is not created from an ubuntu-cloud template (i.e debootstrap based), the driver goes to an infinite wait loop. It should fail gracefully in that case
2. The requirement for a cloud template means that the usage of this driver is limited to Ubuntu. Using any type of lxc container would allow to run tests in LXC on Debian for example.
3. This requirement is not documented
4. More generally there is no man page for this driver while there are for the other drivers
5. If eatmydata is not installed into the container and the driver is invoked with option --eatmydata then it crashes with
    lxc-attach: No such file or directory - failed to exec 'eatmydata'
    <VirtSubproc>: failure: (down) mkdir failed (exit status 255)
  * It should either:
    - Fail gracefully,
    - or install eatmydata if not installed (but an implicit installation might be confusing and affect the test)
    - or run without eatmydata (and display a warning)

I think at least 1, 3 and 5 should be fixed

review: Needs Fixing
Revision history for this message
Iain Lane (laney) wrote :

→ WIP based on the last comments. Please reset to Needs Review once addressed.

Revision history for this message
Robie Basak (racb) wrote :

Thanks for the review, Jean-Baptiste. I've addressed 1, 3, 4 and 5. This driver could in theory work in Debian with Debian's cloud-init. The only real issue is that there's no standard way to detect if an LXC container has finished booting, so I used cloud-init's boot-finished sentinel file.

Specifically:

> 1. If the base container is not created from an ubuntu-cloud template (i.e debootstrap based), the driver goes to an infinite wait loop. It should fail gracefully in that case

Now explicitly checked and fails with a specific error message for this case.

> 2. The requirement for a cloud template means that the usage of this driver is limited to Ubuntu. Using any type of lxc container would allow to run tests in LXC on Debian for example.

As above. Anything with cloud-init should work, and if we can have some generic lxc-wait type mechanism for boot finished detection, we could expand support in the future.

> 3. This requirement is not documented
> 4. More generally there is no man page for this driver while there are for the other drivers

Man page written.

> 5. If eatmydata is not installed into the container and the driver is invoked with option --eatmydata then it crashes with...

I now explicitly check that the container has eatmydata if --eatmydata is specified, and fail with a specific error in that case. I thought that this would be better than printing a warning, as chances are that the developer won't see it (and the developer can trivially work around the problem by not using the --eatmydata option). Like you, I felt that an implicit installation might be confusing.

Revision history for this message
Martin Pitt (pitti) wrote :

Thanks for the updates!

I adjusted the code to be PEP8 compliant and fixed the pyflakes errors, and pushed to the Debian git:

http://anonscm.debian.org/gitweb/?p=autopkgtest/autopkgtest.git;a=commitdiff;h=617e6f48

I also created a set of test cases:

http://anonscm.debian.org/gitweb/?p=autopkgtest/autopkgtest.git;a=commitdiff;h=3fd0972a

I'm closing this MP now. It's not formally merged into the ubuntu branch, but we'll sync it in the t-series.

Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2011-07-30 17:57:27 +0000
3+++ Makefile 2013-10-16 16:21:23 +0000
4@@ -26,6 +26,7 @@
5 virt-subproc/adt-virt-xenlvm \
6 virt-subproc/adt-virt-null \
7 virt-subproc/adt-virt-schroot \
8+ virt-subproc/adt-virt-lxc \
9 runner/adt-run \
10 runner/adt-testreport-onepackage \
11 runner/adt-testreport-runloop \
12
13=== added file 'virt-subproc/adt-virt-lxc'
14--- virt-subproc/adt-virt-lxc 1970-01-01 00:00:00 +0000
15+++ virt-subproc/adt-virt-lxc 2013-10-16 16:21:23 +0000
16@@ -0,0 +1,217 @@
17+#!/usr/bin/python
18+#
19+# adt-virt-lxc is part of autopkgtest
20+# autopkgtest is a tool for testing Debian binary packages
21+#
22+# autopkgtest is Copyright (C) 2006-2007, 2013 Canonical Ltd.
23+#
24+# adt-virt-lxc was derived from adt-virt-schroot and modified to suit LXC by
25+# Robie Basak <robie.basak@canonical.com>.
26+#
27+# This program is free software; you can redistribute it and/or modify
28+# it under the terms of the GNU General Public License as published by
29+# the Free Software Foundation; either version 2 of the License, or
30+# (at your option) any later version.
31+#
32+# This program is distributed in the hope that it will be useful,
33+# but WITHOUT ANY WARRANTY; without even the implied warranty of
34+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35+# GNU General Public License for more details.
36+#
37+# You should have received a copy of the GNU General Public License
38+# along with this program; if not, write to the Free Software
39+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
40+#
41+# See the file CREDITS for a full list of credits information (often
42+# installed as /usr/share/doc/autopkgtest/CREDITS).
43+
44+import sys
45+import os
46+import string
47+import re as regexp
48+import grp
49+import pwd
50+from optparse import OptionParser
51+import random
52+import subprocess
53+import time
54+
55+LXC_WAIT_RETRY_TIME = 0.2
56+
57+try: our_base = os.environ['AUTOPKGTEST_BASE']+'/lib'
58+except KeyError: our_base = '/usr/share/autopkgtest/python';
59+sys.path.insert(1, our_base)
60+
61+import VirtSubproc as vsp
62+
63+def pw_uid(exp_name):
64+ try:
65+ return pwd.getpwnam(exp_name).pw_uid
66+ except KeyError:
67+ return None
68+
69+def gr_gid(exp_name):
70+ try:
71+ return grp.getgrnam(exp_name).gr_gid
72+ except KeyError:
73+ return None
74+
75+def match(exp_names, ids, extract_id):
76+ for exp_name in [n for n in exp_names.split(',') if n]:
77+ if extract_id(exp_name) in ids:
78+ return True
79+ return False
80+
81+def parse_args():
82+ global lxc_template, debuglevel, eatmydata, ephemeral
83+
84+ usage = "%prog [<options>] <template>"
85+ parser = OptionParser(usage=usage)
86+ pa = parser.add_option
87+ pe = parser.error
88+
89+ pa('-d', '--debug', action='store_true', dest='debug');
90+ pa('--eatmydata', action='store_true')
91+ pa('--ephemeral', action='store_true')
92+
93+ (opts,args) = parser.parse_args()
94+ if len(args) != 1: pe("need exactly one arg, template lxc container name")
95+
96+ lxc_template = args[0]
97+ eatmydata = opts.eatmydata
98+ ephemeral = opts.ephemeral
99+
100+ vsp.debuglevel = opts.debug
101+
102+def check_for_cloud_init(container, template_name):
103+ with open('/dev/null', 'wb') as null:
104+ result = subprocess.call(
105+ ['sudo', 'lxc-attach', '-n', container, '--', 'dpkg-query', '-W',
106+ 'cloud-init'],
107+ close_fds=True, stdout=null, stderr=null)
108+ if result:
109+ raise RuntimeError(
110+ ("Container %s does not use cloud-init, and this driver " +
111+ "requires it. Use eg. " +
112+ "\"lxc-create -t ubuntu-cloud ...\" to create your " +
113+ "container instead.")
114+ % repr(template_name)
115+ )
116+
117+def check_for_eatmydata(container, template_name):
118+ with open('/dev/null', 'wb') as null:
119+ result = subprocess.call(
120+ ['sudo', 'lxc-attach', '-n', container, '--', 'sh', '-c',
121+ 'command -v eatmydata'],
122+ close_fds=True, stdout=null, stderr=null)
123+ if result:
124+ raise RuntimeError(
125+ ("Container %s does not contain eatmydata, which is required "
126+ "for the --eatmydata option."
127+ % repr(template_name))
128+ )
129+
130+def full_path_split(path):
131+ head, tail = os.path.split(os.path.normpath(path))
132+ if head in ['', os.path.sep, 2 * os.path.sep]:
133+ return [head, tail]
134+ else:
135+ return full_path_split(head) + [tail]
136+
137+def wait_for_lexists(path):
138+ vsp.debug('waiting for %s to exist' % repr(path))
139+ while not os.path.lexists(path):
140+ time.sleep(LXC_WAIT_RETRY_TIME)
141+
142+def wait_for_lxc_guest_start(lxc_name, template_name):
143+ check_for_cloud_init(lxc_name, template_name)
144+ for subdir in ['delta0', 'rootfs']:
145+ host_guest_root = '/var/lib/lxc/%s/%s' % (lxc_name, subdir)
146+ if os.path.lexists(host_guest_root):
147+ break
148+ else:
149+ raise RuntimeError(
150+ "Failed to find host root directory for container %s." %
151+ repr(lxc_name)
152+ )
153+ host_instance_path = os.path.join(
154+ host_guest_root, 'var/lib/cloud/instance')
155+ wait_for_lexists(host_instance_path)
156+ guest_instance_link = os.readlink(host_instance_path)
157+ # Due to overlayfs, we can sometimes read the link when it doesn't
158+ # officially exist in the guest. Keep waiting in this case. The
159+ # "(overlay-whiteout)" string is defined by the overlayfs module.
160+ while guest_instance_link == '(overlay-whiteout)': # do not translate
161+ time.sleep(LXC_WAIT_RETRY_TIME)
162+ guest_instance_link = os.readlink(host_instance_path)
163+ host_instance_link = os.path.join(
164+ host_guest_root, *full_path_split(guest_instance_link)[1:])
165+ host_boot_finished_path = os.path.join(
166+ host_instance_link, 'boot-finished')
167+ wait_for_lexists(host_boot_finished_path)
168+
169+def generate_random_lxc_container_name():
170+ return ('adt-virt-lxc-' +
171+ ''.join([random.choice(string.ascii_lowercase) for i in range(6)]))
172+
173+def get_available_lxc_container_name():
174+ '''Return an LXC container name that isn't already taken.
175+
176+ There is a race condition between returning this name and creation of
177+ the container. Ideally lxc-start-ephemeral would generate a name in a
178+ race free way and return the name used in machine-readable way, so that
179+ this function would not be necessary. See LP: #1197754.
180+ '''
181+
182+ candidate = generate_random_lxc_container_name()
183+ while os.path.lexists('/var/lib/lxc/%s' % candidate):
184+ candidate = generate_random_lxc_container_name()
185+ return candidate
186+
187+def hook_open():
188+ global sessid, downtmp, lxc_container_name
189+ lxc_container_name = get_available_lxc_container_name()
190+ if ephemeral:
191+ vsp.execute('sudo lxc-start-ephemeral -n %s -k -d -o' % lxc_container_name,[lxc_template], downp=False, outp=True)
192+ else:
193+ vsp.execute('sudo lxc-clone -n %s -o' % lxc_container_name,[lxc_template], downp=False, outp=True)
194+ vsp.execute('sudo lxc-start -n %s -d' % lxc_container_name,[], downp=False, outp=True)
195+ try:
196+ vsp.debug('waiting for lxc guest start')
197+ wait_for_lxc_guest_start(lxc_container_name, lxc_template)
198+ vsp.debug('lxc guest started')
199+ vsp.down = ['sudo', '-E', 'lxc-attach','-n',lxc_container_name,'--']
200+ if eatmydata:
201+ check_for_eatmydata(lxc_container_name, lxc_template)
202+ vsp.down.extend(['eatmydata', '--'])
203+ vsp.downkind = 'auxverb'
204+ except:
205+ # Clean up on failure
206+ vsp.execute('sudo lxc-stop -n',[lxc_container_name], downp=False, outp=True)
207+ vsp.execute('sudo lxc-destroy -n',[lxc_container_name], downp=False, outp=True)
208+ raise
209+
210+def hook_downtmp():
211+ downtmp = '/tmp/adt-downtmp'
212+ vsp.execute('mkdir %s' % downtmp, downp=True)
213+ return downtmp
214+
215+def hook_revert():
216+ vsp.downtmp_remove()
217+ hook_cleanup()
218+ hook_open()
219+
220+def hook_cleanup():
221+ global sessid, downtmp
222+ vsp.downtmp_remove()
223+ vsp.execute('sudo lxc-stop -n',[lxc_container_name], downp=False, outp=True)
224+ vsp.execute('sudo lxc-destroy -n',[lxc_container_name], downp=False, outp=True)
225+
226+def hook_forked_inchild():
227+ pass
228+
229+def hook_capabilities():
230+ return ['revert', 'revert-full-system', 'root-on-testbed', 'suggested-normal-user=ubuntu']
231+
232+parse_args()
233+vsp.main()
234
235=== added file 'virt-subproc/adt-virt-lxc.1'
236--- virt-subproc/adt-virt-lxc.1 1970-01-01 00:00:00 +0000
237+++ virt-subproc/adt-virt-lxc.1 2013-10-16 16:21:23 +0000
238@@ -0,0 +1,124 @@
239+.TH adt\-virt-lxc 1 2013 "Linux Programmer's Manual"
240+.SH NAME
241+adt\-virt\-lxc \- autopkgtest virtualisation server using LXC
242+.SH SYNOPSYS
243+.B adt\-virt\-lxc
244+.RB [ \-\-d | \-\-debug ]
245+.RB [ \-\-eatmydata ]
246+.RB [ \-\-ephemeral ]
247+.I lxc\-template
248+.SH DESCRIPTION
249+.B adt-virt-lxc
250+provides an autopkgtest virtualisation server using LXC. It adapts the raw
251+functionality provided by the
252+.BR lxc- *
253+tools for use by autopkgtest.
254+
255+Normally
256+.B adt-virt-lxc
257+will be invoked by
258+.BR adt-run .
259+
260+.SH REQUIREMENTS
261+.B adt-virt-lxc
262+assumes that you have already prepared a suitable LXC template container. The
263+template must be Debian-based and use cloud-init. \fBdebootstrap\fR(8)-based
264+containers such as those created with the \fIdebian\fR or \fIubuntu\fR LXC
265+templates will not work.
266+
267+On Ubuntu, you can automatically download and install a suitable template using
268+the command:
269+
270+.RS
271+.EX
272+lxc-create -t ubuntu-cloud -n adt -- -r $(distro-info --devel) -d daily
273+.EE
274+.RE
275+
276+The supplied LXC template must be clean. The \fBcloud-init\fR
277+\fI/var/cloud/instance\fR symlink should not exist, and the subsequent
278+\fI/var/lib/cloud/instance/boot-finished\fR should also not exist. If you start
279+your template container in order to modify it, make sure you remove these
280+artifacts when you have finished making your modifications.
281+
282+
283+.SH OPTIONS
284+.TP
285+.B --eatmydata
286+This option is intended for test developer use. It enables the use of
287+\fBeatmydata\fR(1) inside the container on all commands in order to improve
288+performance. This requires
289+.BR eatmydata (1)
290+to already be installed inside the supplied template, which you will need to do
291+manually.
292+
293+However, since eatmydata is not part of the dep8 specification and may affect
294+the test
295+environment, it is not recommended for use in automation.
296+.TP
297+.B --ephemeral
298+This option makes use of lxc-start-ephemeral to create temporary containers,
299+instead of the default behaviour of cloning your containers. This can be used
300+to dramatically improve performance, although you may see issues due to the
301+overlayfs filesystem as used by lxc-start-ephemeral not being completely
302+transparent. To avoid issues, you should not use this option in automation.
303+.TP
304+.BR \-d " | " \-\-debug
305+Enables debugging output.
306+.SH INPUT, OUTPUT AND EXIT STATUS
307+The behaviour of
308+.B adt-virt-lxc
309+is as described by the AutomatedTesting virtualisation regime
310+specification.
311+
312+.SH NOTES
313+
314+\fBadt-run\fR assumes it can use \fBfakeroot\fR(1) to gain root for the
315+\fIbinary\fR target in a package build. If \fBfakeroot\fR(1) isn't available in
316+your template container, you should use \fBadt-run\fR's \fB\-\-gain\-root\fR
317+option to specify what to use instead. On Ubuntu, you can use
318+\fB\-\-gain\-root=sudo\fR since this is included in Ubuntu cloud images
319+configured to permit root access without a password for the default user. See
320+the example below.
321+
322+\fBadt-run\fR does not run \fBapt-get update\fR at the start of a package
323+build, which can cause a build failure if you are using a development release
324+template. You will need to run \fBapt-get update\fR in the template yourself as
325+required, and remove the boot artifacts as discussed in \fBREQUIREMENTS\fR
326+before trying again.
327+
328+.SH EXAMPLE
329+
330+On Ubuntu, download and install a suitable template and call it \fIadt\fR:
331+
332+.RS
333+.EX
334+lxc-create -t ubuntu-cloud -n \fIadt\fR -- -r $(distro-info --devel) -d daily
335+.EE
336+.RE
337+
338+Run tests against \fIhello_2.8\-4.dsc\fR, using the LXC template \fIadt\fR,
339+and with an ephemeral container for speed:
340+
341+.RS
342+.EX
343+adt-run --gain-root=sudo \fIhello_2.8\-4.dsc\fR --- adt-virt-lxc --ephemeral \fIadt\fR
344+.EE
345+.RE
346+
347+.SH SEE ALSO
348+\fBadt-run\fR(1),
349+\fBadt\-virt-null\fR(1),
350+\fBadt\-virt-chroot\fR(1),
351+\fBeatmydata\fR(1),
352+\fB/usr/share/doc/autopkgtest/\fR.
353+
354+.SH AUTHORS AND COPYRIGHT
355+.B adt-virt-lxc
356+was written by Robie Basak <robie.basak@canonical.com>.
357+
358+This manpage is part of autopkgtest, a tool for testing Debian binary
359+packages. autopkgtest is Copyright (C) 2006-2013 Canonical Ltd and others.
360+
361+See \fB/usr/share/doc/autopkgtest/CREDITS\fR for the list of
362+contributors and full copying conditions.

Subscribers

People subscribed via source and target branches

to all changes: