Merge lp:~bregma/libertine/refactor-container-back-ends into lp:libertine

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
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-installable packages

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 :

Since libertine-lxc-manager has been merged, we should probably install it in the python3-libertine-lxc package instead of libertine-tools.

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+

Subscribers

People subscribed via source and target branches