Merge lp:~bregma/libertine/refactor-container-back-ends into lp:libertine
- refactor-container-back-ends
- Merge into devel
Proposed by
Stephen M. Webb
Status: | Merged |
---|---|
Approved by: | Christopher Townsend |
Approved revision: | 144 |
Merged at revision: | 136 |
Proposed branch: | lp:~bregma/libertine/refactor-container-back-ends |
Merge into: | lp:libertine |
Prerequisite: | lp:~bregma/libertine/track-apt-progress |
Diff against target: |
852 lines (+415/-329) 7 files modified
debian/control (+36/-7) debian/python3-libertine-chroot.install (+1/-0) debian/python3-libertine-lxc.install (+1/-0) debian/python3-libertine.install (+3/-1) python/libertine/ChrootContainer.py (+168/-0) python/libertine/Libertine.py (+7/-321) python/libertine/LxcContainer.py (+199/-0) |
To merge this branch: | bzr merge lp:~bregma/libertine/refactor-container-back-ends |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Townsend | Approve | ||
Review via email: mp+278985@code.launchpad.net |
Commit message
refactor lxc and chroot container back ends into separately-
Description of the change
Refactored the container handling code into separate plugin packages for LXC and chroot so both are not required everywhere.
In particular, we want to avoid grief from having people attempt to use the LXC back end on click-based images and we want to avoid people using chroots elsewhere.
To post a comment you must log in.
Revision history for this message
Christopher Townsend (townsend) wrote : | # |
Revision history for this message
Christopher Townsend (townsend) wrote : | # |
Couple of inline comments.
- 144. By Stephen M. Webb
-
removed a debug message
Revision history for this message
Christopher Townsend (townsend) wrote : | # |
Great, thanks!
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/control' |
2 | --- debian/control 2015-11-19 20:17:44 +0000 |
3 | +++ debian/control 2015-11-30 19:37:49 +0000 |
4 | @@ -23,6 +23,7 @@ |
5 | Package: libertine |
6 | Architecture: any |
7 | Depends: libertine-tools, |
8 | + python3-libertine-lxc, |
9 | qml-module-qtquick2, |
10 | qtdeclarative5-ubuntu-ui-toolkit-plugin, |
11 | ${misc:Depends}, |
12 | @@ -82,25 +83,53 @@ |
13 | Architecture: any |
14 | Section: python |
15 | Multi-Arch: allowed |
16 | +Depends: gir1.2-libertine, |
17 | + python3-gi, |
18 | + python3-xdg, |
19 | + ${misc:Depends}, |
20 | + ${python3:Depends} |
21 | +Recommends: python3-libertine-lxc |
22 | +Suggests: python3-libertine-chroot |
23 | +Description: Python3 scripts for the Libertine application sandbox |
24 | + Python3 modules for the Libertine application sandbox tools. Requires at |
25 | + least one of the container back end modules installed to be of any use. |
26 | + |
27 | +Package: python3-libertine-lxc |
28 | +Architecture: any |
29 | +Section: python |
30 | +Multi-Arch: allowed |
31 | +Depends: lxc-templates, |
32 | + python3-lxc, |
33 | + python3-xdg, |
34 | + uidmap, |
35 | + ${misc:Depends}, |
36 | + ${python3:Depends} |
37 | +Description: Python3 scripts for the Libertine application sandbox |
38 | + This package provides the LXC-based container back end module for the |
39 | + Libertine sandbox. It requires support for unprivileged LXC containers in the |
40 | + Linux kernel. |
41 | + |
42 | +Package: python3-libertine-chroot |
43 | +Architecture: any |
44 | +Section: python |
45 | +Multi-Arch: allowed |
46 | Depends: debootstrap, |
47 | fakechroot, |
48 | fakeroot, |
49 | - gir1.2-libertine, |
50 | - lxc-templates, |
51 | proot [amd64 arm64 armhf i386], |
52 | - python3-gi, |
53 | - python3-lxc, |
54 | - python3-xdg, |
55 | - uidmap, |
56 | ${misc:Depends}, |
57 | ${python3:Depends} |
58 | Description: Python3 scripts for the Libertine application sandbox |
59 | - Python3 modules for the Libertine application sandbox tools. |
60 | + This package provides the chroot-based container back end module for the |
61 | + Libertine sandbox. This container back end module is intended only for |
62 | + curated containers distributed for devices that do not support unprivileged |
63 | + LXC contaiers. |
64 | |
65 | Package: libertine-demo |
66 | Architecture: any |
67 | Multi-Arch: allowed |
68 | Depends: libertine-tools, |
69 | + python3-libertine-chroot, |
70 | ${misc:Depends} |
71 | Description: Adds desktop files and icon for Unity 8 desktop support |
72 | Add desktop files and icons for the targeted applications needed for legacy |
73 | |
74 | === added file 'debian/python3-libertine-chroot.install' |
75 | --- debian/python3-libertine-chroot.install 1970-01-01 00:00:00 +0000 |
76 | +++ debian/python3-libertine-chroot.install 2015-11-30 19:37:49 +0000 |
77 | @@ -0,0 +1,1 @@ |
78 | +usr/lib/python*/*/libertine/ChrootContainer.py |
79 | |
80 | === added file 'debian/python3-libertine-lxc.install' |
81 | --- debian/python3-libertine-lxc.install 1970-01-01 00:00:00 +0000 |
82 | +++ debian/python3-libertine-lxc.install 2015-11-30 19:37:49 +0000 |
83 | @@ -0,0 +1,1 @@ |
84 | +usr/lib/python*/*/libertine/LxcContainer.py |
85 | |
86 | === modified file 'debian/python3-libertine.install' |
87 | --- debian/python3-libertine.install 2015-08-24 16:32:41 +0000 |
88 | +++ debian/python3-libertine.install 2015-11-30 19:37:49 +0000 |
89 | @@ -1,1 +1,3 @@ |
90 | -usr/lib/python*/*/libertine |
91 | +usr/lib/python*/*/libertine/__init__.py |
92 | +usr/lib/python*/*/libertine/Libertine.py |
93 | +usr/lib/python*/*/libertine/utils.py |
94 | |
95 | === added file 'python/libertine/ChrootContainer.py' |
96 | --- python/libertine/ChrootContainer.py 1970-01-01 00:00:00 +0000 |
97 | +++ python/libertine/ChrootContainer.py 2015-11-30 19:37:49 +0000 |
98 | @@ -0,0 +1,168 @@ |
99 | +# Copyright 2015 Canonical Ltd. |
100 | +# |
101 | +# This program is free software: you can redistribute it and/or modify it |
102 | +# under the terms of the GNU General Public License version 3, as published |
103 | +# by the Free Software Foundation. |
104 | +# |
105 | +# This program is distributed in the hope that it will be useful, but |
106 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
107 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
108 | +# PURPOSE. See the GNU General Public License for more details. |
109 | +# |
110 | +# You should have received a copy of the GNU General Public License along |
111 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
112 | + |
113 | +import os |
114 | +import shlex |
115 | +import shutil |
116 | +import subprocess |
117 | +from .Libertine import ( |
118 | + BaseContainer, get_container_distro, get_host_architecture, |
119 | + create_libertine_user_data_dir, create_compiz_config) |
120 | +from . import utils |
121 | + |
122 | + |
123 | +def chown_recursive_dirs(path): |
124 | + uid = None |
125 | + gid = None |
126 | + |
127 | + if 'SUDO_UID' in os.environ: |
128 | + uid = int(os.environ['SUDO_UID']) |
129 | + if 'SUDO_GID' in os.environ: |
130 | + gid = int(os.environ['SUDO_GID']) |
131 | + |
132 | + if uid is not None and gid is not None: |
133 | + for root, dirs, files in os.walk(path): |
134 | + for d in dirs: |
135 | + os.chown(os.path.join(root, d), uid, gid) |
136 | + for f in files: |
137 | + os.chown(os.path.join(root, f), uid, gid) |
138 | + |
139 | + os.chown(path, uid, gid) |
140 | + |
141 | + |
142 | +class LibertineChroot(BaseContainer): |
143 | + """ |
144 | + A concrete container type implemented using a plain old chroot. |
145 | + """ |
146 | + |
147 | + def __init__(self, container_id): |
148 | + super().__init__(container_id) |
149 | + self.series = get_container_distro(container_id) |
150 | + self.chroot_path = utils.get_libertine_container_rootfs_path(container_id) |
151 | + os.environ['FAKECHROOT_CMD_SUBST'] = '$FAKECHROOT_CMD_SUBST:/usr/bin/chfn=/bin/true' |
152 | + os.environ['DEBIAN_FRONTEND'] = 'noninteractive' |
153 | + |
154 | + def run_in_container(self, command_string): |
155 | + cmd_args = shlex.split(command_string) |
156 | + if self.series == "trusty": |
157 | + proot_cmd = '/usr/bin/proot' |
158 | + if not os.path.isfile(proot_cmd) or not os.access(proot_cmd, os.X_OK): |
159 | + raise RuntimeError('executable proot not found') |
160 | + command_prefix = proot_cmd + " -b /usr/lib/locale -S " + self.chroot_path |
161 | + else: |
162 | + command_prefix = "fakechroot fakeroot chroot " + self.chroot_path |
163 | + args = shlex.split(command_prefix + ' ' + command_string) |
164 | + cmd = subprocess.Popen(args) |
165 | + return cmd.wait() |
166 | + |
167 | + def destroy_libertine_container(self): |
168 | + shutil.rmtree(self.chroot_path) |
169 | + |
170 | + def create_libertine_container(self, password=None, verbosity=1): |
171 | + installed_release = self.series |
172 | + |
173 | + # Create the actual chroot |
174 | + if installed_release == "trusty": |
175 | + command_line = "debootstrap --verbose " + installed_release + " " + self.chroot_path |
176 | + else: |
177 | + command_line = "fakechroot fakeroot debootstrap --verbose --variant=fakechroot {} {}".format( |
178 | + installed_release, self.chroot_path) |
179 | + args = shlex.split(command_line) |
180 | + subprocess.Popen(args).wait() |
181 | + |
182 | + # Remove symlinks as they can ill-behaved recursive behavior in the chroot |
183 | + if installed_release != "trusty": |
184 | + print("Fixing chroot symlinks...") |
185 | + os.remove(os.path.join(self.chroot_path, 'dev')) |
186 | + os.remove(os.path.join(self.chroot_path, 'proc')) |
187 | + |
188 | + with open(os.path.join(self.chroot_path, 'usr', 'sbin', 'policy-rc.d'), 'w+') as fd: |
189 | + fd.write("#!/bin/sh\n\n") |
190 | + fd.write("while true; do\n") |
191 | + fd.write("case \"$1\" in\n") |
192 | + fd.write(" -*) shift ;;\n") |
193 | + fd.write(" makedev) exit 0;;\n") |
194 | + fd.write(" *) exit 101;;\n") |
195 | + fd.write("esac\n") |
196 | + fd.write("done\n") |
197 | + os.fchmod(fd.fileno(), 0o755) |
198 | + |
199 | + # Add universe and -updates to the chroot's sources.list |
200 | + if (get_host_architecture() == 'armhf'): |
201 | + archive = "deb http://ports.ubuntu.com/ubuntu-ports " |
202 | + else: |
203 | + archive = "deb http://archive.ubuntu.com/ubuntu " |
204 | + |
205 | + if verbosity == 1: |
206 | + print("Updating chroot's sources.list entries...") |
207 | + with open(os.path.join(self.chroot_path, 'etc', 'apt', 'sources.list'), 'a') as fd: |
208 | + fd.write(archive + installed_release + " universe\n") |
209 | + fd.write(archive + installed_release + "-updates main\n") |
210 | + fd.write(archive + installed_release + "-updates universe\n") |
211 | + |
212 | + create_libertine_user_data_dir(self.container_id) |
213 | + |
214 | + if installed_release == "trusty": |
215 | + print("Additional configuration for Trusty chroot...") |
216 | + |
217 | + proot_cmd = '/usr/bin/proot' |
218 | + if not os.path.isfile(proot_cmd) or not os.access(proot_cmd, os.X_OK): |
219 | + raise RuntimeError('executable proot not found') |
220 | + cmd_line_prefix = proot_cmd + " -b /usr/lib/locale -S " + self.chroot_path |
221 | + |
222 | + command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /etc/init.d/systemd-logind" |
223 | + args = shlex.split(command_line) |
224 | + cmd = subprocess.Popen(args).wait() |
225 | + |
226 | + command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /sbin/initctl" |
227 | + args = shlex.split(command_line) |
228 | + cmd = subprocess.Popen(args).wait() |
229 | + |
230 | + command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /sbin/udevd" |
231 | + args = shlex.split(command_line) |
232 | + cmd = subprocess.Popen(args).wait() |
233 | + |
234 | + command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /usr/sbin/rsyslogd" |
235 | + args = shlex.split(command_line) |
236 | + cmd = subprocess.Popen(args).wait() |
237 | + |
238 | + command_line = cmd_line_prefix + " ln -s /bin/true /etc/init.d/systemd-logind" |
239 | + args = shlex.split(command_line) |
240 | + cmd = subprocess.Popen(args).wait() |
241 | + |
242 | + command_line = cmd_line_prefix + " ln -s /bin/true /sbin/initctl" |
243 | + args = shlex.split(command_line) |
244 | + cmd = subprocess.Popen(args).wait() |
245 | + |
246 | + command_line = cmd_line_prefix + " ln -s /bin/true /sbin/udevd" |
247 | + args = shlex.split(command_line) |
248 | + cmd = subprocess.Popen(args).wait() |
249 | + |
250 | + command_line = cmd_line_prefix + " ln -s /bin/true /usr/sbin/rsyslogd" |
251 | + args = shlex.split(command_line) |
252 | + cmd = subprocess.Popen(args).wait() |
253 | + |
254 | + if verbosity == 1: |
255 | + print("Updating the contents of the container after creation...") |
256 | + self.update_packages(verbosity) |
257 | + self.install_package("libnss-extrausers", verbosity) |
258 | + |
259 | + if verbosity == 1: |
260 | + print("Installing Compiz as the Xmir window manager...") |
261 | + self.install_package("compiz", verbosity) |
262 | + create_compiz_config(self.container_id) |
263 | + |
264 | + # Check if the container was created as root and chown the user directories as necessary |
265 | + chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id)) |
266 | + |
267 | |
268 | === modified file 'python/libertine/Libertine.py' |
269 | --- python/libertine/Libertine.py 2015-11-30 19:37:49 +0000 |
270 | +++ python/libertine/Libertine.py 2015-11-30 19:37:49 +0000 |
271 | @@ -14,47 +14,12 @@ |
272 | |
273 | import abc |
274 | import contextlib |
275 | -import crypt |
276 | import json |
277 | -import lxc |
278 | import os |
279 | import shlex |
280 | -import shutil |
281 | import subprocess |
282 | import libertine.utils |
283 | |
284 | -home_path = os.environ['HOME'] |
285 | - |
286 | - |
287 | -def check_lxc_net_entry(entry): |
288 | - lxc_net_file = open('/etc/lxc/lxc-usernet') |
289 | - found = False |
290 | - |
291 | - for line in lxc_net_file: |
292 | - if entry in line: |
293 | - found = True |
294 | - break |
295 | - |
296 | - return found |
297 | - |
298 | - |
299 | -def setup_host_environment(username, password): |
300 | - lxc_net_entry = "%s veth lxcbr0 10" % str(username) |
301 | - |
302 | - if not check_lxc_net_entry(lxc_net_entry): |
303 | - passwd = subprocess.Popen(["sudo", "--stdin", "usermod", "--add-subuids", "100000-165536", |
304 | - "--add-subgids", "100000-165536", str(username)], |
305 | - stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, |
306 | - stderr=subprocess.STDOUT) |
307 | - passwd.communicate((password + '\n').encode('UTF-8')) |
308 | - |
309 | - add_user_cmd = "echo %s | sudo tee -a /etc/lxc/lxc-usernet > /dev/null" % lxc_net_entry |
310 | - subprocess.Popen(add_user_cmd, shell=True) |
311 | - |
312 | - |
313 | -def get_lxc_default_config_path(): |
314 | - return os.path.join(home_path, '.config', 'lxc') |
315 | - |
316 | |
317 | def get_container_distro(container_id): |
318 | container_distro = "" |
319 | @@ -79,25 +44,6 @@ |
320 | return dpkg.stdout.read().strip() |
321 | |
322 | |
323 | -def chown_recursive_dirs(path): |
324 | - uid = None |
325 | - gid = None |
326 | - |
327 | - if 'SUDO_UID' in os.environ: |
328 | - uid = int(os.environ['SUDO_UID']) |
329 | - if 'SUDO_GID' in os.environ: |
330 | - gid = int(os.environ['SUDO_GID']) |
331 | - |
332 | - if uid is not None and gid is not None: |
333 | - for root, dirs, files in os.walk(path): |
334 | - for d in dirs: |
335 | - os.chown(os.path.join(root, d), uid, gid) |
336 | - for f in files: |
337 | - os.chown(os.path.join(root, f), uid, gid) |
338 | - |
339 | - os.chown(path, uid, gid) |
340 | - |
341 | - |
342 | def create_libertine_user_data_dir(container_id): |
343 | user_data = libertine.utils.get_libertine_container_userdata_dir_path(container_id) |
344 | |
345 | @@ -126,13 +72,6 @@ |
346 | fd.write("s0_mode = 3") |
347 | |
348 | |
349 | -def lxc_container(container_id): |
350 | - config_path = libertine.utils.get_libertine_containers_dir_path() |
351 | - container = lxc.Container(container_id, config_path) |
352 | - |
353 | - return container |
354 | - |
355 | - |
356 | def apt_args_for_verbosity_level(verbosity): |
357 | """ |
358 | Maps numeric verbosity levels onto APT command-line arguments. |
359 | @@ -202,273 +141,18 @@ |
360 | |
361 | :param verbosity: the chattiness of the output on a range from 0 to 2 |
362 | """ |
363 | - self.run_in_container(apt_command_prefix(verbosity) + 'update') |
364 | - self.run_in_container(apt_command_prefix(verbosity) + 'upgrade') |
365 | + self.run_in_container(apt_command_prefix(verbosity) + '--force-yes update') |
366 | + self.run_in_container(apt_command_prefix(verbosity) + '--force-yes upgrade') |
367 | |
368 | - def install_package(self, package_name, verbosity=1): |
369 | + def install_package(self, package_name, verbosity=1, extra_apt_args=""): |
370 | """ |
371 | Installs a named package in the container. |
372 | |
373 | :param package_name: The name of the package as APT understands it. |
374 | :param verbosity: the chattiness of the output on a range from 0 to 2 |
375 | """ |
376 | - return self.run_in_container(apt_command_prefix(verbosity) + "install --no-install-recommends '" + package_name + "'") == 0 |
377 | - |
378 | - |
379 | -class LibertineLXC(BaseContainer): |
380 | - """ |
381 | - A concrete container type implemented using an LXC container. |
382 | - """ |
383 | - |
384 | - def __init__(self, container_id): |
385 | - super().__init__(container_id) |
386 | - self.container = lxc_container(container_id) |
387 | - self.series = get_container_distro(container_id) |
388 | - |
389 | - def is_running(self): |
390 | - return self.container.running |
391 | - |
392 | - def start_container(self): |
393 | - if not self.container.running: |
394 | - if not self.container.start(): |
395 | - raise RuntimeError("Container failed to start") |
396 | - if not self.container.wait("RUNNING", 10): |
397 | - raise RuntimeError("Container failed to enter the RUNNING state") |
398 | - |
399 | - if not self.container.get_ips(timeout=30): |
400 | - raise RuntimeError("Not able to connect to the network.") |
401 | - |
402 | - self.run_in_container("umount /tmp/.X11-unix") |
403 | - |
404 | - def stop_container(self): |
405 | - self.container.stop() |
406 | - |
407 | - def run_in_container(self, command_string): |
408 | - cmd_args = shlex.split(command_string) |
409 | - return self.container.attach_wait(lxc.attach_run_command, cmd_args) |
410 | - |
411 | - def destroy_libertine_container(self): |
412 | - if self.container.defined: |
413 | - self.container.stop() |
414 | - self.container.destroy() |
415 | - |
416 | - def create_libertine_container(self, password=None, verbosity=1): |
417 | - if password is None: |
418 | - return |
419 | - |
420 | - installed_release = self.series |
421 | - |
422 | - username = os.environ['USER'] |
423 | - user_id = os.getuid() |
424 | - group_id = os.getgid() |
425 | - |
426 | - setup_host_environment(username, password) |
427 | - |
428 | - # Generate the default lxc default config, if it doesn't exist |
429 | - config_path = get_lxc_default_config_path() |
430 | - config_file = "%s/default.conf" % config_path |
431 | - |
432 | - if not os.path.exists(config_path): |
433 | - os.mkdir(config_path) |
434 | - |
435 | - if not os.path.exists(config_file): |
436 | - with open(config_file, "w+") as fd: |
437 | - fd.write("lxc.network.type = veth\n") |
438 | - fd.write("lxc.network.link = lxcbr0\n") |
439 | - fd.write("lxc.network.flags = up\n") |
440 | - fd.write("lxc.network.hwaddr = 00:16:3e:xx:xx:xx\n") |
441 | - fd.write("lxc.id_map = u 0 100000 %s\n" % user_id) |
442 | - fd.write("lxc.id_map = g 0 100000 %s\n" % group_id) |
443 | - fd.write("lxc.id_map = u %s %s 1\n" % (user_id, user_id)) |
444 | - fd.write("lxc.id_map = g %s %s 1\n" % (group_id, group_id)) |
445 | - fd.write("lxc.id_map = u %s %s %s\n" % (user_id + 1, (user_id + 1) + 100000, 65536 - (user_id + 1))) |
446 | - fd.write("lxc.id_map = g %s %s %s\n" % (group_id + 1, (group_id + 1) + 100000, 65536 - (user_id + 1))) |
447 | - |
448 | - create_libertine_user_data_dir(self.container_id) |
449 | - |
450 | - # Figure out the host architecture |
451 | - architecture = get_host_architecture() |
452 | - |
453 | - if not self.container.create("download", 0, |
454 | - {"dist": "ubuntu", |
455 | - "release": installed_release, |
456 | - "arch": architecture}): |
457 | - return False |
458 | - |
459 | - self.create_libertine_config() |
460 | - |
461 | - if self.container.start(): |
462 | - self.run_in_container("userdel-r ubuntu") |
463 | - self.run_in_container("useradd -u {} -p {} -G sudo {}".format(str(user_id), crypt.crypt(password), str(username))) |
464 | - |
465 | - if verbosity == 1: |
466 | - print("Updating the contents of the container after creation...") |
467 | - self.update_packages(verbosity) |
468 | - |
469 | - if verbosity == 1: |
470 | - print("Installing Compiz as the Xmir window manager...") |
471 | - self.install_package('compiz', verbosity) |
472 | - create_compiz_config(self.container.name) |
473 | - |
474 | - self.container.stop() |
475 | - else: |
476 | - raise RuntimeError("Container failed to start.") |
477 | - |
478 | - def create_libertine_config(self): |
479 | - user_id = os.getuid() |
480 | - home_entry = ( |
481 | - "%s %s none bind,create=dir" |
482 | - % (libertine.utils.get_libertine_container_userdata_dir_path(self.container_id), |
483 | - home_path.strip('/')) |
484 | - ) |
485 | - |
486 | - # Bind mount the user's home directory |
487 | - self.container.append_config_item("lxc.mount.entry", home_entry) |
488 | - |
489 | - xdg_user_dirs = ['Documents', 'Music', 'Pictures', 'Videos'] |
490 | - |
491 | - for user_dir in xdg_user_dirs: |
492 | - xdg_user_dir_entry = ( |
493 | - "%s/%s %s/%s none bind,create=dir,optional" |
494 | - % (home_path, user_dir, home_path.strip('/'), user_dir) |
495 | - ) |
496 | - self.container.append_config_item("lxc.mount.entry", xdg_user_dir_entry) |
497 | - |
498 | - # Setup the mounts for /run/user/$user_id |
499 | - run_user_entry = "/run/user/%s run/user/%s none rbind,create=dir" % (user_id, user_id) |
500 | - self.container.append_config_item("lxc.mount.entry", "tmpfs run tmpfs rw,nodev,noexec,nosuid,size=5242880") |
501 | - self.container.append_config_item("lxc.mount.entry", |
502 | - "none run/user tmpfs rw,nodev,noexec,nosuid,size=104857600,mode=0755,create=dir") |
503 | - self.container.append_config_item("lxc.mount.entry", run_user_entry) |
504 | - |
505 | - self.container.append_config_item("lxc.include", "/usr/share/libertine/libertine-lxc.conf") |
506 | - |
507 | - # Dump it all to disk |
508 | - self.container.save_config() |
509 | - |
510 | - |
511 | -class LibertineChroot(BaseContainer): |
512 | - """ |
513 | - A concrete container type implemented using a plain old chroot. |
514 | - """ |
515 | - |
516 | - def __init__(self, container_id): |
517 | - super().__init__(container_id) |
518 | - self.series = get_container_distro(container_id) |
519 | - self.chroot_path = libertine.utils.get_libertine_container_rootfs_path(container_id) |
520 | - os.environ['FAKECHROOT_CMD_SUBST'] = '$FAKECHROOT_CMD_SUBST:/usr/bin/chfn=/bin/true' |
521 | - os.environ['DEBIAN_FRONTEND'] = 'noninteractive' |
522 | - |
523 | - def run_in_container(self, command_string): |
524 | - cmd_args = shlex.split(command_string) |
525 | - if self.series == "trusty": |
526 | - proot_cmd = '/usr/bin/proot' |
527 | - if not os.path.isfile(proot_cmd) or not os.access(proot_cmd, os.X_OK): |
528 | - raise RuntimeError('executable proot not found') |
529 | - command_prefix = proot_cmd + " -b /usr/lib/locale -S " + self.chroot_path |
530 | - else: |
531 | - command_prefix = "fakechroot fakeroot chroot " + self.chroot_path |
532 | - args = shlex.split(command_prefix + ' ' + command_string) |
533 | - cmd = subprocess.Popen(args).wait() |
534 | - |
535 | - def destroy_libertine_container(self): |
536 | - shutil.rmtree(self.chroot_path) |
537 | - |
538 | - def create_libertine_container(self, password=None, verbosity=1): |
539 | - installed_release = self.series |
540 | - |
541 | - # Create the actual chroot |
542 | - if installed_release == "trusty": |
543 | - command_line = "debootstrap --verbose " + installed_release + " " + self.chroot_path |
544 | - else: |
545 | - command_line = "fakechroot fakeroot debootstrap --verbose --variant=fakechroot " + installed_release + " " + self.chroot_path |
546 | - args = shlex.split(command_line) |
547 | - subprocess.Popen(args).wait() |
548 | - |
549 | - # Remove symlinks as they can ill-behaved recursive behavior in the chroot |
550 | - if installed_release != "trusty": |
551 | - print("Fixing chroot symlinks...") |
552 | - os.remove(os.path.join(self.chroot_path, 'dev')) |
553 | - os.remove(os.path.join(self.chroot_path, 'proc')) |
554 | - |
555 | - with open(os.path.join(self.chroot_path, 'usr', 'sbin', 'policy-rc.d'), 'w+') as fd: |
556 | - fd.write("#!/bin/sh\n\n") |
557 | - fd.write("while true; do\n") |
558 | - fd.write("case \"$1\" in\n") |
559 | - fd.write(" -*) shift ;;\n") |
560 | - fd.write(" makedev) exit 0;;\n") |
561 | - fd.write(" *) exit 101;;\n") |
562 | - fd.write("esac\n") |
563 | - fd.write("done\n") |
564 | - os.fchmod(fd.fileno(), 0o755) |
565 | - |
566 | - # Add universe and -updates to the chroot's sources.list |
567 | - if (get_host_architecture() == 'armhf'): |
568 | - archive = "deb http://ports.ubuntu.com/ubuntu-ports " |
569 | - else: |
570 | - archive = "deb http://archive.ubuntu.com/ubuntu " |
571 | - |
572 | - if verbosity == 1: |
573 | - print("Updating chroot's sources.list entries...") |
574 | - with open(os.path.join(self.chroot_path, 'etc', 'apt', 'sources.list'), 'a') as fd: |
575 | - fd.write(archive + installed_release + " universe\n") |
576 | - fd.write(archive + installed_release + "-updates main\n") |
577 | - fd.write(archive + installed_release + "-updates universe\n") |
578 | - |
579 | - create_libertine_user_data_dir(self.container_id) |
580 | - |
581 | - if installed_release == "trusty": |
582 | - print("Additional configuration for Trusty chroot...") |
583 | - |
584 | - proot_cmd = '/usr/bin/proot' |
585 | - if not os.path.isfile(proot_cmd) or not os.access(proot_cmd, os.X_OK): |
586 | - raise RuntimeError('executable proot not found') |
587 | - cmd_line_prefix = proot_cmd + " -b /usr/lib/locale -S " + self.chroot_path |
588 | - |
589 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /etc/init.d/systemd-logind" |
590 | - args = shlex.split(command_line) |
591 | - cmd = subprocess.Popen(args).wait() |
592 | - |
593 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /sbin/initctl" |
594 | - args = shlex.split(command_line) |
595 | - cmd = subprocess.Popen(args).wait() |
596 | - |
597 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /sbin/udevd" |
598 | - args = shlex.split(command_line) |
599 | - cmd = subprocess.Popen(args).wait() |
600 | - |
601 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /usr/sbin/rsyslogd" |
602 | - args = shlex.split(command_line) |
603 | - cmd = subprocess.Popen(args).wait() |
604 | - |
605 | - command_line = cmd_line_prefix + " ln -s /bin/true /etc/init.d/systemd-logind" |
606 | - args = shlex.split(command_line) |
607 | - cmd = subprocess.Popen(args).wait() |
608 | - |
609 | - command_line = cmd_line_prefix + " ln -s /bin/true /sbin/initctl" |
610 | - args = shlex.split(command_line) |
611 | - cmd = subprocess.Popen(args).wait() |
612 | - |
613 | - command_line = cmd_line_prefix + " ln -s /bin/true /sbin/udevd" |
614 | - args = shlex.split(command_line) |
615 | - cmd = subprocess.Popen(args).wait() |
616 | - |
617 | - command_line = cmd_line_prefix + " ln -s /bin/true /usr/sbin/rsyslogd" |
618 | - args = shlex.split(command_line) |
619 | - cmd = subprocess.Popen(args).wait() |
620 | - |
621 | - if verbosity == 1: |
622 | - print("Updating the contents of the container after creation...") |
623 | - self.update_packages(verbosity) |
624 | - self.install_package("libnss-extrausers", verbosity) |
625 | - |
626 | - if verbosity == 1: |
627 | - print("Installing Compiz as the Xmir window manager...") |
628 | - self.install_package("compiz", verbosity) |
629 | - create_compiz_config(self.container_id) |
630 | - |
631 | - # Check if the container was created as root and chown the user directories as necessary |
632 | - chown_recursive_dirs(libertine.utils.get_libertine_container_userdata_dir_path(self.container_id)) |
633 | + return self.run_in_container(apt_command_prefix(verbosity) + |
634 | + extra_apt_args + " install --no-install-recommends '" + package_name + "'") == 0 |
635 | |
636 | |
637 | class LibertineMock(BaseContainer): |
638 | @@ -511,8 +195,10 @@ |
639 | """ |
640 | super().__init__() |
641 | if container_type == "lxc": |
642 | + from libertine.LxcContainer import LibertineLXC |
643 | self.container = LibertineLXC(container_id) |
644 | elif container_type == "chroot": |
645 | + from libertine.ChrootContainer import LibertineChroot |
646 | self.container = LibertineChroot(container_id) |
647 | elif container_type == "mock": |
648 | self.container = LibertineMock(container_id) |
649 | |
650 | === added file 'python/libertine/LxcContainer.py' |
651 | --- python/libertine/LxcContainer.py 1970-01-01 00:00:00 +0000 |
652 | +++ python/libertine/LxcContainer.py 2015-11-30 19:37:49 +0000 |
653 | @@ -0,0 +1,199 @@ |
654 | +# Copyright 2015 Canonical Ltd. |
655 | +# |
656 | +# This program is free software: you can redistribute it and/or modify it |
657 | +# under the terms of the GNU General Public License version 3, as published |
658 | +# by the Free Software Foundation. |
659 | +# |
660 | +# This program is distributed in the hope that it will be useful, but |
661 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
662 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
663 | +# PURPOSE. See the GNU General Public License for more details. |
664 | +# |
665 | +# You should have received a copy of the GNU General Public License along |
666 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
667 | + |
668 | +import crypt |
669 | +import lxc |
670 | +import os |
671 | +import shlex |
672 | +import subprocess |
673 | +from .Libertine import ( |
674 | + BaseContainer, get_container_distro, get_host_architecture, |
675 | + create_libertine_user_data_dir, create_compiz_config) |
676 | +from . import utils |
677 | + |
678 | + |
679 | +home_path = os.environ['HOME'] |
680 | + |
681 | + |
682 | +def check_lxc_net_entry(entry): |
683 | + lxc_net_file = open('/etc/lxc/lxc-usernet') |
684 | + found = False |
685 | + |
686 | + for line in lxc_net_file: |
687 | + if entry in line: |
688 | + found = True |
689 | + break |
690 | + |
691 | + return found |
692 | + |
693 | + |
694 | +def setup_host_environment(username, password): |
695 | + lxc_net_entry = "%s veth lxcbr0 10" % str(username) |
696 | + |
697 | + if not check_lxc_net_entry(lxc_net_entry): |
698 | + passwd = subprocess.Popen(["sudo", "--stdin", "usermod", "--add-subuids", "100000-165536", |
699 | + "--add-subgids", "100000-165536", str(username)], |
700 | + stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, |
701 | + stderr=subprocess.STDOUT) |
702 | + passwd.communicate((password + '\n').encode('UTF-8')) |
703 | + |
704 | + add_user_cmd = "echo %s | sudo tee -a /etc/lxc/lxc-usernet > /dev/null" % lxc_net_entry |
705 | + subprocess.Popen(add_user_cmd, shell=True) |
706 | + |
707 | + |
708 | +def get_lxc_default_config_path(): |
709 | + return os.path.join(home_path, '.config', 'lxc') |
710 | + |
711 | + |
712 | +def lxc_container(container_id): |
713 | + config_path = utils.get_libertine_containers_dir_path() |
714 | + container = lxc.Container(container_id, config_path) |
715 | + |
716 | + return container |
717 | + |
718 | + |
719 | +class LibertineLXC(BaseContainer): |
720 | + """ |
721 | + A concrete container type implemented using an LXC container. |
722 | + """ |
723 | + |
724 | + def __init__(self, container_id): |
725 | + super().__init__(container_id) |
726 | + self.container = lxc_container(container_id) |
727 | + self.series = get_container_distro(container_id) |
728 | + |
729 | + def is_running(self): |
730 | + return self.container.running |
731 | + |
732 | + def start_container(self): |
733 | + if not self.container.running: |
734 | + if not self.container.start(): |
735 | + raise RuntimeError("Container failed to start") |
736 | + if not self.container.wait("RUNNING", 10): |
737 | + raise RuntimeError("Container failed to enter the RUNNING state") |
738 | + |
739 | + if not self.container.get_ips(timeout=30): |
740 | + raise RuntimeError("Not able to connect to the network.") |
741 | + |
742 | + self.run_in_container("umount /tmp/.X11-unix") |
743 | + |
744 | + def stop_container(self): |
745 | + self.container.stop() |
746 | + |
747 | + def run_in_container(self, command_string): |
748 | + cmd_args = shlex.split(command_string) |
749 | + return self.container.attach_wait(lxc.attach_run_command, cmd_args) |
750 | + |
751 | + def destroy_libertine_container(self): |
752 | + if self.container.defined: |
753 | + self.container.stop() |
754 | + self.container.destroy() |
755 | + |
756 | + def create_libertine_container(self, password=None, verbosity=1): |
757 | + if password is None: |
758 | + return |
759 | + |
760 | + installed_release = self.series |
761 | + |
762 | + username = os.environ['USER'] |
763 | + user_id = os.getuid() |
764 | + group_id = os.getgid() |
765 | + |
766 | + setup_host_environment(username, password) |
767 | + |
768 | + # Generate the default lxc default config, if it doesn't exist |
769 | + config_path = get_lxc_default_config_path() |
770 | + config_file = "%s/default.conf" % config_path |
771 | + |
772 | + if not os.path.exists(config_path): |
773 | + os.mkdir(config_path) |
774 | + |
775 | + if not os.path.exists(config_file): |
776 | + with open(config_file, "w+") as fd: |
777 | + fd.write("lxc.network.type = veth\n") |
778 | + fd.write("lxc.network.link = lxcbr0\n") |
779 | + fd.write("lxc.network.flags = up\n") |
780 | + fd.write("lxc.network.hwaddr = 00:16:3e:xx:xx:xx\n") |
781 | + fd.write("lxc.id_map = u 0 100000 %s\n" % user_id) |
782 | + fd.write("lxc.id_map = g 0 100000 %s\n" % group_id) |
783 | + fd.write("lxc.id_map = u %s %s 1\n" % (user_id, user_id)) |
784 | + fd.write("lxc.id_map = g %s %s 1\n" % (group_id, group_id)) |
785 | + fd.write("lxc.id_map = u %s %s %s\n" % (user_id + 1, (user_id + 1) + 100000, 65536 - (user_id + 1))) |
786 | + fd.write("lxc.id_map = g %s %s %s\n" % (group_id + 1, (group_id + 1) + 100000, 65536 - (user_id + 1))) |
787 | + |
788 | + create_libertine_user_data_dir(self.container_id) |
789 | + |
790 | + # Figure out the host architecture |
791 | + architecture = get_host_architecture() |
792 | + |
793 | + if not self.container.create("download", 0, |
794 | + {"dist": "ubuntu", |
795 | + "release": installed_release, |
796 | + "arch": architecture}): |
797 | + return False |
798 | + |
799 | + self.create_libertine_config() |
800 | + |
801 | + if verbosity == 1: |
802 | + print("starting container ...") |
803 | + self.start_container() |
804 | + self.run_in_container("userdel -r ubuntu") |
805 | + self.run_in_container("useradd -u {} -p {} -G sudo {}".format( |
806 | + str(user_id), crypt.crypt(password), str(username))) |
807 | + |
808 | + if verbosity == 1: |
809 | + print("Updating the contents of the container after creation...") |
810 | + self.update_packages(verbosity) |
811 | + |
812 | + if verbosity == 1: |
813 | + print("Installing Compiz as the Xmir window manager...") |
814 | + self.install_package('compiz', verbosity=verbosity) |
815 | + create_compiz_config(self.container.name) |
816 | + |
817 | + if verbosity == 1: |
818 | + print("stopping container ...") |
819 | + self.stop_container() |
820 | + |
821 | + def create_libertine_config(self): |
822 | + user_id = os.getuid() |
823 | + home_entry = ( |
824 | + "%s %s none bind,create=dir" |
825 | + % (utils.get_libertine_container_userdata_dir_path(self.container_id), |
826 | + home_path.strip('/')) |
827 | + ) |
828 | + |
829 | + # Bind mount the user's home directory |
830 | + self.container.append_config_item("lxc.mount.entry", home_entry) |
831 | + |
832 | + xdg_user_dirs = ['Documents', 'Music', 'Pictures', 'Videos'] |
833 | + |
834 | + for user_dir in xdg_user_dirs: |
835 | + xdg_user_dir_entry = ( |
836 | + "%s/%s %s/%s none bind,create=dir,optional" |
837 | + % (home_path, user_dir, home_path.strip('/'), user_dir) |
838 | + ) |
839 | + self.container.append_config_item("lxc.mount.entry", xdg_user_dir_entry) |
840 | + |
841 | + # Setup the mounts for /run/user/$user_id |
842 | + run_user_entry = "/run/user/%s run/user/%s none rbind,create=dir" % (user_id, user_id) |
843 | + self.container.append_config_item("lxc.mount.entry", "tmpfs run tmpfs rw,nodev,noexec,nosuid,size=5242880") |
844 | + self.container.append_config_item("lxc.mount.entry", |
845 | + "none run/user tmpfs rw,nodev,noexec,nosuid,size=104857600,mode=0755,create=dir") |
846 | + self.container.append_config_item("lxc.mount.entry", run_user_entry) |
847 | + |
848 | + self.container.append_config_item("lxc.include", "/usr/share/libertine/libertine-lxc.conf") |
849 | + |
850 | + # Dump it all to disk |
851 | + self.container.save_config() |
852 | + |
Since libertine- lxc-manager has been merged, we should probably install it in the python3- libertine- lxc package instead of libertine-tools.