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

Subscribers

People subscribed via source and target branches

to all changes: