Merge lp:~townsend/libertine/1.5.1-release into lp:libertine/trunk
- 1.5.1-release
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Larry Price |
Approved revision: | 184 |
Merged at revision: | 182 |
Proposed branch: | lp:~townsend/libertine/1.5.1-release |
Merge into: | lp:libertine/trunk |
Diff against target: |
927 lines (+276/-122) 17 files modified
CMakeLists.txt (+1/-1) data/libertine-lxc.conf (+0/-1) debian/changelog (+27/-0) liblibertine/libertine.cpp (+2/-18) python/libertine/ChrootContainer.py (+8/-13) python/libertine/ContainersConfig.py (+6/-0) python/libertine/HostInfo.py (+12/-0) python/libertine/Libertine.py (+82/-13) python/libertine/LxcContainer.py (+7/-8) python/libertine/LxdContainer.py (+95/-41) python/libertine/lifecycle/ContainerLifecycleService.py (+7/-7) python/libertine/utils.py (+3/-11) snapcraft.yaml (+1/-1) tools/libertine-container-manager (+21/-4) tools/libertine-container-manager.1 (+1/-1) tools/libertine-lxc-manager (+1/-1) tools/libertine-lxd-manager (+2/-2) |
To merge this branch: | bzr merge lp:~townsend/libertine/1.5.1-release |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Larry Price | Approve | ||
Review via email:
|
Commit message
* Remove the /tmp/.X11-unix umount during LXC container start for operations as it seems it's not needed. (LP: #1654650)
* Remove extra finish_
* Set container's locale and language based on the host including installing necessary language packs. (LP: #1609982)
* Bump version to 1.5.1.
* Manually execute lxd bind mount script to fix /run/user and remove service. (LP: #1654647)
* Convert results to dicts on operation/
* Stop bind-mounting /usr/lib/locale and let environment do all the work. (LP: #1654648)
* Mount lxd home directory in $HOME/.
* Manually wait for lxd container to stop when specified after calling the service. (LP: #1655980)
* Ask for container user password when creating lxd containers. (LP: #1655977)
* Bind-mount lxd container applications and icons directories into user's home directory.
Description of the change
- 183. By Christopher Townsend
-
Merge lp:libertine for fix on starting the window manager.
- 184. By Christopher Townsend
-
Merge lp:libertine for fix to remove bind-mount rootfs directories in LXD.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2017-01-05 18:29:51 +0000 |
3 | +++ CMakeLists.txt 2017-01-18 14:37:20 +0000 |
4 | @@ -2,7 +2,7 @@ |
5 | cmake_policy(SET CMP0048 NEW) |
6 | |
7 | project(libertine |
8 | - VERSION 1.5) |
9 | + VERSION 1.5.1) |
10 | |
11 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") |
12 | |
13 | |
14 | === modified file 'data/libertine-lxc.conf' |
15 | --- data/libertine-lxc.conf 2016-03-21 19:26:22 +0000 |
16 | +++ data/libertine-lxc.conf 2017-01-18 14:37:20 +0000 |
17 | @@ -3,5 +3,4 @@ |
18 | lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir |
19 | lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file |
20 | lxc.mount.entry = /dev/fuse dev/fuse none bind,optional,create=file |
21 | -lxc.mount.entry = /usr/lib/locale usr/lib/locale none optional,bind |
22 | lxc.tty = 0 |
23 | |
24 | === modified file 'debian/changelog' |
25 | --- debian/changelog 2017-01-05 20:20:33 +0000 |
26 | +++ debian/changelog 2017-01-18 14:37:20 +0000 |
27 | @@ -1,3 +1,30 @@ |
28 | +libertine (1.5.1-0ubuntu1) UNRELEASED; urgency=medium |
29 | + |
30 | + [ Chris Townsend ] |
31 | + * Remove the /tmp/.X11-unix umount during LXC container start for operations |
32 | + as it seems it's not needed. (LP: #1654650) |
33 | + * Remove extra finish_application() in chroot backend. |
34 | + * Set container's locale and language based on the host including installing |
35 | + necessary language packs. (LP: #1609982) |
36 | + * Bump version to 1.5.1. |
37 | + |
38 | + [ Larry Price ] |
39 | + * Manually execute lxd bind mount script to fix /run/user and remove |
40 | + service. (LP: #1654647) |
41 | + * Convert results to dicts on operation/application collision in container |
42 | + lifecycle managers. |
43 | + * Stop bind-mounting /usr/lib/locale and let environment do all the work. |
44 | + (LP: #1654648) |
45 | + * Mount lxd home directory in $HOME/.local/libertine-container where it |
46 | + belongs. |
47 | + * Manually wait for lxd container to stop when specified after calling the |
48 | + service. (LP: #1655980) |
49 | + * Ask for container user password when creating lxd containers. (LP: #1655977) |
50 | + * Bind-mount lxd container applications and icons directories into user's |
51 | + home directory. |
52 | + |
53 | + -- Chris Townsend <christopher.townsend@canonical.com> Fri, 13 Jan 2017 14:59:52 -0500 |
54 | + |
55 | libertine (1.5+17.04.20170105.1-0ubuntu1) zesty; urgency=medium |
56 | |
57 | [ Chris Townsend ] |
58 | |
59 | === modified file 'liblibertine/libertine.cpp' |
60 | --- liblibertine/libertine.cpp 2016-12-07 18:04:08 +0000 |
61 | +++ liblibertine/libertine.cpp 2017-01-18 14:37:20 +0000 |
62 | @@ -148,16 +148,8 @@ |
63 | g_return_val_if_fail(container_id != nullptr, nullptr); |
64 | LibertineConfig config; |
65 | ContainerConfigList container_list(&config); |
66 | - gchar * path = nullptr; |
67 | |
68 | - if (g_strcmp0((gchar*)container_list.getContainerType(container_id).toStdString().c_str(), "lxd") == 0) |
69 | - { |
70 | - path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", nullptr); |
71 | - } |
72 | - else |
73 | - { |
74 | - path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); |
75 | - } |
76 | + gchar * path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); |
77 | |
78 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
79 | { |
80 | @@ -175,16 +167,8 @@ |
81 | g_return_val_if_fail(container_id != nullptr, nullptr); |
82 | LibertineConfig config; |
83 | ContainerConfigList container_list(&config); |
84 | - gchar * path = nullptr; |
85 | |
86 | - if (g_strcmp0((gchar*)container_list.getContainerType(container_id).toStdString().c_str(), "lxd") == 0) |
87 | - { |
88 | - path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", "home", g_get_user_name(), nullptr); |
89 | - } |
90 | - else |
91 | - { |
92 | - path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); |
93 | - } |
94 | + gchar * path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); |
95 | |
96 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
97 | { |
98 | |
99 | === modified file 'python/libertine/ChrootContainer.py' |
100 | --- python/libertine/ChrootContainer.py 2017-01-04 20:22:33 +0000 |
101 | +++ python/libertine/ChrootContainer.py 2017-01-18 14:37:20 +0000 |
102 | @@ -66,13 +66,7 @@ |
103 | return cmd.wait() |
104 | |
105 | def destroy_libertine_container(self): |
106 | - container_root = os.path.join(utils.get_libertine_containers_dir_path(), self.container_id) |
107 | - try: |
108 | - shutil.rmtree(container_root) |
109 | - return True |
110 | - except Exception as e: |
111 | - utils.get_logger().error("%s" % e) |
112 | - return False |
113 | + return self._delete_rootfs() |
114 | |
115 | def create_libertine_container(self, password=None, multiarch=False): |
116 | # Create the actual chroot |
117 | @@ -120,6 +114,8 @@ |
118 | |
119 | utils.create_libertine_user_data_dir(self.container_id) |
120 | |
121 | + self.update_locale() |
122 | + |
123 | if multiarch and self.architecture == 'amd64': |
124 | utils.get_logger().info("Adding i386 multiarch support...") |
125 | self.run_in_container("dpkg --add-architecture i386") |
126 | @@ -146,10 +142,12 @@ |
127 | # Check if the container was created as root and chown the user directories as necessary |
128 | chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id)) |
129 | |
130 | + super().create_libertine_container() |
131 | + |
132 | return True |
133 | |
134 | - def update_packages(self): |
135 | - retcode = super().update_packages() |
136 | + def update_packages(self, new_locale=None): |
137 | + retcode = super().update_packages(new_locale) |
138 | self._run_ldconfig() |
139 | return retcode == 0 |
140 | |
141 | @@ -229,7 +227,7 @@ |
142 | proot_cmd = self._build_proot_command() |
143 | |
144 | args = shlex.split(proot_cmd) |
145 | - args.extend(utils.setup_window_manager(self.container_id, enable_toolbars=True)) |
146 | + args.extend(self.setup_window_manager(enable_toolbars=True)) |
147 | self._window_manager = psutil.Popen(args, env=environ) |
148 | |
149 | args = shlex.split(proot_cmd) |
150 | @@ -248,6 +246,3 @@ |
151 | args = shlex.split(command_line) |
152 | app = subprocess.Popen(args) |
153 | return app |
154 | - |
155 | - def finish_application(self, app): |
156 | - app.wait() |
157 | |
158 | === modified file 'python/libertine/ContainersConfig.py' |
159 | --- python/libertine/ContainersConfig.py 2016-12-07 21:50:36 +0000 |
160 | +++ python/libertine/ContainersConfig.py 2017-01-18 14:37:20 +0000 |
161 | @@ -296,6 +296,12 @@ |
162 | else: |
163 | return multiarch_support |
164 | |
165 | + def update_container_locale(self, container_id, locale): |
166 | + self._set_value_by_key(container_id, 'locale', locale) |
167 | + |
168 | + def get_container_locale(self, container_id): |
169 | + return self._get_value_by_key(container_id, 'locale') |
170 | + |
171 | """ |
172 | Operations for archive (PPA) maintenance in a Libertine container. |
173 | """ |
174 | |
175 | === modified file 'python/libertine/HostInfo.py' |
176 | --- python/libertine/HostInfo.py 2016-11-21 17:18:55 +0000 |
177 | +++ python/libertine/HostInfo.py 2017-01-18 14:37:20 +0000 |
178 | @@ -12,6 +12,7 @@ |
179 | # You should have received a copy of the GNU General Public License along |
180 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
181 | |
182 | +import locale |
183 | import lsb_release |
184 | import os |
185 | import platform |
186 | @@ -74,3 +75,14 @@ |
187 | def get_host_timezone(self): |
188 | with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: |
189 | return fd.read().strip('\n') |
190 | + |
191 | + def get_host_locale(self): |
192 | + host_locale = locale.getlocale() |
193 | + full_locale = None |
194 | + |
195 | + if len(host_locale) == 2: |
196 | + full_locale = "{}.{}".format(host_locale[0], host_locale[1]) |
197 | + elif len(host_locale) == 1: |
198 | + full_locale = "{}".format(host_locale[0]) |
199 | + |
200 | + return full_locale |
201 | |
202 | === modified file 'python/libertine/Libertine.py' |
203 | --- python/libertine/Libertine.py 2017-01-03 17:51:30 +0000 |
204 | +++ python/libertine/Libertine.py 2017-01-18 14:37:20 +0000 |
205 | @@ -16,7 +16,6 @@ |
206 | from gi.repository import Libertine |
207 | import abc |
208 | import contextlib |
209 | -import libertine.utils |
210 | import os |
211 | import shutil |
212 | |
213 | @@ -82,10 +81,15 @@ |
214 | |
215 | :param container_id: The machine-readable container name. |
216 | """ |
217 | - def __init__(self, container_id): |
218 | + def __init__(self, container_id, containers_config=None): |
219 | + if containers_config is None: |
220 | + containers_config = ContainersConfig() |
221 | + |
222 | self.container_type = 'unknown' |
223 | self.container_id = container_id |
224 | - self.root_path = libertine.utils.get_libertine_container_rootfs_path(self.container_id) |
225 | + self.root_path = utils.get_libertine_container_rootfs_path(self.container_id) |
226 | + self.locale = containers_config.get_container_locale(container_id) |
227 | + self.language = self._get_language_from_locale() |
228 | self.default_packages = ['matchbox-window-manager', |
229 | 'libnss-extrausers', |
230 | 'humanity-icon-theme', |
231 | @@ -93,8 +97,62 @@ |
232 | 'maliit-inputcontext-gtk3', |
233 | 'maliit-framework'] |
234 | |
235 | + def _get_language_from_locale(self): |
236 | + language = None |
237 | + |
238 | + if self.locale is not None: |
239 | + language = self.locale.split('.')[0] |
240 | + if not language.startswith('zh_'): |
241 | + language = language.split('_')[0] |
242 | + elif language.startswith('zh_CN'): |
243 | + language = 'zh-hans' |
244 | + else: |
245 | + language = 'zh-hant' |
246 | + |
247 | + return language |
248 | + |
249 | + def _binary_exists(self, binary): |
250 | + return self.run_in_container("bash -c \"which {} &> /dev/null\"".format(binary)) == 0 |
251 | + |
252 | + def _delete_rootfs(self): |
253 | + container_root = os.path.join(utils.get_libertine_containers_dir_path(), self.container_id) |
254 | + if not os.path.exists(container_root): |
255 | + return True |
256 | + |
257 | + try: |
258 | + shutil.rmtree(container_root) |
259 | + return True |
260 | + except Exception as e: |
261 | + utils.get_logger().error("%s" % e) |
262 | + return False |
263 | + |
264 | + def setup_window_manager(self, enable_toolbars=False): |
265 | + if self._binary_exists('matchbox-window-manager'): |
266 | + if enable_toolbars: |
267 | + return ['matchbox-window-manager'] |
268 | + |
269 | + return ['matchbox-window-manager', '-use_titlebar', 'no'] |
270 | + else: |
271 | + return ['compiz'] |
272 | + |
273 | + |
274 | + def check_language_support(self): |
275 | + if not self._binary_exists('check-language-support'): |
276 | + self.install_package('language-selector-common', update_cache=False) |
277 | + |
278 | + self.run_in_container("bash -c \"{} install $(check-language-support -l {})\"".format(_apt_command_prefix(), self.language)) |
279 | + |
280 | + def update_locale(self): |
281 | + self.run_in_container("locale-gen {}".format(self.locale)) |
282 | + |
283 | + def install_base_language_packs(self): |
284 | + base_language_packs = ['language-pack-{}', 'language-pack-gnome-{}'] |
285 | + |
286 | + for language_pack in [p.format(self.language) for p in base_language_packs]: |
287 | + self.install_package(language_pack, update_cache=False) |
288 | + |
289 | def create_libertine_container(self, password=None, multiarch=False): |
290 | - pass |
291 | + self.install_base_language_packs() |
292 | |
293 | def destroy_libertine_container(self): |
294 | pass |
295 | @@ -150,11 +208,18 @@ |
296 | """ |
297 | return self.run_in_container(_apt_command_prefix() + 'update') |
298 | |
299 | - def update_packages(self): |
300 | + def update_packages(self, new_locale=None): |
301 | """ |
302 | Updates all packages installed in the container. |
303 | """ |
304 | self.update_apt_cache() |
305 | + |
306 | + if new_locale: |
307 | + self.locale = new_locale |
308 | + self.language = self._get_language_from_locale() |
309 | + self.update_locale() |
310 | + self.install_base_language_packs() |
311 | + |
312 | return self.run_in_container(_apt_command_prefix() + '--force-yes dist-upgrade') == 0 |
313 | |
314 | def install_package(self, package_name, no_dialog=False, update_cache=True): |
315 | @@ -185,7 +250,11 @@ |
316 | else: |
317 | if no_dialog: |
318 | os.environ['DEBIAN_FRONTEND'] = 'teletype' |
319 | - return self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0 |
320 | + ret = self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0 |
321 | + |
322 | + self.check_language_support() |
323 | + |
324 | + return ret |
325 | |
326 | def remove_package(self, package_name): |
327 | """ |
328 | @@ -219,7 +288,7 @@ |
329 | :param archive: The configuration command to run. |
330 | :param public_key_file: file containing the public key used to sign this archive |
331 | """ |
332 | - if not os.path.exists(os.path.join(self.root_path, 'usr', 'bin', 'add-apt-repository')): |
333 | + if not self._binary_exists('add-apt-repository'): |
334 | self.install_package("software-properties-common") |
335 | if 'https://' in archive and not os.path.exists(os.path.join(self.root_path, 'usr', 'lib', 'apt', 'methods', 'https')): |
336 | self.install_package("apt-transport-https") |
337 | @@ -254,8 +323,8 @@ |
338 | """ |
339 | A concrete mock container type. Used for unit testing. |
340 | """ |
341 | - def __init__(self, container_id): |
342 | - super().__init__(container_id) |
343 | + def __init__(self, container_id, containers_config=None): |
344 | + super().__init__(container_id, containers_config) |
345 | self.container_type = "mock" |
346 | |
347 | def create_libertine_container(self, password=None, multiarch=False): |
348 | @@ -264,7 +333,7 @@ |
349 | def destroy_libertine_container(self): |
350 | return True |
351 | |
352 | - def update_packages(self): |
353 | + def update_packages(self, new_locale=None): |
354 | return True |
355 | |
356 | def install_package(self, package_name, no_dialog=False, update_cache=True): |
357 | @@ -328,7 +397,7 @@ |
358 | from libertine.ChrootContainer import LibertineChroot |
359 | self.container = LibertineChroot(container_id) |
360 | elif container_type == "mock": |
361 | - self.container = LibertineMock(container_id) |
362 | + self.container = LibertineMock(container_id, self.containers_config) |
363 | else: |
364 | raise RuntimeError("Unsupported container type %s" % container_type) |
365 | |
366 | @@ -363,13 +432,13 @@ |
367 | |
368 | return self.container.create_libertine_container(password, multiarch) |
369 | |
370 | - def update_libertine_container(self): |
371 | + def update_libertine_container(self, new_locale=None): |
372 | """ |
373 | Updates the contents of the container. |
374 | """ |
375 | try: |
376 | with ContainerRunning(self.container): |
377 | - return self.container.update_packages() |
378 | + return self.container.update_packages(new_locale) |
379 | except RuntimeError as e: |
380 | return handle_runtime_error(e) |
381 | |
382 | |
383 | === modified file 'python/libertine/LxcContainer.py' |
384 | --- python/libertine/LxcContainer.py 2017-01-05 15:25:21 +0000 |
385 | +++ python/libertine/LxcContainer.py 2017-01-18 14:37:20 +0000 |
386 | @@ -179,11 +179,6 @@ |
387 | _dump_lxc_log(result.logfile) |
388 | raise RuntimeError(result.error) |
389 | |
390 | - if self.run_in_container("mountpoint -q /tmp/.X11-unix") == 0: |
391 | - self.run_in_container("umount /tmp/.X11-unix") |
392 | - if self.run_in_container("mountpoint -q /usr/lib/locale") == 0: |
393 | - self.run_in_container("umount -l /usr/lib/locale") |
394 | - |
395 | def stop_container(self): |
396 | if self.lxc_manager_interface: |
397 | self.lxc_manager_interface.operation_stop(self.container_id) |
398 | @@ -194,14 +189,14 @@ |
399 | cmd_args = shlex.split(command_string) |
400 | return self.container.attach_wait(lxc.attach_run_command, cmd_args) |
401 | |
402 | - def update_packages(self): |
403 | + def update_packages(self, update_locale=False): |
404 | if self.timezone_needs_update(): |
405 | self.run_in_container("bash -c \'echo \"{}\" >/etc/timezone\'".format( |
406 | self.host_info.get_host_timezone())) |
407 | self.run_in_container("rm -f /etc/localtime") |
408 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
409 | |
410 | - return super().update_packages() |
411 | + return super().update_packages(update_locale) |
412 | |
413 | def destroy_libertine_container(self): |
414 | if not self.container.defined: |
415 | @@ -265,6 +260,8 @@ |
416 | self.destroy_libertine_container() |
417 | return False |
418 | |
419 | + self.update_locale() |
420 | + |
421 | self.run_in_container("userdel -r ubuntu") |
422 | self.run_in_container("useradd -u {} -p {} -G sudo {}".format( |
423 | str(user_id), crypt.crypt(password), str(username))) |
424 | @@ -282,6 +279,8 @@ |
425 | self.destroy_libertine_container() |
426 | return False |
427 | |
428 | + super().create_libertine_container() |
429 | + |
430 | utils.get_logger().info("stopping container ...") |
431 | self.stop_container() |
432 | |
433 | @@ -334,7 +333,7 @@ |
434 | return |
435 | |
436 | self.window_manager = self.container.attach(lxc.attach_run_command, |
437 | - utils.setup_window_manager(self.container_id)) |
438 | + self.setup_window_manager()) |
439 | |
440 | # Setup pulse to work inside the container |
441 | os.environ['PULSE_SERVER'] = utils.get_libertine_lxc_pulse_socket_path() |
442 | |
443 | === modified file 'python/libertine/LxdContainer.py' |
444 | --- python/libertine/LxdContainer.py 2017-01-05 20:16:25 +0000 |
445 | +++ python/libertine/LxdContainer.py 2017-01-18 14:37:20 +0000 |
446 | @@ -12,6 +12,7 @@ |
447 | # You should have received a copy of the GNU General Public License along |
448 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
449 | |
450 | +import contextlib |
451 | import crypt |
452 | import dbus |
453 | import os |
454 | @@ -98,20 +99,6 @@ |
455 | |
456 | |
457 | def _setup_bind_mount_service(container, uid, username): |
458 | - utils.get_logger().info("Creating systemd mount override service") |
459 | - service = ''' |
460 | -[Unit] |
461 | -Description=Fix system mounts for libertine |
462 | - |
463 | -[Service] |
464 | -ExecStart=/usr/bin/libertine-lxd-mount-update |
465 | - |
466 | -[Install] |
467 | -WantedBy=multi-user.target |
468 | -'''[1:-1] |
469 | - container.files.put('/etc/systemd/system/libertine-lxd-mount-update.service', service.encode('utf-8')) |
470 | - container.execute(shlex.split('chmod 644 /etc/systemd/system/libertine-lxd-mount-update.service')) |
471 | - |
472 | utils.get_logger().info("Creating mount update shell script") |
473 | script = ''' |
474 | #!/bin/sh |
475 | @@ -128,9 +115,6 @@ |
476 | container.files.put('/usr/bin/libertine-lxd-mount-update', script.format(uid=uid, username=username).encode('utf-8')) |
477 | container.execute(shlex.split('chmod 755 /usr/bin/libertine-lxd-mount-update')) |
478 | |
479 | - utils.get_logger().info("Enabling systemd mount update service") |
480 | - container.execute(shlex.split('systemctl enable libertine-lxd-mount-update.service')) |
481 | - |
482 | |
483 | def lxd_container(client, container_id): |
484 | try: |
485 | @@ -174,11 +158,72 @@ |
486 | return LifecycleResult() |
487 | |
488 | |
489 | +def _lxd_save(entity, error, wait=True): |
490 | + try: |
491 | + entity.save(wait=wait) |
492 | + except pylxd.exceptions.LXDAPIException as e: |
493 | + utils.get_logger().warning('{} {}'.format(error, str(e))) |
494 | + |
495 | + |
496 | +_CONTAINER_DATA_DIRS = ["/usr/share/applications", "/usr/share/icons", "/usr/local/share/applications", "/usr/share/pixmaps"] |
497 | + |
498 | + |
499 | +class RestoreDevice(contextlib.ExitStack): |
500 | + def __init__(self, container, device): |
501 | + super().__init__() |
502 | + self._container = container |
503 | + self._device = device |
504 | + self._source = None |
505 | + self.remove() |
506 | + self.callback(self.restore) |
507 | + |
508 | + def remove(self): |
509 | + if self._device in self._container.devices: |
510 | + self._source = self._container.devices[self._device] |
511 | + del self._container.devices[self._device] |
512 | + _lxd_save(self._container, 'Saving bind mounts for container \'{}\' raised:'.format(self._container.name)) |
513 | + |
514 | + def restore(self): |
515 | + if self._source is not None: |
516 | + self._container.devices[self._device] = self._source |
517 | + _lxd_save(self._container, 'Saving bind mounts for container \'{}\' raised:'.format(self._container.name), False) |
518 | + |
519 | + |
520 | +def _sync_application_dirs_to_host(container): |
521 | + host_root = utils.get_libertine_container_rootfs_path(container.name) |
522 | + for container_path in _CONTAINER_DATA_DIRS: |
523 | + utils.get_logger().info("Syncing applications directory: {}".format(container_path)) |
524 | + os.makedirs(os.path.join(host_root, container_path.lstrip("/")), exist_ok=True) |
525 | + with RestoreDevice(container, container_path): |
526 | + # find a list of files within the container |
527 | + find = subprocess.Popen(shlex.split("lxc exec {} -- find {} -type f".format(container.name, container_path)), stdout=subprocess.PIPE) |
528 | + stdout, stderr = find.communicate() |
529 | + |
530 | + if find.returncode == 0: |
531 | + for filepath in stdout.decode('utf-8').strip().split('\n'): |
532 | + if filepath: |
533 | + host_path = os.path.join(host_root, filepath.lstrip("/")) |
534 | + if not os.path.exists(host_path): |
535 | + utils.get_logger().debug("Syncing file: {}:{}".format(filepath, host_path)) |
536 | + os.makedirs(os.path.dirname(host_path), exist_ok=True) |
537 | + with open(host_path, 'wb') as f: |
538 | + f.write(container.files.get(filepath)) |
539 | + |
540 | + |
541 | def update_bind_mounts(container, config): |
542 | home_path = os.environ['HOME'] |
543 | + userdata_dir = utils.get_libertine_container_userdata_dir_path(container.name) |
544 | |
545 | container.devices.clear() |
546 | container.devices['root'] = {'type': 'disk', 'path': '/'} |
547 | + container.devices['home'] = {'type': 'disk', 'path': home_path, 'source': userdata_dir} |
548 | + |
549 | + # applications and icons directories |
550 | + rootfs_path = utils.get_libertine_container_rootfs_path(container.name) |
551 | + for data_dir in _CONTAINER_DATA_DIRS: |
552 | + host_path = "{}{}".format(rootfs_path, data_dir) |
553 | + os.makedirs(host_path, exist_ok=True) |
554 | + container.devices[data_dir] = {'type': 'disk', 'path': data_dir, 'source': host_path} |
555 | |
556 | if os.path.exists(os.path.join(home_path, '.config', 'dconf')): |
557 | container.devices['dconf'] = { |
558 | @@ -201,6 +246,8 @@ |
559 | path = user_dir[1] |
560 | else: |
561 | path = os.path.join(home_path, user_dir[1]) |
562 | + hostpath = os.path.join(userdata_dir, user_dir[1]) |
563 | + os.makedirs(hostpath, exist_ok=True) |
564 | |
565 | utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container.name)) |
566 | |
567 | @@ -211,11 +258,7 @@ |
568 | 'type': 'disk' |
569 | } |
570 | |
571 | - try: |
572 | - container.save(wait=True) |
573 | - except pylxd.exceptions.LXDAPIException as e: |
574 | - utils.get_logger().warning('Saving bind mounts for container \'{}\' raised: {}'.format(container.name, str(e))) |
575 | - # This is most likely the result of the container currently running |
576 | + _lxd_save(container, 'Saving bind mounts for container \'{}\' raised:'.format(container.name)) |
577 | |
578 | |
579 | def update_libertine_profile(client): |
580 | @@ -226,12 +269,7 @@ |
581 | profile.devices = _get_devices_map() |
582 | profile.config['raw.idmap'] = 'both 1000 1000' |
583 | |
584 | - try: |
585 | - profile.save() |
586 | - except pylxd.exceptions.LXDAPIException as e: |
587 | - utils.get_logger().warning('Saving libertine lxd profile raised: {}'.format(str(e))) |
588 | - # This is most likely the result of an older container currently |
589 | - # running and/or containing a conflicting device entry |
590 | + _lxd_save(profile, 'Saving libertine lxd profile raised:') |
591 | except pylxd.exceptions.LXDAPIException: |
592 | utils.get_logger().info('Creating libertine lxd profile.') |
593 | client.profiles.create('libertine', config={'raw.idmap': 'both 1000 1000'}, devices=_get_devices_map()) |
594 | @@ -251,7 +289,6 @@ |
595 | |
596 | self._client = pylxd.Client() |
597 | self._window_manager = None |
598 | - self.root_path = '{}/containers/{}/rootfs'.format(os.getenv('LXD_DIR', '/var/lib/lxd'), name) |
599 | |
600 | utils.set_session_dbus_env_var() |
601 | try: |
602 | @@ -277,19 +314,22 @@ |
603 | utils.get_logger().error("Creating container '{}' failed with code '{}'".format(self._id, create.returncode)) |
604 | return False |
605 | |
606 | - if not self.start_container(): |
607 | - utils.get_logger().error("Failed to start container '{}'".format(self._id)) |
608 | - self.destroy_libertine_container() |
609 | - return False |
610 | + self._try_get_container() |
611 | + _sync_application_dirs_to_host(self._container) |
612 | + update_bind_mounts(self._container, self._config) |
613 | + |
614 | + self.update_locale() |
615 | |
616 | username = os.environ['USER'] |
617 | uid = str(os.getuid()) |
618 | self.run_in_container("userdel -r ubuntu") |
619 | self.run_in_container("useradd -u {} -U -p {} -G sudo,audio,video {}".format( |
620 | - uid, crypt.crypt(''), username)) |
621 | + uid, crypt.crypt(password or ''), username)) |
622 | self.run_in_container("mkdir -p /home/{}".format(username)) |
623 | self.run_in_container("chown {0}:{0} /home/{0}".format(username)) |
624 | |
625 | + utils.create_libertine_user_data_dir(self._id) |
626 | + |
627 | _setup_bind_mount_service(self._container, uid, username) |
628 | |
629 | if multiarch and self.architecture == 'amd64': |
630 | @@ -305,25 +345,34 @@ |
631 | self.destroy_libertine_container() |
632 | return False |
633 | |
634 | + super().create_libertine_container() |
635 | + |
636 | return True |
637 | |
638 | - def update_packages(self): |
639 | + def update_packages(self, update_locale=False): |
640 | if not self._timezone_in_sync(): |
641 | utils.get_logger().info("Re-syncing timezones") |
642 | self.run_in_container("bash -c 'echo \"%s\" > /etc/timezone'" % self._host_info.get_host_timezone()) |
643 | self.run_in_container("rm -f /etc/localtime") |
644 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
645 | |
646 | - return super().update_packages() |
647 | + _sync_application_dirs_to_host(self._container) |
648 | + update_bind_mounts(self._container, self._config) |
649 | + |
650 | + return super().update_packages(update_locale) |
651 | |
652 | def destroy_libertine_container(self): |
653 | if not self._try_get_container(): |
654 | utils.get_logger().error("No such container '%s'" % self._id) |
655 | return False |
656 | |
657 | - self.stop_container(wait=True) |
658 | + if not self.stop_container(wait=True): |
659 | + utils.get_logger().error("Canceling destruction due to running container") |
660 | + return False |
661 | + |
662 | self._container.delete() |
663 | - return True |
664 | + |
665 | + return self._delete_rootfs() |
666 | |
667 | def _timezone_in_sync(self): |
668 | proc = subprocess.Popen(self._lxc_args('cat /etc/timezone'), stdout=subprocess.PIPE) |
669 | @@ -366,7 +415,7 @@ |
670 | return False |
671 | |
672 | if self._manager: |
673 | - result = LifecycleResult.from_dict(self._manager.operation_stop(self._id)) |
674 | + result = LifecycleResult.from_dict(self._manager.operation_stop(self._id, {'wait': wait})) |
675 | else: |
676 | result = lxd_stop(self._container, wait) |
677 | |
678 | @@ -382,7 +431,7 @@ |
679 | |
680 | # FIXME: Remove once window management logic has been moved to the host |
681 | def _start_window_manager(self, args): |
682 | - args.extend(utils.setup_window_manager(self._id)) |
683 | + args.extend(self.setup_window_manager()) |
684 | |
685 | if 'matchbox-window-manager' in args: |
686 | pids = self._get_matchbox_pids() |
687 | @@ -404,6 +453,8 @@ |
688 | utils.get_logger().error("Could not get container '{}'".format(self._id)) |
689 | return None |
690 | |
691 | + requires_remount = self._container.status != 'Running' |
692 | + |
693 | if self._manager: |
694 | result = LifecycleResult.from_dict(self._manager.app_start(self._id)) |
695 | else: |
696 | @@ -415,7 +466,10 @@ |
697 | utils.get_logger().error(result.error) |
698 | return False |
699 | |
700 | - args = self._lxc_args("sudo -E -u {} env PATH={}".format(os.environ['USER'], environ['PATH']), environ) |
701 | + if requires_remount: |
702 | + self._container.execute(shlex.split('/usr/bin/libertine-lxd-mount-update')) |
703 | + |
704 | + args = self._lxc_args("sudo -E -u {} env PATH={}".format(environ['USER'], environ['PATH']), environ) |
705 | |
706 | self._start_window_manager(args.copy()) |
707 | |
708 | |
709 | === modified file 'python/libertine/lifecycle/ContainerLifecycleService.py' |
710 | --- python/libertine/lifecycle/ContainerLifecycleService.py 2016-12-23 15:46:44 +0000 |
711 | +++ python/libertine/lifecycle/ContainerLifecycleService.py 2017-01-18 14:37:20 +0000 |
712 | @@ -46,7 +46,7 @@ |
713 | def start(self, container): |
714 | raise NotImplementedError("Subclasses must implement start(container)") |
715 | |
716 | - def stop(self, container): |
717 | + def stop(self, container, options={}): |
718 | raise NotImplementedError("Subclasses must implement stop(container)") |
719 | |
720 | @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, |
721 | @@ -55,7 +55,7 @@ |
722 | def app_start(self, container): |
723 | utils.get_logger().debug("app_start({})".format(container)) |
724 | if self._operations[container] != 0: |
725 | - return LifecycleResult("Libertine container operation already running: cannot launch application.") |
726 | + return LifecycleResult("Libertine container operation already running: cannot launch application.").to_dict() |
727 | |
728 | result = self.start(container, True) |
729 | |
730 | @@ -84,7 +84,7 @@ |
731 | def operation_start(self, container): |
732 | utils.get_logger().debug("operation_start({})".format(container)) |
733 | if self._apps[container] != 0: |
734 | - return LifecycleResult("Application already running in container: cannot run operation.") |
735 | + return LifecycleResult("Application already running in container: cannot run operation.").to_dict() |
736 | |
737 | result = self.start(container, False) |
738 | |
739 | @@ -94,15 +94,15 @@ |
740 | return result.to_dict() |
741 | |
742 | @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, |
743 | - in_signature='s', |
744 | + in_signature='sa{ss}', |
745 | out_signature='a{ss}') |
746 | - def operation_stop(self, container): |
747 | - utils.get_logger().debug("operation_stop({})".format(container)) |
748 | + def operation_stop(self, container, options={}): |
749 | + utils.get_logger().debug("operation_stop({}, {})".format(container, options)) |
750 | self._operations[container] -= 1 |
751 | result = LifecycleResult() |
752 | |
753 | if self._operations[container] == 0: |
754 | - result = self.stop(container) |
755 | + result = self.stop(container, options) |
756 | del self._operations[container] |
757 | |
758 | return result.to_dict() |
759 | |
760 | === modified file 'python/libertine/utils.py' |
761 | --- python/libertine/utils.py 2017-01-05 14:23:21 +0000 |
762 | +++ python/libertine/utils.py 2017-01-18 14:37:20 +0000 |
763 | @@ -103,6 +103,9 @@ |
764 | if path is None: |
765 | path = os.path.join(basedir.xdg_data_home, 'libertine-container', 'user-data', container_id) |
766 | |
767 | + if is_snap_environment(): |
768 | + path = path.replace(os.environ['HOME'], os.getenv('SNAP_USER_COMMON')) |
769 | + |
770 | return path |
771 | |
772 | |
773 | @@ -163,17 +166,6 @@ |
774 | return os.path.join(get_libertine_runtime_dir(), 'pulse_socket') |
775 | |
776 | |
777 | -def setup_window_manager(container_id, enable_toolbars=False): |
778 | - if os.path.exists(os.path.join(get_libertine_container_rootfs_path(container_id), |
779 | - 'usr', 'bin', 'matchbox-window-manager')): |
780 | - if enable_toolbars: |
781 | - return ['matchbox-window-manager'] |
782 | - |
783 | - return ['matchbox-window-manager', '-use_titlebar', 'no'] |
784 | - else: |
785 | - return ['compiz'] |
786 | - |
787 | - |
788 | def terminate_window_manager(window_manager): |
789 | for child in window_manager.children(): |
790 | child.terminate() |
791 | |
792 | === modified file 'snapcraft.yaml' |
793 | --- snapcraft.yaml 2017-01-05 20:20:33 +0000 |
794 | +++ snapcraft.yaml 2017-01-18 14:37:20 +0000 |
795 | @@ -1,5 +1,5 @@ |
796 | name: libertine |
797 | -version: 1.5+17.04.20170105.1-0ubuntu1 |
798 | +version: "1.5.1" |
799 | summary: Libertine suite |
800 | description: | |
801 | Suite for maintaining deb-based applications in a non-deb environment |
802 | |
803 | === modified file 'tools/libertine-container-manager' |
804 | --- tools/libertine-container-manager 2017-01-05 14:23:21 +0000 |
805 | +++ tools/libertine-container-manager 2017-01-18 14:37:20 +0000 |
806 | @@ -34,6 +34,14 @@ |
807 | self.containers_config = ContainersConfig() |
808 | self.host_info = HostInfo() |
809 | |
810 | + def _get_updated_locale(self, container_id): |
811 | + host_locale = self.host_info.get_host_locale() |
812 | + |
813 | + if host_locale == self.containers_config.get_container_locale(container_id): |
814 | + return None |
815 | + else: |
816 | + return host_locale |
817 | + |
818 | def create(self, args): |
819 | password = None |
820 | |
821 | @@ -71,7 +79,7 @@ |
822 | if not args.name: |
823 | args.name = "Ubuntu \'" + (self.host_info.get_distro_codename(args.distro) or args.distro) + "\'" |
824 | |
825 | - if container_type == "lxc": |
826 | + if container_type == "lxc" or container_type == "lxd": |
827 | if args.password: |
828 | password = args.password |
829 | elif sys.stdin.isatty(): |
830 | @@ -88,6 +96,7 @@ |
831 | self.containers_config.update_container_multiarch_support(args.id, multiarch) |
832 | |
833 | try: |
834 | + self.containers_config.update_container_locale(args.id, self.host_info.get_host_locale()) |
835 | container = LibertineContainer(args.id) |
836 | self.containers_config.update_container_install_status(args.id, "installing") |
837 | if not container.create_libertine_container(password, args.multiarch): |
838 | @@ -96,6 +105,9 @@ |
839 | sys.exit(1) |
840 | except Exception as e: |
841 | libertine.utils.get_logger().error("Failed to create container: '{}'".format(str(e))) |
842 | + if container: |
843 | + container.destroy_libertine_container() |
844 | + |
845 | self.containers_config.delete_container(args.id) |
846 | sys.exit(1) |
847 | |
848 | @@ -139,7 +151,7 @@ |
849 | else: |
850 | self.containers_config.add_new_package(container_id, package) |
851 | |
852 | - container = LibertineContainer(container_id) |
853 | + container = LibertineContainer(container_id, self.containers_config) |
854 | |
855 | self.containers_config.update_package_install_status(container_id, package, "installing") |
856 | if not container.install_package(args.package, args.no_dialog): |
857 | @@ -191,14 +203,18 @@ |
858 | |
859 | def update(self, args): |
860 | container_id = self.containers_config.check_container_id(args.id) |
861 | + new_locale = self._get_updated_locale(container_id) |
862 | |
863 | container = LibertineContainer(container_id) |
864 | |
865 | self.containers_config.update_container_install_status(container_id, "updating") |
866 | - if not container.update_libertine_container(): |
867 | + if not container.update_libertine_container(new_locale): |
868 | self.containers_config.update_container_install_status(container_id, "ready") |
869 | sys.exit(1) |
870 | |
871 | + if new_locale: |
872 | + self.containers_config.update_container_locale(container_id, new_locale) |
873 | + |
874 | self.containers_config.update_container_install_status(container_id, "ready") |
875 | |
876 | def list(self, args): |
877 | @@ -454,7 +470,8 @@ |
878 | # Handle the update command and its options |
879 | parser_update = subparsers.add_parser( |
880 | 'update', |
881 | - help=("Update the packages in the Libertine container.")) |
882 | + help=("Update the packages in the Libertine container. Also updates the container's " |
883 | + "locale and installs necessary language packs if the host's locale has changed.")) |
884 | parser_update.add_argument( |
885 | '-i', '--id', |
886 | help=("Container identifier. Default container is used if omitted.")) |
887 | |
888 | === modified file 'tools/libertine-container-manager.1' |
889 | --- tools/libertine-container-manager.1 2016-12-07 21:24:16 +0000 |
890 | +++ tools/libertine-container-manager.1 2017-01-18 14:37:20 +0000 |
891 | @@ -52,7 +52,7 @@ |
892 | Searches the apt cache inside an existing Libertine container for matches based on the given search string. |
893 | .TP |
894 | .B libertine-container-manager update [options] |
895 | -Updates the packages inside an existing Libertine container. |
896 | +Updates the packages inside an existing Libertine container. Also updates the container's locale and installs necessary language packs if the host's locale has changed. |
897 | .TP |
898 | .B libertine-container-manager list [options] |
899 | Lists all existing Libertine containers. |
900 | |
901 | === modified file 'tools/libertine-lxc-manager' |
902 | --- tools/libertine-lxc-manager 2017-01-05 19:50:46 +0000 |
903 | +++ tools/libertine-lxc-manager 2017-01-18 14:37:20 +0000 |
904 | @@ -55,7 +55,7 @@ |
905 | |
906 | return LifecycleResult() |
907 | |
908 | - def stop(self, container_id): |
909 | + def stop(self, container_id, options={}): |
910 | container = libertine.LxcContainer.lxc_container(container_id) |
911 | libertine.LxcContainer.lxc_stop(container) |
912 | |
913 | |
914 | === modified file 'tools/libertine-lxd-manager' |
915 | --- tools/libertine-lxd-manager 2016-12-21 20:37:57 +0000 |
916 | +++ tools/libertine-lxd-manager 2017-01-18 14:37:20 +0000 |
917 | @@ -49,8 +49,8 @@ |
918 | |
919 | return LifecycleResult() |
920 | |
921 | - def stop(self, container_id): |
922 | - return libertine.LxdContainer.lxd_stop(libertine.LxdContainer.lxd_container(self._client, container_id), False) |
923 | + def stop(self, container_id, options={}): |
924 | + return libertine.LxdContainer.lxd_stop(libertine.LxdContainer.lxd_container(self._client, container_id), options.get('wait', False)) |
925 | |
926 | |
927 | if __name__ == '__main__': |
lgtm!