Merge lp:~townsend/libertine/1.6-release into lp:libertine/trunk

Proposed by Christopher Townsend
Status: Merged
Approved by: Stephen M. Webb
Approved revision: 187
Merged at revision: 185
Proposed branch: lp:~townsend/libertine/1.6-release
Merge into: lp:libertine/trunk
Diff against target: 4853 lines (+1240/-1763)
74 files modified
CMakeLists.txt (+2/-2)
cmake/FindGObjectIntrospection.cmake (+0/-61)
cmake/ListOperations.cmake (+0/-18)
cmake/UseGObjectIntrospection.cmake (+0/-102)
common/LibertineConfig.cpp (+10/-1)
data/CMakeLists.txt (+1/-3)
data/com.canonical.libertine.LxcManager.service (+0/-3)
data/com.canonical.libertine.LxdManager.service (+0/-3)
data/com.canonical.libertine.Service.service (+1/-1)
data/libertine-xmir.conf (+1/-1)
data/snap-runner.wrapper (+2/-1)
debian/changelog (+40/-0)
debian/control (+22/-18)
debian/gir1.2-libertine.install (+0/-2)
debian/libertine-tools.install (+0/-2)
debian/libertined.install (+3/-0)
debian/python3-libertine-lxc.install (+0/-2)
debian/python3-libertine-lxd.install (+0/-2)
debian/python3-libertine.install (+0/-3)
debian/rules (+1/-4)
liblibertine/CMakeLists.txt (+3/-30)
liblibertine/libertine.cpp (+20/-131)
liblibertine/libertine.h (+4/-4)
liblibertine/libertined.cpp (+199/-0)
liblibertine/libertined.h (+25/-0)
python/libertine/AppDiscovery.py (+0/-220)
python/libertine/ChrootContainer.py (+6/-13)
python/libertine/ContainersConfig.py (+21/-2)
python/libertine/HostInfo.py (+8/-1)
python/libertine/Libertine.py (+62/-52)
python/libertine/LxcContainer.py (+137/-65)
python/libertine/LxdContainer.py (+131/-131)
python/libertine/launcher/config.py (+1/-4)
python/libertine/lifecycle/ContainerLifecycleService.py (+0/-108)
python/libertine/lifecycle/ContainerLifecycleServiceRunner.py (+0/-46)
python/libertine/lifecycle/LifecycleResult.py (+0/-37)
python/libertine/lifecycle/__init__.py (+0/-23)
python/libertine/service/apt.py (+2/-6)
python/libertine/service/container.py (+23/-8)
python/libertine/service/manager.py (+40/-4)
python/libertine/service/progress.py (+3/-3)
python/libertine/service/task_dispatcher.py (+7/-8)
python/libertine/service/tasks/__init__.py (+3/-3)
python/libertine/service/tasks/base_task.py (+2/-4)
python/libertine/service/tasks/container_info_task.py (+21/-5)
python/libertine/service/tasks/list_app_ids_task.py (+35/-0)
python/libertine/service/tasks/list_apps_task.py (+0/-30)
python/libertine/service/tasks/list_task.py (+6/-4)
python/libertine/utils.py (+29/-59)
setup/gui/libertine-manager-app.desktop (+12/-0)
snapcraft.yaml (+40/-6)
tests/integration/CMakeLists.txt (+1/-1)
tests/integration/test_libertine_service.py (+40/-22)
tests/unit/service/tasks/CMakeLists.txt (+1/-1)
tests/unit/service/tasks/test_container_info_task.py (+14/-2)
tests/unit/service/tasks/test_list_app_ids_task.py (+59/-0)
tests/unit/service/tasks/test_list_apps_task.py (+0/-59)
tests/unit/service/tasks/test_list_task.py (+8/-6)
tests/unit/service/test_apt.py (+37/-45)
tests/unit/service/test_container.py (+33/-47)
tests/unit/service/test_task_dispatcher.py (+17/-17)
tests/unit/test_app_discovery.py (+0/-52)
tests/unit/test_libertine_container.py (+2/-1)
tests/unit/test_libertine_gir.py (+0/-65)
tools/CMakeLists.txt (+3/-3)
tools/completions/libertine-container-manager (+9/-5)
tools/libertine-container-manager (+65/-11)
tools/libertine-container-manager.1 (+25/-3)
tools/libertine-lxc-manager (+0/-109)
tools/libertine-lxc-manager.1 (+0/-9)
tools/libertine-lxd-manager (+0/-57)
tools/libertine-lxd-manager.1 (+0/-9)
tools/libertine-lxd-setup (+1/-1)
tools/libertine-xmir (+2/-2)
To merge this branch: bzr merge lp:~townsend/libertine/1.6-release
Reviewer Review Type Date Requested Status
Stephen M. Webb (community) Approve
Review via email: mp+316750@code.launchpad.net

Commit message

* Get libertine-container-manager and libertine-launch working in a confined environment.
* Create a special desktop file to satisfy the snappy store.
* Update snap definition to include aliases, renamed commands, and internal desktop file.
* Ensure absolute path lxd bind-mounts have a valid name by using the full path as a backup.
* Refactor liblibertine as a client wrapper for libertined and bump version to 1.5.2.
* Protect against containers that aren't started when running commands.
* Migrate the list-apps subcommand to list application ids. (LP: #1657877)
* Run the libertined integration test with its own dbus sesison to prevent collisions with running service.
* Discover if a session is running and if so, use it's dbus session to start the container manager. (LP: #1657490)
* Replace all pylxd execute()'s with calls to 'lxc exec' as pylxd has performance issues in execute().
* Add ability to freeze and unfreeze LXC/LXD containers. This is disabled by default for now. (LP: #1654355)
* Add ability to enable and disable the freezing of LXC/LXD containers when not in use. (LP: #1654355)
* Deprecate the lx[cd] managers and use libertined for managing the containers and move the actual starting and stopping of containers to the respective backends. (LP: #1660685)
* Add a restart subcommand to libertine-container-manager. Also try to restart the container when adding/removing bind-mounts, if appropriate. (LP: #1654355)
* Enable Xmir '-rootless' mode by default and remove using Matchbox for the window manager. (LP: #1662555)
* Bump release version to 1.6.

To post a comment you must log in.
lp:~townsend/libertine/1.6-release updated
186. By Christopher Townsend

Fix CMake file typo.

187. By Christopher Townsend

Change when the libertine-xmir Upstart gets started to account for Unity 8 now directly starting apps.

188. By Christopher Townsend

Return True instead of pass for start_container() in the base class. Fixes running operation in chroot's.

Revision history for this message
Stephen M. Webb (bregma) wrote :

Sane.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2017-01-13 20:11:25 +0000
3+++ CMakeLists.txt 2017-02-09 16:08:46 +0000
4@@ -2,12 +2,11 @@
5 cmake_policy(SET CMP0048 NEW)
6
7 project(libertine
8- VERSION 1.5.1)
9+ VERSION 1.6)
10
11 set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")
12
13 find_package(PkgConfig REQUIRED)
14-find_package(GObjectIntrospection REQUIRED)
15 include(GNUInstallDirs)
16 include(CheckIncludeFile)
17 include(CheckFunctionExists)
18@@ -17,6 +16,7 @@
19 include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR})
20
21 find_package(Qt5Core REQUIRED)
22+find_package(Qt5DBus REQUIRED)
23 find_package(Qt5Gui REQUIRED)
24 find_package(Qt5Quick REQUIRED)
25 find_package(Qt5Widgets REQUIRED)
26
27=== removed directory 'cmake'
28=== removed file 'cmake/FindGObjectIntrospection.cmake'
29--- cmake/FindGObjectIntrospection.cmake 2015-08-17 20:42:31 +0000
30+++ cmake/FindGObjectIntrospection.cmake 1970-01-01 00:00:00 +0000
31@@ -1,61 +0,0 @@
32-# - try to find gobject-introspection
33-#
34-# Once done this will define
35-#
36-# INTROSPECTION_FOUND - system has gobject-introspection
37-# INTROSPECTION_SCANNER - the gobject-introspection scanner, g-ir-scanner
38-# INTROSPECTION_COMPILER - the gobject-introspection compiler, g-ir-compiler
39-# INTROSPECTION_GENERATE - the gobject-introspection generate, g-ir-generate
40-# INTROSPECTION_GIRDIR
41-# INTROSPECTION_TYPELIBDIR
42-# INTROSPECTION_CFLAGS
43-# INTROSPECTION_LIBS
44-#
45-# Copyright (C) 2010, Pino Toscano, <pino@kde.org>
46-#
47-# Redistribution and use is allowed according to the terms of the BSD license.
48-# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
49-
50-macro(_GIR_GET_PKGCONFIG_VAR _outvar _varname)
51- execute_process(
52- COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=${_varname} gobject-introspection-1.0
53- OUTPUT_VARIABLE _result
54- RESULT_VARIABLE _null
55- )
56-
57- if (_null)
58- else()
59- string(REGEX REPLACE "[\r\n]" " " _result "${_result}")
60- string(REGEX REPLACE " +$" "" _result "${_result}")
61- separate_arguments(_result)
62- set(${_outvar} ${_result} CACHE INTERNAL "")
63- endif()
64-endmacro(_GIR_GET_PKGCONFIG_VAR)
65-
66-find_package(PkgConfig)
67-if(PKG_CONFIG_FOUND)
68- if(PACKAGE_FIND_VERSION_COUNT GREATER 0)
69- set(_gir_version_cmp ">=${PACKAGE_FIND_VERSION}")
70- endif()
71- pkg_check_modules(_pc_gir gobject-introspection-1.0${_gir_version_cmp})
72- if(_pc_gir_FOUND)
73- set(INTROSPECTION_FOUND TRUE)
74- _gir_get_pkgconfig_var(INTROSPECTION_SCANNER "g_ir_scanner")
75- _gir_get_pkgconfig_var(INTROSPECTION_COMPILER "g_ir_compiler")
76- _gir_get_pkgconfig_var(INTROSPECTION_GENERATE "g_ir_generate")
77- _gir_get_pkgconfig_var(INTROSPECTION_GIRDIR "girdir")
78- _gir_get_pkgconfig_var(INTROSPECTION_TYPELIBDIR "typelibdir")
79- set(INTROSPECTION_CFLAGS "${_pc_gir_CFLAGS}")
80- set(INTROSPECTION_LIBS "${_pc_gir_LIBS}")
81- endif()
82-endif()
83-
84-mark_as_advanced(
85- INTROSPECTION_SCANNER
86- INTROSPECTION_COMPILER
87- INTROSPECTION_GENERATE
88- INTROSPECTION_GIRDIR
89- INTROSPECTION_TYPELIBDIR
90- INTROSPECTION_CFLAGS
91- INTROSPECTION_LIBS
92-)
93
94=== removed file 'cmake/ListOperations.cmake'
95--- cmake/ListOperations.cmake 2015-08-17 20:42:31 +0000
96+++ cmake/ListOperations.cmake 1970-01-01 00:00:00 +0000
97@@ -1,18 +0,0 @@
98-
99-macro(list_prefix _outvar _listvar _prefix)
100- set(${_outvar})
101- foreach(_item IN LISTS ${_listvar})
102- list(APPEND ${_outvar} ${_prefix}${_item})
103- endforeach()
104-endmacro(list_prefix)
105-
106-macro(list_make_absolute _outvar _listvar _prefix)
107- set(${_outvar})
108- foreach(_item IN LISTS ${_listvar})
109- if(IS_ABSOLUTE ${_item})
110- list(APPEND ${_outvar} ${_item})
111- else()
112- list(APPEND ${_outvar} ${_prefix}${_item})
113- endif()
114- endforeach()
115-endmacro(list_make_absolute)
116
117=== removed file 'cmake/UseGObjectIntrospection.cmake'
118--- cmake/UseGObjectIntrospection.cmake 2015-08-17 20:42:31 +0000
119+++ cmake/UseGObjectIntrospection.cmake 1970-01-01 00:00:00 +0000
120@@ -1,102 +0,0 @@
121-# Copyright (C) 2010, Pino Toscano, <pino@kde.org>
122-#
123-# Redistribution and use is allowed according to the terms of the BSD license.
124-# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
125-
126-include(ListOperations)
127-
128-macro(_gir_list_prefix _outvar _listvar _prefix)
129- set(${_outvar})
130- foreach(_item IN LISTS ${_listvar})
131- list(APPEND ${_outvar} ${_prefix}${_item})
132- endforeach()
133-endmacro(_gir_list_prefix)
134-
135-macro(gir_add_introspections introspections_girs)
136-
137- foreach(gir IN LISTS ${introspections_girs})
138-
139- set(_gir_name "${gir}")
140-
141- ## Transform the gir filename to something which can reference through a variable
142- ## without automake/make complaining, eg Gtk-2.0.gir -> Gtk_2_0_gir
143- string(REPLACE "-" "_" _gir_name "${_gir_name}")
144- string(REPLACE "." "_" _gir_name "${_gir_name}")
145-
146- # Namespace and Version is either fetched from the gir filename
147- # or the _NAMESPACE/_VERSION variable combo
148- set(_gir_namespace "${${_gir_name}_NAMESPACE}")
149- if (_gir_namespace STREQUAL "")
150- string(REGEX REPLACE "([^-]+)-.*" "\\1" _gir_namespace "${gir}")
151- endif ()
152- set(_gir_version "${${_gir_name}_VERSION}")
153- if (_gir_version STREQUAL "")
154- string(REGEX REPLACE ".*-([^-]+).gir" "\\1" _gir_version "${gir}")
155- endif ()
156-
157- # _PROGRAM is an optional variable which needs it's own --program argument
158- set(_gir_program "${${_gir_name}_PROGRAM}")
159- if (NOT _gir_program STREQUAL "")
160- set(_gir_program "--program=${_gir_program}")
161- endif ()
162-
163- # Variables which provides a list of things
164- _gir_list_prefix(_gir_libraries ${_gir_name}_LIBS "--library=")
165- _gir_list_prefix(_gir_packages ${_gir_name}_PACKAGES "--pkg=")
166- _gir_list_prefix(_gir_includes ${_gir_name}_INCLUDES "--include=")
167- _gir_list_prefix(_gir_export_packages ${_gir_name}_EXPORT_PACKAGES "--pkg-export=")
168-
169- # Reuse the LIBTOOL variable from by automake if it's set
170- set(_gir_libtool "--no-libtool")
171-
172- add_custom_command(
173- COMMAND ${INTROSPECTION_SCANNER}
174- ${INTROSPECTION_SCANNER_ARGS}
175- --quiet
176- --warn-all
177- --namespace=${_gir_namespace}
178- --nsversion=${_gir_version}
179- ${_gir_libtool}
180- ${_gir_program}
181- ${_gir_libraries}
182- ${_gir_packages}
183- ${_gir_includes}
184- ${_gir_export_packages}
185- ${${_gir_name}_SCANNERFLAGS}
186- ${${_gir_name}_CFLAGS}
187- ${${_gir_name}_FILES}
188- --output ${CMAKE_CURRENT_BINARY_DIR}/${gir}
189- DEPENDS ${${_gir_name}_FILES}
190- ${${_gir_name}_LIBS}
191- OUTPUT ${gir}
192- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
193- VERBATIM
194- )
195- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${gir} DESTINATION share/gir-1.0)
196-
197- string(REPLACE ".gir" ".typelib" _typelib "${gir}")
198- add_custom_command(
199- COMMAND ${INTROSPECTION_COMPILER}
200- ${INTROSPECTION_COMPILER_ARGS}
201- --includedir=.
202- ${CMAKE_CURRENT_BINARY_DIR}/${gir}
203- -o ${CMAKE_CURRENT_BINARY_DIR}/${_typelib}
204- DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${gir}
205- OUTPUT ${_typelib}
206- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
207- )
208- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_typelib} DESTINATION ${CMAKE_INSTALL_LIBDIR}/girepository-1.0)
209-
210- add_custom_target(gir-${gir} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${gir})
211- add_custom_target(gir-typelibs-${_typelib} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_typelib})
212- endforeach()
213-
214-endmacro(gir_add_introspections)
215-
216-macro(gir_get_cflags _output)
217- get_directory_property(_tmp_includes INCLUDE_DIRECTORIES)
218- list_prefix(_includes _tmp_includes "-I")
219- get_directory_property(_tmp_compile_definitions COMPILE_DEFINITIONS)
220- list_prefix(_compile_definitions _tmp_compile_definitions "-D")
221- set(${_output} ${_includes} ${_compile_definitions})
222-endmacro(gir_get_cflags)
223
224=== modified file 'common/LibertineConfig.cpp'
225--- common/LibertineConfig.cpp 2016-09-26 18:17:07 +0000
226+++ common/LibertineConfig.cpp 2017-02-09 16:08:46 +0000
227@@ -3,7 +3,7 @@
228 * @brief Libertine Manager application-wide configuration module
229 */
230 /*
231- * Copyright 2015 Canonical Ltd
232+ * Copyright 2015-2017 Canonical Ltd
233 *
234 * Libertine is free software: you can redistribute it and/or modify it under
235 * the terms of the GNU General Public License, version 3, as published by the
236@@ -18,6 +18,7 @@
237 */
238 #include "common/LibertineConfig.h"
239
240+#include <cstdlib>
241 #include <QtCore/QDir>
242 #include <QtCore/QFile>
243 #include <QtCore/QStandardPaths>
244@@ -27,6 +28,14 @@
245 containers_config_file_name() const
246 {
247 QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/libertine";
248+
249+ // if running from a snap
250+ auto snap_common = std::getenv("SNAP_USER_COMMON");
251+ if (snap_common != nullptr)
252+ {
253+ path.replace(std::getenv("HOME"), snap_common);
254+ }
255+
256 QDir dir(path);
257
258 if (!dir.exists())
259
260=== modified file 'data/CMakeLists.txt'
261--- data/CMakeLists.txt 2016-12-21 20:57:15 +0000
262+++ data/CMakeLists.txt 2017-02-09 16:08:46 +0000
263@@ -8,7 +8,5 @@
264 DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions)
265 install(FILES libertine-lxc-sudo libertine-lxd-sudo
266 DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d)
267-install(FILES com.canonical.libertine.ContainerManager.service
268- com.canonical.libertine.LxcManager.service
269- com.canonical.libertine.LxdManager.service
270+install(FILES com.canonical.libertine.Service.service
271 DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services)
272
273=== removed file 'data/com.canonical.libertine.LxcManager.service'
274--- data/com.canonical.libertine.LxcManager.service 2016-08-19 14:02:31 +0000
275+++ data/com.canonical.libertine.LxcManager.service 1970-01-01 00:00:00 +0000
276@@ -1,3 +0,0 @@
277-[D-BUS Service]
278-Name=com.canonical.libertine.LxcManager
279-Exec=/usr/bin/libertine-lxc-manager
280
281=== removed file 'data/com.canonical.libertine.LxdManager.service'
282--- data/com.canonical.libertine.LxdManager.service 2016-12-20 20:23:16 +0000
283+++ data/com.canonical.libertine.LxdManager.service 1970-01-01 00:00:00 +0000
284@@ -1,3 +0,0 @@
285-[D-BUS Service]
286-Name=com.canonical.libertine.LxdManager
287-Exec=/usr/bin/libertine-lxd-manager
288
289=== renamed file 'data/com.canonical.libertine.ContainerManager.service' => 'data/com.canonical.libertine.Service.service'
290--- data/com.canonical.libertine.ContainerManager.service 2016-11-01 20:10:58 +0000
291+++ data/com.canonical.libertine.Service.service 2017-02-09 16:08:46 +0000
292@@ -1,3 +1,3 @@
293 [D-BUS Service]
294-Name=com.canonical.libertine.ContainerManager
295+Name=com.canonical.libertine.Service
296 Exec=/usr/bin/libertined --cache-output
297
298=== modified file 'data/libertine-xmir.conf'
299--- data/libertine-xmir.conf 2016-02-18 15:49:20 +0000
300+++ data/libertine-xmir.conf 2017-02-09 16:08:46 +0000
301@@ -1,7 +1,7 @@
302 description "Set global environment variable to tell u-a-l where to look to start Xmir"
303 author "Christopher Townsend <christopher.townsend@canonical.com>"
304
305-start on started unity8
306+start on starting unity8
307
308 pre-start script
309 initctl set-env --global UBUNTU_APP_LAUNCH_XMIR_PATH="/usr/bin/libertine-xmir"
310
311=== modified file 'data/snap-runner.wrapper'
312--- data/snap-runner.wrapper 2016-12-06 16:14:42 +0000
313+++ data/snap-runner.wrapper 2017-02-09 16:08:46 +0000
314@@ -3,9 +3,10 @@
315
316 export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/fakechroot:$LD_LIBRARY_PATH
317 export DEBOOTSTRAP_DIR=$SNAP/usr/share/debootstrap
318+export LXD_DIR=/var/snap/lxd/common/lxd
319
320 # Useful debug variables
321 # export FAKECHROOT_DEBUG=1
322 # export LIBERTINE_DEBUG=1
323
324-exec $SNAP/bin/desktop-launch $@
325+exec $SNAP/bin/desktop-launch "$@"
326
327=== modified file 'debian/changelog'
328--- debian/changelog 2017-01-18 14:50:33 +0000
329+++ debian/changelog 2017-02-09 16:08:46 +0000
330@@ -1,3 +1,43 @@
331+libertine (1.6-0ubuntu1) UNRELEASED; urgency=medium
332+
333+ [ Larry Price ]
334+ * Get libertine-container-manager and libertine-launch working in a confined
335+ environment.
336+ * Create a special desktop file to satisfy the snappy store.
337+ * Update snap definition to include aliases, renamed commands, and internal
338+ desktop file.
339+ * Ensure absolute path lxd bind-mounts have a valid name by using the full
340+ path as a backup.
341+ * Refactor liblibertine as a client wrapper for libertined and bump version
342+ to 1.5.2.
343+ * Protect against containers that aren't started when running commands.
344+ * Migrate the list-apps subcommand to list application ids. (LP: #1657877)
345+ * Run the libertined integration test with its own dbus sesison to prevent
346+ collisions with running service.
347+
348+ [ Chris Townsend ]
349+ * Discover if a session is running and if so, use it's dbus session to start
350+ the container manager. (LP: #1657490)
351+ * Replace all pylxd execute()'s with calls to 'lxc exec' as pylxd has
352+ performance issues in execute().
353+ * Add ability to freeze and unfreeze LXC/LXD containers. This is disabled by
354+ default for now. (LP: #1654355)
355+ * Add ability to enable and disable the freezing of LXC/LXD containers when
356+ not in use. (LP: #1654355)
357+ * Deprecate the lx[cd] managers and use libertined for managing the containers
358+ and move the actual starting and stopping of containers to the respective
359+ backends. (LP: #1660685)
360+ * Add a restart subcommand to libertine-container-manager. Also try to restart
361+ the container when adding/removing bind-mounts, if appropriate.
362+ (LP: #1654355)
363+ * Enable Xmir '-rootless' mode by default and remove using Matchbox for the
364+ window manager. (LP: #1662555)
365+ * Change when the libertine-xmir Upstart gets started to account for Unity 8
366+ now directly starting apps.
367+ * Bump release version to 1.6.
368+
369+ -- Chris Townsend <christopher.townsend@canonical.com> Wed, 08 Feb 2017 13:36:05 -0500
370+
371 libertine (1.5.1+17.04.20170118-0ubuntu1) zesty; urgency=medium
372
373 [ Chris Townsend ]
374
375=== modified file 'debian/control'
376--- debian/control 2016-12-07 17:45:28 +0000
377+++ debian/control 2017-02-09 16:08:46 +0000
378@@ -23,6 +23,7 @@
379 python3-distro-info,
380 python3-gi,
381 python3-lxc,
382+ python3-pexpect,
383 python3-psutil,
384 python3-pytest,
385 python3-testtools,
386@@ -84,7 +85,6 @@
387 Architecture: any
388 Depends: libglib2.0-bin,
389 lsb-release,
390- python3-apt,
391 python3-dbus,
392 python3-distro-info,
393 python3-libertine,
394@@ -104,7 +104,6 @@
395 libqt5core5a,
396 libqt5gui5,
397 libqt5widgets5,
398- matchbox-window-manager,
399 xmir [amd64 armhf arm64 i386],
400 ${misc:Depends},
401 ${python3:Depends},
402@@ -115,15 +114,33 @@
403 Helper applications for using and interacting with Xmir such as launching
404 Xmir and allowing copy and paste.
405
406+Package: libertined
407+Architecture: any
408+Multi-Arch: same
409+Depends: python3-libertine,
410+ python3-apt,
411+ python3-dbus,
412+ python3-gi,
413+ ${misc:Depends},
414+ ${shlibs:Depends},
415+ ${python3:Depends}
416+Breaks: libertine-tools (<< 1.5.2)
417+Replaces: libertine-tools (<< 1.5.2)
418+Description: d-bus service for managing libertine containers
419+ D-Bus service for asynchronously discovering and managing classic applications
420+ using libertine containers. Allows installation and removal of deb-packaged
421+ X11 apps within an isolated sandbox.
422+
423 Package: liblibertine1
424 Architecture: any
425 Multi-Arch: same
426-Depends: ${misc:Depends},
427+Depends: libertined,
428+ ${misc:Depends},
429 ${shlibs:Depends}
430 Description: runtime for running deb-packaged X11 apps on Ubuntu Personal
431 Runtime library for creating and using the Ubuntu Personal sandbox for legacy
432 Deb-packaged X11 applicatons. This library is used by the Libertine tools
433- and other software interacting wit hthe Libertine container, such as scopes
434+ and other software interacting with the Libertine container, such as scopes
435 or application launchers.
436
437 Package: liblibertine-dev
438@@ -137,24 +154,11 @@
439 Headers and shared libraries used to create the tools for creating and using
440 the Ubuntu Personal sandbox for legacy Deb-packaged X11 applicatons.
441
442-Package: gir1.2-libertine
443-Architecture: any
444-Multi-Arch: same
445-Depends: liblibertine1 (= ${binary:Version}),
446- ${gir:Depends},
447- ${misc:Depends}
448-Description: GObject introspection files for the Libertine application sandbox
449- The GObject introspection description files for the Libertine application
450- sandbox runtime. This package allows the Libertine API to be used from
451- GIR-compliant languages such as Python.
452-
453 Package: python3-libertine
454 Architecture: any
455 Section: python
456 Multi-Arch: allowed
457-Depends: gir1.2-libertine,
458- python3-gi,
459- python3-psutil,
460+Depends: python3-psutil,
461 python3-xdg,
462 xdg-user-dirs,
463 ${misc:Depends},
464
465=== removed file 'debian/gir1.2-libertine.install'
466--- debian/gir1.2-libertine.install 2015-08-24 16:54:54 +0000
467+++ debian/gir1.2-libertine.install 1970-01-01 00:00:00 +0000
468@@ -1,2 +0,0 @@
469-usr/lib/*/girepository-1.0/Libertine-1.typelib
470-usr/share/gir-1.0/Libertine-1.gir
471
472=== modified file 'debian/libertine-tools.install'
473--- debian/libertine-tools.install 2016-11-01 20:10:58 +0000
474+++ debian/libertine-tools.install 2017-02-09 16:08:46 +0000
475@@ -1,6 +1,4 @@
476 usr/bin/libertine-container-manager
477 usr/bin/libertine-launch
478-usr/bin/libertined
479-usr/share/dbus-1/services/com.canonical.libertine.ContainerManager.service
480 usr/share/bash-completion/completions/libertine-container-manager
481 usr/share/man
482
483=== added file 'debian/libertined.install'
484--- debian/libertined.install 1970-01-01 00:00:00 +0000
485+++ debian/libertined.install 2017-02-09 16:08:46 +0000
486@@ -0,0 +1,3 @@
487+usr/bin/libertined
488+usr/lib/python*/*/libertine/service
489+usr/share/dbus-1/services/com.canonical.libertine.Service.service
490
491=== modified file 'debian/python3-libertine-lxc.install'
492--- debian/python3-libertine-lxc.install 2016-10-07 21:09:41 +0000
493+++ debian/python3-libertine-lxc.install 2017-02-09 16:08:46 +0000
494@@ -1,6 +1,4 @@
495 etc/sudoers.d/libertine-lxc-sudo
496-usr/bin/libertine-lxc-manager
497 usr/bin/libertine-lxc-setup
498 usr/lib/python*/*/libertine/LxcContainer.py
499-usr/share/dbus-1/services/com.canonical.libertine.LxcManager.service
500 usr/share/libertine/libertine-lxc.conf
501
502=== modified file 'debian/python3-libertine-lxd.install'
503--- debian/python3-libertine-lxd.install 2016-12-21 20:37:57 +0000
504+++ debian/python3-libertine-lxd.install 2017-02-09 16:08:46 +0000
505@@ -1,5 +1,3 @@
506 etc/sudoers.d/libertine-lxd-sudo
507 usr/bin/libertine-lxd-setup
508-usr/bin/libertine-lxd-manager
509 usr/lib/python*/*/libertine/LxdContainer.py
510-usr/share/dbus-1/services/com.canonical.libertine.LxdManager.service
511
512=== modified file 'debian/python3-libertine.install'
513--- debian/python3-libertine.install 2016-12-21 18:07:07 +0000
514+++ debian/python3-libertine.install 2017-02-09 16:08:46 +0000
515@@ -1,9 +1,6 @@
516-usr/lib/python*/*/libertine/AppDiscovery.py
517 usr/lib/python*/*/libertine/ContainersConfig.py
518 usr/lib/python*/*/libertine/HostInfo.py
519 usr/lib/python*/*/libertine/Libertine.py
520 usr/lib/python*/*/libertine/__init__.py
521 usr/lib/python*/*/libertine/launcher
522-usr/lib/python*/*/libertine/lifecycle
523-usr/lib/python*/*/libertine/service
524 usr/lib/python*/*/libertine/utils.py
525
526=== modified file 'debian/rules'
527--- debian/rules 2016-12-06 20:52:55 +0000
528+++ debian/rules 2017-02-09 16:08:46 +0000
529@@ -1,7 +1,4 @@
530 #!/usr/bin/make -f
531
532 %:
533- dh $@ --with python3,gir
534-
535-override_dh_auto_test:
536- dbus-run-session -- dh_auto_test
537+ dh $@ --with python3
538
539=== modified file 'liblibertine/CMakeLists.txt'
540--- liblibertine/CMakeLists.txt 2016-11-03 19:19:40 +0000
541+++ liblibertine/CMakeLists.txt 2017-02-09 16:08:46 +0000
542@@ -1,15 +1,12 @@
543 set(API_VERSION 1)
544 set(ABI_VERSION 1)
545
546-set(COMMON_DIR ${CMAKE_SOURCE_DIR}/common)
547-
548 add_library(
549 ${LIBERTINE_CORE} SHARED
550 libertine.cpp
551
552- ${COMMON_DIR}/ContainerConfig.cpp
553- ${COMMON_DIR}/ContainerConfigList.cpp
554- ${COMMON_DIR}/LibertineConfig.cpp
555+ # libertined client
556+ libertined.cpp
557 )
558
559 set_target_properties(${LIBERTINE_CORE} PROPERTIES
560@@ -22,6 +19,7 @@
561 ${GLIB2_LIBRARIES}
562 ${PYTHON3_LIBRARIES}
563 Qt5::Core
564+ Qt5::DBus
565 )
566
567 # "liblibertine_headers_path" is used in libertine.pc.in
568@@ -32,28 +30,3 @@
569 configure_file(libertine.pc.in ${CMAKE_BINARY_DIR}/libertine.pc @ONLY)
570 install(FILES ${CMAKE_BINARY_DIR}/libertine.pc
571 DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
572-
573-##########################
574-# Introspection
575-##########################
576-
577-include(UseGObjectIntrospection)
578-
579-set(INTROSPECTION_GIRS)
580-set(_introspection_files libertine.h)
581-set(Libertine_1_gir "libertine")
582-set(Libertine_1_gir_INCLUDES GObject-2.0)
583-
584-gir_get_cflags(_cflags)
585-set(Libertine_1_gir_CFLAGS ${c_flags})
586-set(Libertine_1_gir_LIBS libertine)
587-
588-list_make_absolute(_abs_introspection_files _introspection_files "${CMAKE_CURRENT_SOURCE_DIR}/")
589-set(Libertine_1_gir_FILES ${_abs_introspection_files})
590-set(Libertine_1_gir_SCANNERFLAGS --c-include "libertine.h")
591-set(Libertine_1_gir_EXPORT_PACKAGES "libertine-${API_VERSION}")
592-
593-list(APPEND INTROSPECTION_GIRS Libertine-1.gir)
594-gir_add_introspections(INTROSPECTION_GIRS)
595-
596-install(FILES "${CMAKE_CURRENT_BINARY_DIR}/Libertine-1.typelib" DESTINATION "${CMAKE_INSTALL_LIBDIR}/girepository-1.0")
597
598=== modified file 'liblibertine/libertine.cpp'
599--- liblibertine/libertine.cpp 2017-01-12 18:18:49 +0000
600+++ liblibertine/libertine.cpp 2017-02-09 16:08:46 +0000
601@@ -1,9 +1,9 @@
602 /**
603- * @file libertine_common.cpp
604+ * @file libertine.cpp
605 * @brief The Libertine Common shared library
606 */
607 /*
608- * Copyright 2015 Canonical Ltd.
609+ * Copyright 2015-2017 Canonical Ltd.
610 *
611 * This program is free software: you can redistribute it and/or modify it under
612 * the terms of the GNU General Public License, version 3, as published by the
613@@ -17,103 +17,21 @@
614 * You should have received a copy of the GNU General Public License
615 * along with this program. If not, see <http://www.gnu.org/licenses/>.
616 */
617+
618 #include "liblibertine/libertine.h"
619-
620-#include "common/ContainerConfigList.h"
621-#include "common/LibertineConfig.h"
622-
623-
624-namespace
625-{
626-constexpr auto DESKTOP_EXTENSION = ".desktop";
627-constexpr auto GLOBAL_APPLICATIONS = "usr/share/applications";
628-constexpr auto LOCAL_APPLICATIONS = ".local/share/applications";
629-
630-
631-GError*
632-list_apps_from_path(gchar* path, const gchar* container_id, GArray* apps)
633-{
634- GError* error = nullptr;
635- GDir* dir = g_dir_open(path, 0, &error);
636- if (error != nullptr)
637- {
638- return error;
639- }
640-
641- const gchar * files;
642- while ((files = g_dir_read_name(dir)) != nullptr)
643- {
644- gchar *file = g_build_filename(path, files, nullptr);
645- if (g_file_test(file, G_FILE_TEST_IS_REGULAR) && g_str_has_suffix(files, DESKTOP_EXTENSION))
646- {
647- auto name = g_strdup(files);
648- name[strlen(name)-strlen(DESKTOP_EXTENSION)] = 0; // truncate the file extension
649-
650- gchar * app_id = g_strjoin("_", g_strdup(container_id), name, "0.0", nullptr);
651- g_array_append_val(apps, app_id);
652- g_free(name);
653- }
654- else if (g_file_test(file, G_FILE_TEST_IS_DIR))
655- {
656- error = list_apps_from_path(file, container_id, apps);
657- if (error != nullptr)
658- {
659- return error;
660- }
661- }
662- g_free(file);
663- }
664-
665- g_dir_close(dir);
666- return nullptr;
667-}
668-
669-
670-gchar*
671-id_from_list_index(const ContainerConfigList& container_list, guint index)
672-{
673- return (gchar*)container_list.data(container_list.index(index, 0),
674- (int)ContainerConfigList::DataRole::ContainerId)
675- .toString().toStdString().c_str();
676-}
677-}
678+#include "liblibertine/libertined.h"
679+
680
681 gchar**
682 libertine_list_apps_for_container(const gchar* container_id)
683 {
684 g_return_val_if_fail(container_id != nullptr, nullptr);
685- gchar* path = libertine_container_path(container_id);
686- GError* error = nullptr;
687 GArray* apps = g_array_new(TRUE, TRUE, sizeof(gchar*));
688-
689- if (path != nullptr)
690- {
691- auto global_path = g_build_filename("/", g_strdup(path), GLOBAL_APPLICATIONS, nullptr);
692- error = list_apps_from_path(global_path, container_id, apps);
693- if (error != nullptr)
694- {
695- g_free(global_path);
696- g_free(path);
697- g_error_free(error);
698- return (gchar**)g_array_free(apps, FALSE);
699- }
700- g_free(global_path);
701- }
702- g_free(path);
703-
704- auto home_path = libertine_container_home_path(container_id);
705- if (home_path != nullptr)
706- {
707- auto local_path = g_build_filename(home_path, LOCAL_APPLICATIONS, nullptr);
708-
709- error = list_apps_from_path(local_path, container_id, apps);
710- if (error != nullptr)
711- {
712- g_error_free(error); // free error, but return previously found apps
713- }
714- g_free(local_path);
715- }
716- g_free(home_path);
717+ for (auto const& app: libertined_list_app_ids(container_id))
718+ {
719+ auto app_id = g_strdup((gchar *)app.toString().toStdString().c_str());
720+ g_array_append_val(apps, app_id);
721+ }
722
723 return (gchar**)g_array_free(apps, FALSE);
724 }
725@@ -122,22 +40,12 @@
726 gchar **
727 libertine_list_containers(void)
728 {
729- guint container_count;
730- guint i;
731- LibertineConfig config;
732- ContainerConfigList container_list(&config);
733- GArray * containers = g_array_new(TRUE, TRUE, sizeof(gchar *));
734- QVariant id;
735-
736- container_count = (guint)container_list.size();
737-
738- for (i = 0; i < container_count; ++i)
739+ auto containers = g_array_new(TRUE, TRUE, sizeof(gchar *));
740+ for (auto const& container: libertined_list())
741 {
742- id = container_list.data(container_list.index(i, 0), (int)ContainerConfigList::DataRole::ContainerId);
743- gchar * container_id = g_strdup((gchar *)id.toString().toStdString().c_str());
744+ auto container_id = g_strdup((gchar *)container.toString().toStdString().c_str());
745 g_array_append_val(containers, container_id);
746 }
747-
748 return (gchar **)g_array_free(containers, FALSE);
749 }
750
751@@ -146,11 +54,8 @@
752 libertine_container_path(const gchar * container_id)
753 {
754 g_return_val_if_fail(container_id != nullptr, nullptr);
755- LibertineConfig config;
756- ContainerConfigList container_list(&config);
757-
758- gchar * path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr);
759-
760+
761+ gchar* path = g_strdup((gchar *)libertined_container_path(container_id).toStdString().c_str());
762 if (g_file_test(path, G_FILE_TEST_EXISTS))
763 {
764 return path;
765@@ -165,11 +70,8 @@
766 libertine_container_home_path(const gchar * container_id)
767 {
768 g_return_val_if_fail(container_id != nullptr, nullptr);
769- LibertineConfig config;
770- ContainerConfigList container_list(&config);
771-
772- gchar * path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr);
773-
774+
775+ gchar* path = g_strdup((gchar *)libertined_container_home_path(container_id).toStdString().c_str());
776 if (g_file_test(path, G_FILE_TEST_EXISTS))
777 {
778 return path;
779@@ -183,20 +85,7 @@
780 gchar *
781 libertine_container_name(const gchar * container_id)
782 {
783- gchar * container_name = nullptr;
784- LibertineConfig config;
785- ContainerConfigList container_list(&config);
786- guint container_count = (guint)container_list.size();
787-
788- for (guint i = 0; i < container_count; ++i)
789- {
790- if (g_strcmp0(id_from_list_index(container_list, i), container_id) == 0)
791- {
792- QVariant name = container_list.data(container_list.index(i, 0), (int)ContainerConfigList::DataRole::ContainerName);
793- container_name = g_strdup(name.toString().toStdString().c_str());
794- break;
795- }
796- }
797-
798- return container_name;
799+ g_return_val_if_fail(container_id != nullptr, nullptr);
800+
801+ return g_strdup((gchar *)libertined_container_name(container_id).toStdString().c_str());
802 }
803
804=== modified file 'liblibertine/libertine.h'
805--- liblibertine/libertine.h 2016-09-06 12:36:47 +0000
806+++ liblibertine/libertine.h 2017-02-09 16:08:46 +0000
807@@ -1,5 +1,5 @@
808 /*
809- * Copyright 2015 Canonical Ltd.
810+ * Copyright 2015-2017 Canonical Ltd.
811 *
812 * This program is free software: you can redistribute it and/or modify it under
813 * the terms of the GNU General Public License, version 3, as published by the
814@@ -15,8 +15,8 @@
815 */
816 #include <glib.h>
817
818-#ifndef _LIBERTINE_COMMON_H_
819-#define _LIBERTINE_COMMON_H_
820+#ifndef _LIBLIBERTINE_H_
821+#define _LIBLIBERTINE_H_
822
823 #ifdef __cplusplus
824 extern "C" {
825@@ -80,4 +80,4 @@
826 }
827 #endif
828
829-#endif /* _LIBERTINE_COMMON_H_ */
830+#endif /* _LIBLIBERTINE_H_ */
831
832=== added file 'liblibertine/libertined.cpp'
833--- liblibertine/libertined.cpp 1970-01-01 00:00:00 +0000
834+++ liblibertine/libertined.cpp 2017-02-09 16:08:46 +0000
835@@ -0,0 +1,199 @@
836+/*
837+ * Copyright 2017 Canonical Ltd
838+ *
839+ * Libertine is free software: you can redistribute it and/or modify it under
840+ * the terms of the GNU General Public License, version 3, as published by the
841+ * Free Software Foundation.
842+ *
843+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
844+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
845+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
846+ *
847+ * You should have received a copy of the GNU General Public License
848+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
849+ */
850+
851+
852+#include "libertined.h"
853+
854+#include <QDBusMessage>
855+#include <QDBusInterface>
856+#include <QDebug>
857+#include <thread>
858+#include <chrono>
859+#include <QJsonDocument>
860+#include <QJsonArray>
861+#include <QJsonObject>
862+
863+namespace
864+{
865+static const auto SERVICE_INTERFACE = "com.canonical.libertine.Service";
866+static const auto PROGRESS_INTERFACE = "com.canonical.libertine.Service.Progress";
867+
868+static QVariantList
869+dbusCall(QDBusConnection const& bus, QString const& iface, QString const& path,
870+ QString const& method, QVariantList const& args = QVariantList())
871+{
872+ auto message = QDBusMessage::createMethodCall(SERVICE_INTERFACE, path, iface, method);
873+ message.setArguments(args);
874+ auto response = bus.call(message);
875+ if (response.type() == QDBusMessage::ErrorMessage)
876+ {
877+ qWarning() << "error calling result" << response.errorMessage();
878+ return QVariantList();
879+ }
880+
881+ return response.arguments();
882+}
883+
884+static bool
885+isRunning(QDBusConnection const& bus, QString const& path)
886+{
887+ auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "running", QVariantList());
888+
889+ if (args.isEmpty())
890+ {
891+ qWarning() << "lastError - no arguments?";
892+ return false;
893+ }
894+
895+ return args.first().toBool();
896+}
897+
898+static QString
899+result(QDBusConnection const& bus, QString const& path)
900+{
901+ auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "result");
902+
903+ if (args.isEmpty())
904+ {
905+ qWarning() << "lastError - no arguments?";
906+ return "";
907+ }
908+
909+ return args.first().toString();
910+}
911+
912+static QString
913+lastError(QDBusConnection const& bus, QString const& path)
914+{
915+ auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "last_error");
916+
917+ if (args.isEmpty())
918+ {
919+ qWarning() << "lastError - no arguments?";
920+ return "";
921+ }
922+
923+ return args.first().toString();
924+}
925+
926+static QString
927+call(QDBusConnection const& bus, QString const& method, QVariantList const& args)
928+{
929+ auto results = dbusCall(bus, SERVICE_INTERFACE, "/Manager", method, args);
930+
931+ if (results.isEmpty())
932+ {
933+ return QString();
934+ }
935+
936+ return qvariant_cast<QDBusObjectPath>(results.first()).path();
937+}
938+
939+static bool
940+waitForFinish(QDBusConnection const& bus, QString const& path)
941+{
942+ std::chrono::microseconds wait(500);
943+ for (auto i = 0; i < 2000; ++i)
944+ {
945+ if (!isRunning(bus, path))
946+ {
947+ return true;
948+ }
949+ std::this_thread::sleep_for(wait);
950+ }
951+ return !isRunning(bus, path);
952+}
953+
954+QString
955+container_info(char const* container_id, QString const& key)
956+{
957+ auto bus = QDBusConnection::sessionBus();
958+ auto path = call(bus, "container_info", QVariantList{QVariant(container_id)});
959+
960+ if (!waitForFinish(bus, path))
961+ {
962+ return QString();
963+ }
964+
965+ auto error = lastError(bus, path);
966+ if (!error.isEmpty())
967+ {
968+ qWarning() << "error:" << error;
969+ return QString();
970+ }
971+
972+ return QJsonDocument::fromJson(result(bus, path).toLatin1()).object().value(key).toString();
973+}
974+}
975+
976+QJsonArray
977+libertined_list()
978+{
979+ auto bus = QDBusConnection::sessionBus();
980+ auto path = call(bus, "list", QVariantList());
981+
982+ if (!waitForFinish(bus, path))
983+ {
984+ return QJsonArray();
985+ }
986+
987+ auto error = lastError(bus, path);
988+ if (!error.isEmpty())
989+ {
990+ qWarning() << "error:" << error;
991+ return QJsonArray();
992+ }
993+
994+ return QJsonDocument::fromJson(result(bus, path).toLatin1()).array();
995+}
996+
997+QJsonArray
998+libertined_list_app_ids(char const* container_id)
999+{
1000+ auto bus = QDBusConnection::sessionBus();
1001+ auto path = call(bus, "list_app_ids", QVariantList{QVariant(container_id)});
1002+
1003+ if (!waitForFinish(bus, path))
1004+ {
1005+ return QJsonArray();
1006+ }
1007+
1008+ auto error = lastError(bus, path);
1009+ if (!error.isEmpty())
1010+ {
1011+ qWarning() << "error:" << error;
1012+ return QJsonArray();
1013+ }
1014+
1015+ return QJsonDocument::fromJson(result(bus, path).toLatin1()).array();
1016+}
1017+
1018+QString
1019+libertined_container_path(char const* container_id)
1020+{
1021+ return container_info(container_id, "root");
1022+}
1023+
1024+QString
1025+libertined_container_home_path(char const* container_id)
1026+{
1027+ return container_info(container_id, "home");
1028+}
1029+
1030+QString
1031+libertined_container_name(char const* container_id)
1032+{
1033+ return container_info(container_id, "name");
1034+}
1035
1036=== added file 'liblibertine/libertined.h'
1037--- liblibertine/libertined.h 1970-01-01 00:00:00 +0000
1038+++ liblibertine/libertined.h 2017-02-09 16:08:46 +0000
1039@@ -0,0 +1,25 @@
1040+/*
1041+ * Copyright 2017 Canonical Ltd
1042+ *
1043+ * Libertine is free software: you can redistribute it and/or modify it under
1044+ * the terms of the GNU General Public License, version 3, as published by the
1045+ * Free Software Foundation.
1046+ *
1047+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
1048+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
1049+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
1050+ *
1051+ * You should have received a copy of the GNU General Public License
1052+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1053+ */
1054+
1055+#pragma once
1056+
1057+#include <QDBusConnection>
1058+#include <QJsonArray>
1059+
1060+QJsonArray libertined_list();
1061+QString libertined_container_path(char const* container_id);
1062+QString libertined_container_home_path(char const* container_id);
1063+QString libertined_container_name(char const* container_id);
1064+QJsonArray libertined_list_app_ids(char const* container_id);
1065
1066=== removed file 'python/libertine/AppDiscovery.py'
1067--- python/libertine/AppDiscovery.py 2016-12-07 21:50:36 +0000
1068+++ python/libertine/AppDiscovery.py 1970-01-01 00:00:00 +0000
1069@@ -1,220 +0,0 @@
1070-# Copyright 2015 Canonical Ltd.
1071-#
1072-# This program is free software: you can redistribute it and/or modify it
1073-# under the terms of the GNU General Public License version 3, as published
1074-# by the Free Software Foundation.
1075-#
1076-# This program is distributed in the hope that it will be useful, but
1077-# WITHOUT ANY WARRANTY; without even the implied warranties of
1078-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1079-# PURPOSE. See the GNU General Public License for more details.
1080-#
1081-# You should have received a copy of the GNU General Public License along
1082-# with this program. If not, see <http://www.gnu.org/licenses/>.
1083-
1084-from . import utils
1085-from xdg.BaseDirectory import xdg_data_dirs
1086-import configparser
1087-import glob
1088-import io
1089-import json
1090-import os
1091-import re
1092-import sys
1093-
1094-
1095-class IconCache(object):
1096- """
1097- Caches the names of all icon files available in the standard places (possibly
1098- in a container) and provides a search function to deliver icon file names
1099- matching a given icon name.
1100-
1101- See the `Freedesktop.org icon theme specification
1102- <http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html>`_
1103- for the detailed specification.
1104- """
1105-
1106- def __init__(self, root_path, file_loader=None):
1107- """
1108- :param root_path: Where to start the file scan.
1109- :type root_path: A valid filesystem path string.
1110- :param file_loader: A function that builds a cache of filenames.
1111- :type file_loader: Function returning a list of filenames.
1112- """
1113- if file_loader:
1114- self._icon_cache = file_loader(root_path)
1115- else:
1116- self._icon_cache = self._file_loader(root_path)
1117-
1118- def _get_icon_search_paths(self, root_path):
1119- """
1120- Gets a list of paths on which to search for icons.
1121-
1122- :param root_path: Where to start the file scan.
1123- :type root_path: A valid filesystem path string.
1124- :rtype: A list of filesystem patch to serarch for qualifying icon files.
1125- """
1126- icon_search_paths = []
1127- icon_search_paths.append(os.path.join(root_path, os.environ['HOME'], ".icons"))
1128- for d in reversed(xdg_data_dirs):
1129- icon_search_paths.append(os.path.join(root_path, d.lstrip('/'), "icons"))
1130- icon_search_paths.append(os.path.join(root_path, "usr/share/pixmaps"))
1131- return icon_search_paths
1132-
1133- def _file_loader(self, root_path):
1134- """
1135- Loads a cache of file names by scanning the filesystem rooted at
1136- ``root_path``.
1137-
1138- :param root_path: Where to start the file scan.
1139- :type root_path: A valid filesystem path.
1140- :rtype: A list of fully-qualified file paths.
1141- """
1142- file_names = []
1143- pattern = re.compile(r".*\.(png|svg|xpm)")
1144- for path in self._get_icon_search_paths(root_path):
1145- for base, dirs, files in os.walk(path):
1146- for file in files:
1147- if pattern.match(file):
1148- file_names.append(os.path.join(base, file))
1149- return file_names
1150-
1151- def _find_icon_files(self, icon_name):
1152- """
1153- Finds a list of file name strings matching the given icon name.
1154-
1155- :param icon_name: An icon name, pobably from a .desktop file.
1156- :rtype: A list of filename strings matching the icon name.
1157- """
1158- icon_file_names = []
1159- match_string = r"/" + os.path.splitext(icon_name)[0] + r"\....$"
1160- pattern = re.compile(match_string)
1161- for icon_file in self._icon_cache:
1162- if pattern.search(icon_file):
1163- icon_file_names.append(icon_file)
1164- return icon_file_names
1165-
1166- def expand_icons(self, desktop_icon_list):
1167- """
1168- Expands a string containing a list of icon names into a list of matching
1169- file names.
1170-
1171- :param desktop_icon_list: A string containing a list of icon names
1172- separated by semicolons.
1173- :rtype: A list of filename stings matching the icon names passed in.
1174-
1175- See `the Freedesktop.org desktop file specification
1176- <http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys>`_
1177- for more information.
1178- """
1179- if desktop_icon_list:
1180- icon_list = desktop_icon_list.split(';')
1181- if icon_list[0][0] == '/':
1182- return icon_list
1183- icon_files = []
1184- for i in icon_list:
1185- icon_files += self._find_icon_files(i)
1186- return icon_files
1187- return []
1188-
1189-
1190-def expand_mime_types(desktop_mime_types):
1191- if desktop_mime_types:
1192- return desktop_mime_types.split(';')
1193- return []
1194-
1195-
1196-class AppInfo(object):
1197-
1198- def __init__(self, desktop_file_name, config_entry, icon_cache):
1199- self.desktop_file_name = desktop_file_name
1200- self.name = config_entry.get('Name')
1201- if not self.name:
1202- raise RuntimeError("required Name attribute is missing")
1203- d = config_entry.get('NoDisplay')
1204- self.no_display = (d != None and d == 'true')
1205- self.exec_line = config_entry.get('Exec')
1206- if not self.exec_line:
1207- raise RuntimeError("required Exec attribute is missing")
1208- self.icons = icon_cache.expand_icons(config_entry.get('Icon'))
1209- self.mime_types = expand_mime_types(config_entry.get('MimeType'))
1210-
1211- def __str__(self):
1212- with io.StringIO() as ostr:
1213- print(self.name, file=ostr)
1214- print(" desktop_file={}".format(self.desktop_file_name), file=ostr)
1215- print(" no-display={}".format(self.no_display), file=ostr)
1216- print(" exec='{}'".format(self.exec_line), file=ostr)
1217- for icon in self.icons:
1218- print(" icon: {}".format(icon), file=ostr)
1219- for mime in self.mime_types:
1220- print(" mime: {}".format(mime), file=ostr)
1221- return ostr.getvalue()
1222-
1223- def to_json(self):
1224- return json.dumps(self.__dict__)
1225-
1226-
1227-def desktop_file_is_showable(desktop_entry):
1228- """
1229- Determines if a particular application entry should be reported: the entry
1230- can not be hidden and must be showable in Unity.
1231- """
1232- t = desktop_entry.get('Type')
1233- if t != 'Application':
1234- return False
1235- n = desktop_entry.get('Hidden')
1236- if n and n == 'true':
1237- return False
1238- n = desktop_entry.get('NoShowIn')
1239- if n:
1240- targets = n.split(';')
1241- if 'Unity' in targets:
1242- return False
1243- n = desktop_entry.get('OnlyShowIn')
1244- if n:
1245- targets = n.split(';')
1246- if 'Unity' not in targets:
1247- return False
1248- return True
1249-
1250-
1251-def get_app_info(desktop_path, icon_cache):
1252- for desktop_file_name in glob.glob(desktop_path):
1253- desktop_file = configparser.ConfigParser(strict=False, interpolation=None)
1254- try:
1255- desktop_file.read(desktop_file_name)
1256- desktop_entry = desktop_file['Desktop Entry']
1257- if desktop_file_is_showable(desktop_entry):
1258- yield AppInfo(desktop_file_name, desktop_entry, icon_cache)
1259- except Exception as ex:
1260- utils.get_logger().error("error processing {}: {}".format(desktop_file_name, ex))
1261-
1262-
1263-class AppLauncherCache(object):
1264- """
1265- Caches a list of application launcher information (derived from .desktop
1266- files installed in a container.
1267- """
1268-
1269- def __init__(self, name, root_path):
1270- self.name = name
1271- self.app_launchers = []
1272- icon_cache = IconCache(root_path)
1273- for dir in reversed(xdg_data_dirs):
1274- path = os.path.join(root_path, dir.lstrip('/'), "applications")
1275- for app_info in get_app_info(os.path.join(path, "*.desktop"), icon_cache):
1276- self.app_launchers.append(app_info)
1277-
1278- def __str__(self):
1279- with io.StringIO() as ostr:
1280- print("{}\n".format(self.name), file=ostr)
1281- for app_info in self.app_launchers:
1282- print(" {}".format(app_info), file=ostr)
1283- return ostr.getvalue()
1284-
1285- def to_json(self):
1286- return json.dumps(self,
1287- default=lambda o: o.__dict__,
1288- sort_keys=True,
1289- indent=4)
1290
1291=== modified file 'python/libertine/ChrootContainer.py'
1292--- python/libertine/ChrootContainer.py 2017-01-17 20:19:30 +0000
1293+++ python/libertine/ChrootContainer.py 2017-02-09 16:08:46 +0000
1294@@ -1,4 +1,4 @@
1295-# Copyright 2015-2016 Canonical Ltd.
1296+# Copyright 2015-2017 Canonical Ltd.
1297 #
1298 # This program is free software: you can redistribute it and/or modify it
1299 # under the terms of the GNU General Public License version 3, as published
1300@@ -47,10 +47,8 @@
1301 A concrete container type implemented using a plain old chroot.
1302 """
1303
1304- def __init__(self, container_id):
1305- super().__init__(container_id)
1306- self.container_type = "chroot"
1307- self._window_manager = None
1308+ def __init__(self, container_id, config):
1309+ super().__init__(container_id, 'chroot', config)
1310 # FIXME: Disabling seccomp is a temporary measure until we fully understand why we need
1311 # it or figure out when we need it.
1312 os.environ['PROOT_NO_SECCOMP'] = '1'
1313@@ -112,7 +110,7 @@
1314 fd.write(archive + self.installed_release + " multiverse\n")
1315 fd.write(archive + self.installed_release + "-updates multiverse\n")
1316
1317- utils.create_libertine_user_data_dir(self.container_id)
1318+ self._create_libertine_user_data_dir()
1319
1320 self.update_locale()
1321
1322@@ -140,7 +138,7 @@
1323 self.update_packages()
1324
1325 # Check if the container was created as root and chown the user directories as necessary
1326- chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id))
1327+ chown_recursive_dirs(utils.get_libertine_container_home_dir(self.container_id))
1328
1329 super().create_libertine_container()
1330
1331@@ -186,7 +184,7 @@
1332 # Bind-mount common XDG direcotries
1333 bind_mounts = (
1334 " -b %s:%s"
1335- % (utils.get_libertine_container_userdata_dir_path(self.container_id), home_path)
1336+ % (utils.get_libertine_container_home_dir(self.container_id), home_path)
1337 )
1338
1339 mounts = self._sanitize_bind_mounts(utils.get_common_xdg_user_directories() + \
1340@@ -227,15 +225,10 @@
1341 proot_cmd = self._build_proot_command()
1342
1343 args = shlex.split(proot_cmd)
1344- args.extend(self.setup_window_manager(enable_toolbars=True))
1345- self._window_manager = psutil.Popen(args, env=environ)
1346-
1347- args = shlex.split(proot_cmd)
1348 args.extend(app_exec_line)
1349 return psutil.Popen(args, env=environ)
1350
1351 def finish_application(self, app):
1352- utils.terminate_window_manager(self._window_manager)
1353 app.wait()
1354
1355 def _run_ldconfig(self):
1356
1357=== modified file 'python/libertine/ContainersConfig.py'
1358--- python/libertine/ContainersConfig.py 2017-01-12 20:46:05 +0000
1359+++ python/libertine/ContainersConfig.py 2017-02-09 16:08:46 +0000
1360@@ -1,4 +1,4 @@
1361-# Copyright 2016 Canonical Ltd.
1362+# Copyright 2016-2017 Canonical Ltd.
1363 #
1364 # This program is free software: you can redistribute it and/or modify it
1365 # under the terms of the GNU General Public License version 3, as published
1366@@ -102,7 +102,8 @@
1367 if not container:
1368 return
1369
1370- if type(value) is str:
1371+ if (type(value) is str or
1372+ type(value) is bool):
1373 container[key] = value
1374 elif type(value) is dict:
1375 if key not in container:
1376@@ -241,6 +242,9 @@
1377 if write_json:
1378 write_container_config_file(self.container_list)
1379
1380+ def get_containers(self):
1381+ return [c["id"] for c in self.container_list.get('containerList', [])]
1382+
1383 """
1384 Operations for the container itself.
1385 """
1386@@ -302,6 +306,12 @@
1387 def get_container_locale(self, container_id):
1388 return self._get_value_by_key(container_id, 'locale')
1389
1390+ def get_container_name(self, container_id):
1391+ return self._get_value_by_key(container_id, 'name')
1392+
1393+ def get_container_install_status(self, container_id):
1394+ return self._get_value_by_key(container_id, 'installStatus')
1395+
1396 """
1397 Operations for archive (PPA) maintenance in a Libertine container.
1398 """
1399@@ -375,6 +385,15 @@
1400 self._delete_array_object_by_value(container_id, 'bindMounts', mount_path)
1401
1402 """
1403+ Operations for setting container freeze on stop command.
1404+ """
1405+ def update_freeze_on_stop(self, container_id, freeze_on_stop=True):
1406+ self._set_value_by_key(container_id, 'freezeOnStop', freeze_on_stop)
1407+
1408+ def get_freeze_on_stop(self, container_id):
1409+ return self._get_value_by_key(container_id, 'freezeOnStop') or False
1410+
1411+ """
1412 Fetcher functions for various configuration information.
1413 """
1414 def get_container_distro(self, container_id):
1415
1416=== modified file 'python/libertine/HostInfo.py'
1417--- python/libertine/HostInfo.py 2017-01-12 20:46:05 +0000
1418+++ python/libertine/HostInfo.py 2017-02-09 16:08:46 +0000
1419@@ -12,6 +12,7 @@
1420 # You should have received a copy of the GNU General Public License along
1421 # with this program. If not, see <http://www.gnu.org/licenses/>.
1422
1423+import libertine.utils
1424 import locale
1425 import lsb_release
1426 import os
1427@@ -25,7 +26,10 @@
1428
1429 def select_container_type_by_kernel(self):
1430 if self.has_lxc_support():
1431- return "lxc"
1432+ if libertine.utils.is_snap_environment():
1433+ return "lxd"
1434+ else:
1435+ return "lxc"
1436 else:
1437 return "chroot"
1438
1439@@ -64,6 +68,9 @@
1440 return None
1441
1442 def get_host_architecture(self):
1443+ if 'ARCH' in os.environ:
1444+ return os.environ['ARCH']
1445+
1446 dpkg = subprocess.Popen(['dpkg', '--print-architecture'],
1447 stdout=subprocess.PIPE,
1448 universal_newlines=True)
1449
1450=== modified file 'python/libertine/Libertine.py'
1451--- python/libertine/Libertine.py 2017-01-17 20:28:15 +0000
1452+++ python/libertine/Libertine.py 2017-02-09 16:08:46 +0000
1453@@ -1,4 +1,4 @@
1454-# Copyright 2015-2016 Canonical Ltd.
1455+# Copyright 2015-2017 Canonical Ltd.
1456 #
1457 # This program is free software: you can redistribute it and/or modify it
1458 # under the terms of the GNU General Public License version 3, as published
1459@@ -12,8 +12,6 @@
1460 # You should have received a copy of the GNU General Public License along
1461 # with this program. If not, see <http://www.gnu.org/licenses/>.
1462
1463-from .AppDiscovery import AppLauncherCache
1464-from gi.repository import Libertine
1465 import abc
1466 import contextlib
1467 import os
1468@@ -81,17 +79,14 @@
1469
1470 :param container_id: The machine-readable container name.
1471 """
1472- def __init__(self, container_id, containers_config=None):
1473- if containers_config is None:
1474- containers_config = ContainersConfig()
1475-
1476- self.container_type = 'unknown'
1477+ def __init__(self, container_id, container_type, config):
1478+ self.container_type = container_type
1479 self.container_id = container_id
1480+ self._config = config
1481 self.root_path = utils.get_libertine_container_rootfs_path(self.container_id)
1482- self.locale = containers_config.get_container_locale(container_id)
1483+ self.locale = self._config.get_container_locale(container_id)
1484 self.language = self._get_language_from_locale()
1485- self.default_packages = ['matchbox-window-manager',
1486- 'libnss-extrausers',
1487+ self.default_packages = ['libnss-extrausers',
1488 'humanity-icon-theme',
1489 'maliit-inputcontext-gtk2',
1490 'maliit-inputcontext-gtk3',
1491@@ -126,16 +121,6 @@
1492 utils.get_logger().error("%s" % e)
1493 return False
1494
1495- def setup_window_manager(self, enable_toolbars=False):
1496- if self._binary_exists('matchbox-window-manager'):
1497- if enable_toolbars:
1498- return ['matchbox-window-manager']
1499-
1500- return ['matchbox-window-manager', '-use_titlebar', 'no']
1501- else:
1502- return ['compiz']
1503-
1504-
1505 def check_language_support(self):
1506 if not self._binary_exists('check-language-support'):
1507 self.install_package('language-selector-common', update_cache=False)
1508@@ -185,7 +170,7 @@
1509 'running' state, the meaning of which depends on the type of the
1510 container.
1511 """
1512- pass
1513+ return True
1514
1515 def stop_container(self):
1516 """
1517@@ -193,6 +178,12 @@
1518 """
1519 pass
1520
1521+ def restart_container(self):
1522+ """
1523+ Restarts the container.
1524+ """
1525+ pass
1526+
1527 @abc.abstractmethod
1528 def run_in_container(self, command_string):
1529 """
1530@@ -247,10 +238,10 @@
1531 self.delete_file_in_container(dest)
1532
1533 return ret
1534- else:
1535- if no_dialog:
1536- os.environ['DEBIAN_FRONTEND'] = 'teletype'
1537- ret = self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0
1538+
1539+ if no_dialog:
1540+ os.environ['DEBIAN_FRONTEND'] = 'teletype'
1541+ ret = self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0
1542
1543 self.check_language_support()
1544
1545@@ -313,19 +304,26 @@
1546 """
1547 The human-readable name of the container.
1548 """
1549- name = Libertine.container_name(self.container_id)
1550- if not name:
1551- name = 'Unknown'
1552- return name
1553+ return self._config.get_container_name(self.container_id) or 'Unknown'
1554+
1555+ def _create_libertine_user_data_dir(self):
1556+ user_data = utils.get_libertine_container_home_dir(self.container_id)
1557+
1558+ if not os.path.exists(user_data):
1559+ os.makedirs(user_data)
1560+
1561+ config_path = os.path.join(user_data, ".config", "dconf")
1562+
1563+ if not os.path.exists(config_path):
1564+ os.makedirs(config_path)
1565
1566
1567 class LibertineMock(BaseContainer):
1568 """
1569 A concrete mock container type. Used for unit testing.
1570 """
1571- def __init__(self, container_id, containers_config=None):
1572- super().__init__(container_id, containers_config)
1573- self.container_type = "mock"
1574+ def __init__(self, container_id, config):
1575+ super().__init__(container_id, 'mock', config)
1576
1577 def create_libertine_container(self, password=None, multiarch=False):
1578 return True
1579@@ -354,6 +352,9 @@
1580 def finish_application(self, app):
1581 app.wait()
1582
1583+ def start_container(self):
1584+ return True
1585+
1586
1587 class ContainerRunning(contextlib.ExitStack):
1588 """
1589@@ -364,7 +365,9 @@
1590 """
1591 def __init__(self, container):
1592 super().__init__()
1593- container.start_container()
1594+ if not container.start_container():
1595+ raise RuntimeError("Container failed to start.")
1596+
1597 self.callback(lambda: container.stop_container())
1598
1599
1600@@ -389,13 +392,13 @@
1601
1602 if container_type == "lxc":
1603 from libertine.LxcContainer import LibertineLXC
1604- self.container = LibertineLXC(container_id)
1605+ self.container = LibertineLXC(container_id, self.containers_config)
1606 elif container_type == "lxd":
1607 from libertine.LxdContainer import LibertineLXD
1608 self.container = LibertineLXD(container_id, self.containers_config)
1609 elif container_type == "chroot":
1610 from libertine.ChrootContainer import LibertineChroot
1611- self.container = LibertineChroot(container_id)
1612+ self.container = LibertineChroot(container_id, self.containers_config)
1613 elif container_type == "mock":
1614 self.container = LibertineMock(container_id, self.containers_config)
1615 else:
1616@@ -480,6 +483,12 @@
1617 except RuntimeError as e:
1618 return handle_runtime_error(e)
1619
1620+ def restart_libertine_container(self):
1621+ """
1622+ Restarts a frozen container.
1623+ """
1624+ return self.container.restart_container()
1625+
1626 def connect(self):
1627 """
1628 Connects to the container in preparation to launch an application. May
1629@@ -509,22 +518,23 @@
1630 """
1631 self.container.finish_application(app)
1632
1633- def list_app_launchers(self, use_json=False):
1634- """
1635- Enumerates all application launchers (based on .desktop files) available
1636- in the container.
1637-
1638- :param use_json: Indicates the returned string should be i JSON format.
1639- The default format is some human-readble format.
1640- :rtype: A printable string containing a list of application launchers
1641- available in the container.
1642- """
1643- if use_json:
1644- return AppLauncherCache(self.container.name,
1645- self.container.root_path).to_json()
1646- else:
1647- return str(AppLauncherCache(self.container.name,
1648- self.container.root_path))
1649+ def list_app_ids(self):
1650+ """
1651+ Finds application ids (based on .desktop files) available in the
1652+ container.
1653+
1654+ :rtype: A list of app ids consumable by tools such as ubuntu-app-launch
1655+ """
1656+ home = utils.get_libertine_container_home_dir(self.container_id)
1657+ app_ids = []
1658+ for apps_dir in ["{}/usr/share/applications".format(self.root_path),
1659+ "{}/usr/local/share/applications".format(self.root_path),
1660+ "{}/.local/share/applications".format(home)]:
1661+ if os.path.exists(apps_dir):
1662+ for root, dirs, files in os.walk(apps_dir):
1663+ app_ids.extend(["{}_{}_0.0".format(self.container_id, f[:-8]) for f in files if f.endswith(".desktop")])
1664+
1665+ return app_ids
1666
1667 def exec_command(self, exec_line):
1668 """
1669
1670=== modified file 'python/libertine/LxcContainer.py'
1671--- python/libertine/LxcContainer.py 2017-01-17 15:41:51 +0000
1672+++ python/libertine/LxcContainer.py 2017-02-09 16:08:46 +0000
1673@@ -1,4 +1,4 @@
1674-# Copyright 2015-2016 Canonical Ltd.
1675+# Copyright 2015-2017 Canonical Ltd.
1676 #
1677 # This program is free software: you can redistribute it and/or modify it
1678 # under the terms of the GNU General Public License version 3, as published
1679@@ -22,17 +22,15 @@
1680 import subprocess
1681 import sys
1682 import tempfile
1683+import time
1684
1685-from .lifecycle import LifecycleResult
1686 from .Libertine import BaseContainer
1687+from .service.manager import LIBERTINE_MANAGER_NAME, LIBERTINE_STORE_PATH
1688 from . import utils, HostInfo
1689
1690
1691 home_path = os.environ['HOME']
1692
1693-LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxcManager"
1694-LIBERTINE_LXC_MANAGER_PATH = "/LxcManager"
1695-
1696
1697 def _check_lxc_net_entry(entry):
1698 lxc_net_file = open('/etc/lxc/lxc-usernet')
1699@@ -57,14 +55,6 @@
1700 return os.path.join(home_path, '.config', 'lxc')
1701
1702
1703-def get_lxc_manager_dbus_name():
1704- return LIBERTINE_LXC_MANAGER_NAME
1705-
1706-
1707-def get_lxc_manager_dbus_path():
1708- return LIBERTINE_LXC_MANAGER_PATH
1709-
1710-
1711 def lxc_container(container_id):
1712 config_path = utils.get_libertine_containers_dir_path()
1713 if not os.path.exists(config_path):
1714@@ -101,22 +91,43 @@
1715 def lxc_start(container):
1716 lxc_log_file = get_logfile(container)
1717
1718- if not container.start():
1719- return LifecycleResult("Container failed to start.")
1720+ if container.state == 'STOPPED':
1721+ if not container.start():
1722+ utils.get_logger().error("Container failed to start.")
1723+ return False
1724+ elif container.state == 'FROZEN':
1725+ if not container.unfreeze():
1726+ utils.get_logger().error("Container failed to unfreeze.")
1727+ return False
1728
1729 if not container.wait("RUNNING", 10):
1730- return LifecycleResult("Container failed to enter the RUNNING state.")
1731+ utils.get_logger().error("Container failed to enter the RUNNING state.")
1732+ return False
1733
1734 if not container.get_ips(timeout=30):
1735 lxc_stop(container)
1736- return LifecycleResult("Not able to connect to the network.")
1737-
1738- return LifecycleResult()
1739-
1740-
1741-def lxc_stop(container):
1742- if container.running:
1743+ utils.get_logger().error("Not able to connect to the network.")
1744+ return False
1745+
1746+ return True
1747+
1748+
1749+def lxc_stop(container, freeze_on_stop=False):
1750+ if container.state == 'STOPPED' or not container.running:
1751+ return True
1752+
1753+ if freeze_on_stop:
1754+ container.freeze()
1755+ if not container.wait("FROZEN", 10):
1756+ utils.get_logger().error("Container failed to enter the FROZEN state.")
1757+ return False
1758+ else:
1759 container.stop()
1760+ if not container.wait("STOPPED", 10):
1761+ utils.get_logger().error("Container failed to enter the STOPPED state.")
1762+ return False
1763+
1764+ return True
1765
1766
1767 class EnvLxcSettings(contextlib.ExitStack):
1768@@ -149,21 +160,62 @@
1769 A concrete container type implemented using an LXC container.
1770 """
1771
1772- def __init__(self, container_id):
1773- super().__init__(container_id)
1774- self.container_type = "lxc"
1775+ def __init__(self, container_id, config):
1776+ super().__init__(container_id, 'lxc', config)
1777 self.container = lxc_container(container_id)
1778 self.lxc_manager_interface = None
1779- self.window_manager = None
1780 self.host_info = HostInfo.HostInfo()
1781-
1782- utils.set_session_dbus_env_var()
1783-
1784- try:
1785- bus = dbus.SessionBus()
1786- self.lxc_manager_interface = bus.get_object(get_lxc_manager_dbus_name(), get_lxc_manager_dbus_path())
1787- except dbus.exceptions.DBusException:
1788- pass
1789+ self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)
1790+
1791+ if utils.set_session_dbus_env_var():
1792+ try:
1793+ bus = dbus.SessionBus()
1794+ self.lxc_manager_interface = bus.get_object(LIBERTINE_MANAGER_NAME, LIBERTINE_STORE_PATH)
1795+ except dbus.exceptions.DBusException:
1796+ pass
1797+
1798+ def _setup_pulse(self):
1799+ pulse_socket_path = os.path.join(utils.get_libertine_runtime_dir(), 'pulse_socket')
1800+
1801+ os.environ['PULSE_SERVER'] = pulse_socket_path
1802+
1803+ lsof_cmd = 'lsof -n %s' % pulse_socket_path
1804+ args = shlex.split(lsof_cmd)
1805+ lsof = subprocess.Popen(args, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
1806+ lsof.wait()
1807+
1808+ if not os.path.exists(pulse_socket_path) or lsof.returncode == 1:
1809+ pactl_cmd = (
1810+ 'pactl load-module module-native-protocol-unix auth-anonymous=1 socket=%s'
1811+ % pulse_socket_path)
1812+ args = shlex.split(pactl_cmd)
1813+ subprocess.Popen(args).wait()
1814+
1815+ def _dynamic_bind_mounts(self):
1816+ self._config.refresh_database()
1817+ mounts = self._sanitize_bind_mounts(utils.get_common_xdg_user_directories() + \
1818+ self._config.get_container_bind_mounts(self.container_id))
1819+
1820+ data_dir = utils.get_libertine_container_home_dir(self.container_id)
1821+ for user_dir in utils.generate_binding_directories(mounts, home_path):
1822+ if os.path.isabs(user_dir[1]):
1823+ path = user_dir[1].strip('/')
1824+ fullpath = os.path.join(utils.get_libertine_container_rootfs_path(self.container_id), path)
1825+ else:
1826+ path = "{}/{}".format(home_path.strip('/'), user_dir[1])
1827+ fullpath = os.path.join(data_dir, user_dir[1])
1828+
1829+ os.makedirs(fullpath, exist_ok=True)
1830+
1831+ utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, self.container_id))
1832+ xdg_user_dir_entry = (
1833+ "%s %s none bind,create=dir,optional"
1834+ % (user_dir[0], path)
1835+ )
1836+ self.container.append_config_item("lxc.mount.entry", xdg_user_dir_entry)
1837+
1838+ def _sanitize_bind_mounts(self, mounts):
1839+ return [mount.replace(" ", "\\040") for mount in mounts]
1840
1841 def timezone_needs_update(self):
1842 with open(os.path.join(self.root_path, 'etc', 'timezone'), 'r') as fd:
1843@@ -171,19 +223,54 @@
1844
1845 def start_container(self):
1846 if self.lxc_manager_interface:
1847- result = LifecycleResult.from_dict(self.lxc_manager_interface.operation_start(self.container_id))
1848- else:
1849- result = lxc_start(self.container)
1850-
1851- if not result.success:
1852- _dump_lxc_log(result.logfile)
1853- raise RuntimeError(result.error)
1854+ retries = 0
1855+ while not self.lxc_manager_interface.container_operation_start(self.container_id):
1856+ retries += 1
1857+ if retries > 5:
1858+ return False
1859+ time.sleep(.5)
1860+
1861+ if self.container.state == 'RUNNING':
1862+ return True
1863+
1864+ if self.container.state == 'STOPPED':
1865+ self._dynamic_bind_mounts()
1866+ self._setup_pulse()
1867+
1868+ if not lxc_start(self.container):
1869+ _dump_lxc_log(get_logfile(self.container))
1870+ return False
1871+
1872+ return True
1873
1874 def stop_container(self):
1875 if self.lxc_manager_interface:
1876- self.lxc_manager_interface.operation_stop(self.container_id)
1877+ if self.lxc_manager_interface.container_operation_finished(self.container_id):
1878+ if not lxc_stop(self.container, self._freeze_on_stop):
1879+ return False
1880+ self.lxc_manager_interface.container_stopped(self.container_id)
1881+ return True
1882+ else:
1883+ return False
1884 else:
1885- lxc_stop(self.container)
1886+ return lxc_stop(self.container, self._freeze_on_stop)
1887+
1888+ def restart_container(self):
1889+ if self.container.state != 'FROZEN':
1890+ utils.get_logger().warning("Container {} is not frozen. Cannot restart.".format(self.container_id))
1891+ return False
1892+
1893+ orig_freeze = self._freeze_on_stop
1894+ self._freeze_on_stop = False
1895+
1896+ # We never want to use the manager when restarting.
1897+ self.lxc_manager_interface = None
1898+
1899+ if not (self.stop_container() and self.start_container()):
1900+ return False
1901+
1902+ self._freeze_on_stop = orig_freeze
1903+ return self.stop_container()
1904
1905 def run_in_container(self, command_string):
1906 cmd_args = shlex.split(command_string)
1907@@ -238,7 +325,7 @@
1908
1909 self.container.load_config(config_file)
1910
1911- utils.create_libertine_user_data_dir(self.container_id)
1912+ self._create_libertine_user_data_dir()
1913
1914 with EnvLxcSettings():
1915 lxc_logfile = get_logfile(self.container)
1916@@ -253,10 +340,7 @@
1917 self.create_libertine_config()
1918
1919 utils.get_logger().info("starting container ...")
1920- try:
1921- self.start_container()
1922- except RuntimeError as e:
1923- utils.get_logger().error("Container failed to start: %s" % e)
1924+ if not self.start_container():
1925 self.destroy_libertine_container()
1926 return False
1927
1928@@ -290,7 +374,7 @@
1929 user_id = os.getuid()
1930 home_entry = (
1931 "%s %s none bind,create=dir"
1932- % (utils.get_libertine_container_userdata_dir_path(self.container_id),
1933+ % (utils.get_libertine_container_home_dir(self.container_id),
1934 home_path.strip('/'))
1935 )
1936
1937@@ -325,19 +409,10 @@
1938 os.environ.clear()
1939 os.environ.update(environ)
1940
1941- result = LifecycleResult.from_dict(self.lxc_manager_interface.app_start(self.container_id))
1942-
1943- if not result.success:
1944- _dump_lxc_log(get_logfile(self.container))
1945- utils.get_logger().error("%s" % result.error)
1946+ if not self.start_container():
1947+ self.lxc_manager_interface.container_stopped(self.container_id)
1948 return
1949
1950- self.window_manager = self.container.attach(lxc.attach_run_command,
1951- self.setup_window_manager())
1952-
1953- # Setup pulse to work inside the container
1954- os.environ['PULSE_SERVER'] = utils.get_libertine_lxc_pulse_socket_path()
1955-
1956 app_launch_cmd = "sudo -E -u " + os.environ['USER'] + " env PATH=" + os.environ['PATH']
1957 cmd = shlex.split(app_launch_cmd)
1958 app = self.container.attach(lxc.attach_run_command,
1959@@ -347,7 +422,4 @@
1960 def finish_application(self, app):
1961 os.waitpid(app.pid, 0)
1962
1963- utils.terminate_window_manager(psutil.Process(self.window_manager))
1964-
1965- # Tell libertine-lxc-manager that the app has stopped.
1966- self.lxc_manager_interface.app_stop(self.container_id)
1967+ self.stop_container()
1968
1969=== modified file 'python/libertine/LxdContainer.py'
1970--- python/libertine/LxdContainer.py 2017-01-17 20:35:37 +0000
1971+++ python/libertine/LxdContainer.py 2017-02-09 16:08:46 +0000
1972@@ -1,4 +1,4 @@
1973-# Copyright 2016 Canonical Ltd.
1974+# Copyright 2016-2017 Canonical Ltd.
1975 #
1976 # This program is free software: you can redistribute it and/or modify it
1977 # under the terms of the GNU General Public License version 3, as published
1978@@ -22,20 +22,8 @@
1979 import subprocess
1980 import time
1981
1982-from .lifecycle import LifecycleResult
1983 from libertine import Libertine, utils, HostInfo
1984-
1985-
1986-LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxdManager"
1987-LIBERTINE_LXC_MANAGER_PATH = "/LxdManager"
1988-
1989-
1990-def get_lxd_manager_dbus_name():
1991- return LIBERTINE_LXC_MANAGER_NAME
1992-
1993-
1994-def get_lxd_manager_dbus_path():
1995- return LIBERTINE_LXC_MANAGER_PATH
1996+from .service.manager import LIBERTINE_MANAGER_NAME, LIBERTINE_STORE_PATH
1997
1998
1999 def _get_devices_map():
2000@@ -74,6 +62,10 @@
2001 return source
2002
2003 def _setup_lxd():
2004+ if utils.is_snap_environment():
2005+ utils.get_logger().warning("Running in snap environment, skipping automatic lxd setup.")
2006+ return True
2007+
2008 utils.get_logger().info("Running LXD setup.")
2009 import pexpect
2010 child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'})
2011@@ -113,7 +105,7 @@
2012 [ -n /dev/video0 ] && chgrp video /dev/video0
2013 '''[1:-1]
2014 container.files.put('/usr/bin/libertine-lxd-mount-update', script.format(uid=uid, username=username).encode('utf-8'))
2015- container.execute(shlex.split('chmod 755 /usr/bin/libertine-lxd-mount-update'))
2016+ subprocess.Popen(shlex.split("lxc exec {} -- chmod 755 /usr/bin/libertine-lxd-mount-update".format(container.name)))
2017
2018
2019 def lxd_container(client, container_id):
2020@@ -125,7 +117,9 @@
2021
2022 def _wait_for_network(container):
2023 for retries in range(0, 10):
2024- out, err = container.execute(shlex.split('ping -c 1 ubuntu.com'))
2025+ ping = subprocess.Popen(shlex.split("lxc exec {} -- ping -c 1 ubuntu.com".format(container.name)),
2026+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
2027+ out, err = ping.communicate()
2028 if out:
2029 utils.get_logger().info("Network connection active")
2030 return True
2031@@ -134,28 +128,40 @@
2032
2033
2034 def lxd_start(container):
2035- if container.status != 'Running':
2036+ if container.status == 'Stopped':
2037 container.start(wait=True)
2038+ elif container.status == 'Frozen':
2039+ container.unfreeze(wait=True)
2040
2041 container.sync(rollback=True) # required for pylxd=2.0.x
2042
2043 if container.status != 'Running':
2044- return LifecycleResult("Container {} failed to start".format(container.name))
2045-
2046- return LifecycleResult()
2047-
2048-
2049-def lxd_stop(container, wait):
2050+ utils.get_logger().error("Container {} failed to start".format(container.name))
2051+ return False
2052+
2053+ return True
2054+
2055+
2056+def lxd_stop(container, wait=True, freeze_on_stop=False):
2057 if container.status == 'Stopped':
2058- return LifecycleResult()
2059-
2060- container.stop(wait=wait)
2061+ return True
2062+
2063+ if freeze_on_stop:
2064+ container.freeze(wait=wait)
2065+ else:
2066+ container.stop(wait=wait)
2067+
2068 container.sync(rollback=True) # required for pylxd=2.0.x
2069
2070- if wait and container.status != 'Stopped':
2071- return LifecycleResult("Container {} failed to stop".format(container.name))
2072+ if wait:
2073+ if freeze_on_stop and container.status != 'Frozen':
2074+ utils.get_logger().error("Container {} failed to freeze".format(container.name))
2075+ return False
2076+ elif not freeze_on_stop and container.status != 'Stopped':
2077+ utils.get_logger().error("Container {} failed to stop".format(container.name))
2078+ return False
2079
2080- return LifecycleResult()
2081+ return True
2082
2083
2084 def _lxd_save(entity, error, wait=True):
2085@@ -210,9 +216,8 @@
2086 f.write(container.files.get(filepath))
2087
2088
2089-def update_bind_mounts(container, config):
2090- home_path = os.environ['HOME']
2091- userdata_dir = utils.get_libertine_container_userdata_dir_path(container.name)
2092+def update_bind_mounts(container, config, home_path):
2093+ userdata_dir = utils.get_libertine_container_home_dir(container.name)
2094
2095 container.devices.clear()
2096 container.devices['root'] = {'type': 'disk', 'path': '/'}
2097@@ -235,9 +240,13 @@
2098 run_user = '/run/user/{}'.format(os.getuid())
2099 container.devices[run_user] = {'source': run_user, 'path': '/var/tmp{}'.format(run_user), 'type': 'disk'}
2100
2101- mounts = utils.get_common_xdg_user_directories() + \
2102- config.get_container_bind_mounts(container.name)
2103- for user_dir in utils.generate_binding_directories(mounts, home_path):
2104+ mounts = config.get_container_bind_mounts(container.name)
2105+ if utils.is_snap_environment():
2106+ mounts += [os.path.join(home_path, d) for d in ["Documents", "Downloads", "Music", "Videos", "Pictures"]]
2107+ else:
2108+ mounts += utils.get_common_xdg_user_directories()
2109+
2110+ for user_dir in utils.generate_binding_directories(mounts, home_path.rstrip('/')):
2111 if not os.path.exists(user_dir[0]):
2112 utils.get_logger().warning('Bind-mount path \'{}\' does not exist.'.format(user_dir[0]))
2113 continue
2114@@ -251,7 +260,7 @@
2115
2116 utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container.name))
2117
2118- container.devices[user_dir[1]] = {
2119+ container.devices[user_dir[1] or user_dir[0]] = {
2120 'source': _readlink(user_dir[0]),
2121 'path': path,
2122 'optional': 'true',
2123@@ -275,29 +284,33 @@
2124 client.profiles.create('libertine', config={'raw.idmap': 'both 1000 1000'}, devices=_get_devices_map())
2125
2126
2127+def env_home_path():
2128+ if utils.is_snap_environment():
2129+ return '/home/{}'.format(os.environ['USER'])
2130+ return os.environ['HOME']
2131+
2132+
2133 class LibertineLXD(Libertine.BaseContainer):
2134 def __init__(self, name, config):
2135- super().__init__(name)
2136- self._id = name
2137- self._config = config
2138+ super().__init__(name, 'lxd', config)
2139 self._host_info = HostInfo.HostInfo()
2140 self._container = None
2141- self._matchbox_pid = None
2142+ self._manager = None
2143+ self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)
2144
2145 if not _setup_lxd():
2146 raise Exception("Failed to setup lxd.")
2147
2148 self._client = pylxd.Client()
2149- self._window_manager = None
2150
2151- utils.set_session_dbus_env_var()
2152 try:
2153- bus = dbus.SessionBus()
2154- self._manager = bus.get_object(get_lxd_manager_dbus_name(), get_lxd_manager_dbus_path())
2155+ if utils.set_session_dbus_env_var():
2156+ bus = dbus.SessionBus()
2157+ self._manager = bus.get_object(LIBERTINE_MANAGER_NAME, LIBERTINE_STORE_PATH)
2158+ except PermissionError as e:
2159+ utils.get_logger().warning("Failed to set dbus session env var")
2160 except dbus.exceptions.DBusException:
2161 utils.get_logger().warning("D-Bus Service not found.")
2162- self._manager = None
2163-
2164
2165 def create_libertine_container(self, password=None, multiarch=False):
2166 if self._try_get_container():
2167@@ -306,17 +319,17 @@
2168
2169 update_libertine_profile(self._client)
2170
2171- utils.get_logger().info("Creating container '%s' with distro '%s'" % (self._id, self.installed_release))
2172+ utils.get_logger().info("Creating container '%s' with distro '%s'" % (self.container_id, self.installed_release))
2173 create = subprocess.Popen(shlex.split('lxc launch ubuntu-daily:{distro} {id} --profile '
2174 'default --profile libertine'.format(
2175- distro=self.installed_release, id=self._id)))
2176+ distro=self.installed_release, id=self.container_id)))
2177 if create.wait() is not 0:
2178- utils.get_logger().error("Creating container '{}' failed with code '{}'".format(self._id, create.returncode))
2179+ utils.get_logger().error("Creating container '{}' failed with code '{}'".format(self.container_id, create.returncode))
2180 return False
2181
2182 self._try_get_container()
2183 _sync_application_dirs_to_host(self._container)
2184- update_bind_mounts(self._container, self._config)
2185+ update_bind_mounts(self._container, self._config, env_home_path())
2186
2187 self.update_locale()
2188
2189@@ -328,18 +341,18 @@
2190 self.run_in_container("mkdir -p /home/{}".format(username))
2191 self.run_in_container("chown {0}:{0} /home/{0}".format(username))
2192
2193- utils.create_libertine_user_data_dir(self._id)
2194+ self._create_libertine_user_data_dir()
2195
2196 _setup_bind_mount_service(self._container, uid, username)
2197
2198 if multiarch and self.architecture == 'amd64':
2199- utils.get_logger().info("Adding i386 multiarch support to container '{}'".format(self._id))
2200+ utils.get_logger().info("Adding i386 multiarch support to container '{}'".format(self.container_id))
2201 self.run_in_container("dpkg --add-architecture i386")
2202
2203 self.update_packages()
2204
2205 for package in self.default_packages:
2206- utils.get_logger().info("Installing package '%s' in container '%s'" % (package, self._id))
2207+ utils.get_logger().info("Installing package '%s' in container '%s'" % (package, self.container_id))
2208 if not self.install_package(package, no_dialog=True, update_cache=False):
2209 utils.get_logger().error("Failure installing '%s' during container creation" % package)
2210 self.destroy_libertine_container()
2211@@ -357,13 +370,13 @@
2212 self.run_in_container("dpkg-reconfigure -f noninteractive tzdata")
2213
2214 _sync_application_dirs_to_host(self._container)
2215- update_bind_mounts(self._container, self._config)
2216+ update_bind_mounts(self._container, self._config, env_home_path())
2217
2218 return super().update_packages(update_locale)
2219
2220 def destroy_libertine_container(self):
2221 if not self._try_get_container():
2222- utils.get_logger().error("No such container '%s'" % self._id)
2223+ utils.get_logger().error("No such container '%s'" % self.container_id)
2224 return False
2225
2226 if not self.stop_container(wait=True):
2227@@ -384,7 +397,7 @@
2228 for k, v in environ.items():
2229 env_as_args += '--env {}="{}" '.format(k, v)
2230
2231- return shlex.split('lxc exec {}{}-- {}'.format(self._id,
2232+ return shlex.split('lxc exec {}{}-- {}'.format(self.container_id,
2233 env_as_args,
2234 command))
2235
2236@@ -392,108 +405,95 @@
2237 proc = subprocess.Popen(self._lxc_args(command))
2238 return proc.wait()
2239
2240- def start_container(self):
2241+ def start_container(self, home=env_home_path()):
2242 if not self._try_get_container():
2243 return False
2244
2245 if self._manager:
2246- result = LifecycleResult.from_dict(self._manager.operation_start(self._id))
2247- else:
2248- result = lxd_start(self._container)
2249-
2250- if not result.success:
2251- utils.get_logger().error(result.error)
2252+ retries = 0
2253+ while not self._manager.container_operation_start(self.container_id):
2254+ retries += 1
2255+ if retries > 5:
2256+ return False
2257+ time.sleep(.5)
2258+
2259+ if self._container.status == 'Running':
2260+ return True
2261+
2262+ requires_remount = self._container.status == 'Stopped'
2263+
2264+ if requires_remount:
2265+ update_libertine_profile(self._client)
2266+ update_bind_mounts(self._container, self._config, home)
2267+
2268+ if not lxd_start(self._container):
2269+ if self._manager:
2270+ self._manager.container_stopped(self.container_id)
2271 return False
2272
2273 if not _wait_for_network(self._container):
2274- utils.get_logger().warning("Network unavailable in container '{}'".format(self._id))
2275-
2276- return result.success
2277+ utils.get_logger().warning("Network unavailable in container '{}'".format(self.container_id))
2278+
2279+ if requires_remount:
2280+ self.run_in_container("/usr/bin/libertine-lxd-mount-update")
2281+
2282+ return True
2283
2284 def stop_container(self, wait=False):
2285 if not self._try_get_container():
2286 return False
2287
2288 if self._manager:
2289- result = LifecycleResult.from_dict(self._manager.operation_stop(self._id, {'wait': wait}))
2290+ if self._manager.container_operation_finished(self.container_id):
2291+ if not lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop):
2292+ return False
2293+ self._manager.container_stopped(self.container_id)
2294+ return True
2295+ else:
2296+ return False
2297 else:
2298- result = lxd_stop(self._container, wait)
2299-
2300- if not result.success:
2301- utils.get_logger().error(result.error)
2302-
2303- return result.success
2304-
2305- def _get_matchbox_pids(self):
2306- p = subprocess.Popen(self._lxc_args('pgrep matchbox'), stdout=subprocess.PIPE)
2307- out, err = p.communicate()
2308- return out.decode('utf-8').split('\n')
2309-
2310- # FIXME: Remove once window management logic has been moved to the host
2311- def _start_window_manager(self, args):
2312- args.extend(self.setup_window_manager())
2313-
2314- if 'matchbox-window-manager' in args:
2315- pids = self._get_matchbox_pids()
2316-
2317- self._window_manager = psutil.Popen(args)
2318-
2319- time.sleep(.25) # Give matchbox a moment to start in the container
2320- if self._window_manager.poll() is not None:
2321- utils.get_logger().warning("Window manager terminated prematurely with exit code '{}'".format(
2322- self._window_manager.returncode))
2323- self._window_manager = None
2324- elif 'matchbox-window-manager' in args:
2325- after_pids = self._get_matchbox_pids()
2326- if len(after_pids) > len(pids):
2327- self._matchbox_pid = (set(after_pids) - set(pids)).pop()
2328-
2329+ return lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop)
2330+
2331+ def restart_container(self, wait=True):
2332+ if not self._try_get_container():
2333+ return False
2334+
2335+ if self._container.status != 'Frozen':
2336+ utils.get_logger().warning("Container {} is not frozen. Cannot restart.".format(self._container.name))
2337+ return False
2338+
2339+ orig_freeze = self._freeze_on_stop
2340+ self._freeze_on_stop = False
2341+
2342+ # We never want to use the manager when restarting.
2343+ self._manager = None
2344+
2345+ if not (self.stop_container(wait=True) and self.start_container()):
2346+ return False
2347+
2348+ self._freeze_on_stop = orig_freeze
2349+ return self.stop_container(wait=True)
2350+
2351 def start_application(self, app_exec_line, environ):
2352 if not self._try_get_container():
2353- utils.get_logger().error("Could not get container '{}'".format(self._id))
2354+ utils.get_logger().error("Could not get container '{}'".format(self.container_id))
2355 return None
2356
2357- requires_remount = self._container.status != 'Running'
2358-
2359- if self._manager:
2360- result = LifecycleResult.from_dict(self._manager.app_start(self._id))
2361- else:
2362- update_libertine_profile(self._client)
2363- update_bind_mounts(self._container, self._config)
2364- result = lxd_start(self._container)
2365-
2366- if not result.success:
2367- utils.get_logger().error(result.error)
2368+ if utils.is_snap_environment():
2369+ environ['HOME'] = '/home/{}'.format(environ['USER'])
2370+
2371+ if not self.start_container(home=environ['HOME']):
2372 return False
2373
2374- if requires_remount:
2375- self._container.execute(shlex.split('/usr/bin/libertine-lxd-mount-update'))
2376-
2377 args = self._lxc_args("sudo -E -u {} env PATH={}".format(environ['USER'], environ['PATH']), environ)
2378
2379- self._start_window_manager(args.copy())
2380-
2381 args.extend(app_exec_line)
2382 return psutil.Popen(args)
2383
2384 def finish_application(self, app):
2385- if self._window_manager is not None:
2386- utils.terminate_window_manager(self._window_manager)
2387- self._window_manager = None
2388-
2389- # This is a workaround for an issue on xenial where the process
2390- # running the window manager is not killed with the application
2391- if self._matchbox_pid is not None:
2392- utils.get_logger().debug("Manually killing matchbox-window-manager")
2393- self.run_in_container("kill -9 {}".format(self._matchbox_pid))
2394- self._matchbox_pid = None
2395-
2396 app.wait()
2397
2398- if self._manager:
2399- self._manager.app_stop(self.container_id)
2400- else:
2401- lxd_stop(self._container, False)
2402+ self.stop_container()
2403
2404 def copy_file_to_container(self, source, dest):
2405 with open(source, 'rb') as f:
2406@@ -504,6 +504,6 @@
2407
2408 def _try_get_container(self):
2409 if self._container is None:
2410- self._container = lxd_container(self._client, self._id)
2411+ self._container = lxd_container(self._client, self.container_id)
2412
2413 return self._container is not None
2414
2415=== modified file 'python/libertine/launcher/config.py'
2416--- python/libertine/launcher/config.py 2016-12-05 18:33:47 +0000
2417+++ python/libertine/launcher/config.py 2017-02-09 16:08:46 +0000
2418@@ -1,4 +1,4 @@
2419-# Copyright 2016 Canonical Ltd.
2420+# Copyright 2016-2017 Canonical Ltd.
2421 #
2422 # This program is free software: you can redistribute it and/or modify it
2423 # under the terms of the GNU General Public License version 3, as published
2424@@ -215,9 +215,6 @@
2425 tasks = []
2426 tasks.append(TaskConfig(TaskType.LAUNCH_SERVICE, ["pasted"]))
2427
2428- if self.container_id is None:
2429- tasks.append(TaskConfig(TaskType.LAUNCH_SERVICE, ['matchbox-window-manager', '-use_titlebar', 'no']))
2430-
2431 return tasks
2432
2433 def _create_socket_bridges(self):
2434
2435=== removed directory 'python/libertine/lifecycle'
2436=== removed file 'python/libertine/lifecycle/ContainerLifecycleService.py'
2437--- python/libertine/lifecycle/ContainerLifecycleService.py 2017-01-12 16:09:37 +0000
2438+++ python/libertine/lifecycle/ContainerLifecycleService.py 1970-01-01 00:00:00 +0000
2439@@ -1,108 +0,0 @@
2440-#!/usr/bin/python3
2441-# -*- coding: utf-8 -*-
2442-
2443-# Copyright (C) 2016 Canonical Ltd.
2444-
2445-# This program is free software: you can redistribute it and/or modify
2446-# it under the terms of the GNU General Public License as published by
2447-# the Free Software Foundation; version 3 of the License.
2448-#
2449-# This program is distributed in the hope that it will be useful,
2450-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2451-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2452-# GNU General Public License for more details.
2453-#
2454-# You should have received a copy of the GNU General Public License
2455-# along with this program. If not, see <http://www.gnu.org/licenses/>.
2456-
2457-
2458-import dbus.exceptions
2459-import dbus.service
2460-
2461-from .LifecycleResult import LifecycleResult
2462-from collections import Counter
2463-from dbus.mainloop.glib import DBusGMainLoop
2464-from libertine import utils
2465-
2466-
2467-LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE = 'com.canonical.libertine.ContainerLifecycle'
2468-
2469-
2470-class ContainerLifecycleService(dbus.service.Object):
2471- def __init__(self, service_name, service_path):
2472- self._apps = Counter()
2473- self._operations = Counter()
2474-
2475- DBusGMainLoop(set_as_default=True)
2476- try:
2477- bus_name = dbus.service.BusName(service_name,
2478- bus=dbus.SessionBus(),
2479- do_not_queue=True)
2480- except dbus.exceptions.NameExistsException:
2481- utils.get_logger().error("service is already running")
2482- raise
2483- super().__init__(bus_name, service_path)
2484-
2485- def start(self, container):
2486- raise NotImplementedError("Subclasses must implement start(container)")
2487-
2488- def stop(self, container, options={}):
2489- raise NotImplementedError("Subclasses must implement stop(container)")
2490-
2491- @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE,
2492- in_signature='s',
2493- out_signature='a{ss}')
2494- def app_start(self, container):
2495- utils.get_logger().debug("app_start({})".format(container))
2496- if self._operations[container] != 0:
2497- return LifecycleResult("Libertine container operation already running: cannot launch application.").to_dict()
2498-
2499- result = self.start(container, True)
2500-
2501- if result.success:
2502- self._apps[container] += 1
2503-
2504- return result.to_dict()
2505-
2506- @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE,
2507- in_signature='s',
2508- out_signature='a{ss}')
2509- def app_stop(self, container):
2510- utils.get_logger().debug("app_stop({})".format(container))
2511- self._apps[container] -= 1
2512- result = LifecycleResult()
2513-
2514- if self._apps[container] == 0:
2515- result = self.stop(container)
2516- del self._apps[container]
2517-
2518- return result.to_dict()
2519-
2520- @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE,
2521- in_signature='s',
2522- out_signature='a{ss}')
2523- def operation_start(self, container):
2524- utils.get_logger().debug("operation_start({})".format(container))
2525- if self._apps[container] != 0:
2526- return LifecycleResult("Application already running in container: cannot run operation.").to_dict()
2527-
2528- result = self.start(container, False)
2529-
2530- if result.success:
2531- self._operations[container] += 1
2532-
2533- return result.to_dict()
2534-
2535- @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE,
2536- in_signature='sa{ss}',
2537- out_signature='a{ss}')
2538- def operation_stop(self, container, options={}):
2539- utils.get_logger().debug("operation_stop({}, {})".format(container, options))
2540- self._operations[container] -= 1
2541- result = LifecycleResult()
2542-
2543- if self._operations[container] == 0:
2544- result = self.stop(container, options)
2545- del self._operations[container]
2546-
2547- return result.to_dict()
2548
2549=== removed file 'python/libertine/lifecycle/ContainerLifecycleServiceRunner.py'
2550--- python/libertine/lifecycle/ContainerLifecycleServiceRunner.py 2016-12-21 20:39:40 +0000
2551+++ python/libertine/lifecycle/ContainerLifecycleServiceRunner.py 1970-01-01 00:00:00 +0000
2552@@ -1,46 +0,0 @@
2553-#!/usr/bin/python3
2554-# -*- coding: utf-8 -*-
2555-
2556-# Copyright (C) 2016 Canonical Ltd.
2557-
2558-# This program is free software: you can redistribute it and/or modify
2559-# it under the terms of the GNU General Public License as published by
2560-# the Free Software Foundation; version 3 of the License.
2561-#
2562-# This program is distributed in the hope that it will be useful,
2563-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2564-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2565-# GNU General Public License for more details.
2566-#
2567-# You should have received a copy of the GNU General Public License
2568-# along with this program. If not, see <http://www.gnu.org/licenses/>.
2569-
2570-import signal
2571-
2572-from gi.repository import GLib
2573-from libertine import utils
2574-
2575-
2576-class ContainerLifecycleServiceRunner(object):
2577- def __init__(self, service):
2578- self._service = service
2579-
2580- def _sigterm(self, sig):
2581- utils.get_logger().warning("Received SIGTERM")
2582- self._shutdown()
2583-
2584- def _shutdown(self):
2585- utils.get_logger().info("Shutting down")
2586- GLib.MainLoop().quit()
2587-
2588- def run(self):
2589- GLib.unix_signal_add(GLib.PRIORITY_HIGH,
2590- signal.SIGTERM,
2591- self._sigterm,
2592- None)
2593-
2594- try:
2595- utils.get_logger().info("Starting main loop")
2596- GLib.MainLoop().run()
2597- except KeyboardInterrupt:
2598- self._shutdown()
2599
2600=== removed file 'python/libertine/lifecycle/LifecycleResult.py'
2601--- python/libertine/lifecycle/LifecycleResult.py 2016-12-21 18:26:51 +0000
2602+++ python/libertine/lifecycle/LifecycleResult.py 1970-01-01 00:00:00 +0000
2603@@ -1,37 +0,0 @@
2604-#!/usr/bin/python3
2605-# -*- coding: utf-8 -*-
2606-
2607-# Copyright (C) 2016 Canonical Ltd.
2608-
2609-# This program is free software: you can redistribute it and/or modify
2610-# it under the terms of the GNU General Public License as published by
2611-# the Free Software Foundation; version 3 of the License.
2612-#
2613-# This program is distributed in the hope that it will be useful,
2614-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2615-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2616-# GNU General Public License for more details.
2617-#
2618-# You should have received a copy of the GNU General Public License
2619-# along with this program. If not, see <http://www.gnu.org/licenses/>.
2620-
2621-class LifecycleResult(object):
2622- def __init__(self, error=''):
2623- self._error = error
2624-
2625- @property
2626- def error(self):
2627- return self._error or ''
2628-
2629- @property
2630- def success(self):
2631- return self.error == ''
2632-
2633- @classmethod
2634- def from_dict(kls, d):
2635- return LifecycleResult(d.get('error', None))
2636-
2637- def to_dict(self):
2638- return {
2639- 'error': self.error
2640- }
2641
2642=== removed file 'python/libertine/lifecycle/__init__.py'
2643--- python/libertine/lifecycle/__init__.py 2016-12-21 18:26:51 +0000
2644+++ python/libertine/lifecycle/__init__.py 1970-01-01 00:00:00 +0000
2645@@ -1,23 +0,0 @@
2646-# Copyright 2016 Canonical Ltd.
2647-#
2648-# This program is free software: you can redistribute it and/or modify it
2649-# under the terms of the GNU General Public License version 3, as published
2650-# by the Free Software Foundation.
2651-#
2652-# This program is distributed in the hope that it will be useful, but
2653-# WITHOUT ANY WARRANTY; without even the implied warranties of
2654-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2655-# PURPOSE. See the GNU General Public License for more details.
2656-#
2657-# You should have received a copy of the GNU General Public License along
2658-# with this program. If not, see <http://www.gnu.org/licenses/>.
2659-
2660-from .ContainerLifecycleServiceRunner import ContainerLifecycleServiceRunner
2661-from .ContainerLifecycleService import ContainerLifecycleService
2662-from .LifecycleResult import LifecycleResult
2663-
2664-__all__ = [
2665- 'ContainerLifecycleServiceRunner',
2666- 'ContainerLifecycleService',
2667- 'LifecycleResult'
2668- ]
2669
2670=== modified file 'python/libertine/service/apt.py'
2671--- python/libertine/service/apt.py 2016-11-08 15:37:58 +0000
2672+++ python/libertine/service/apt.py 2017-02-09 16:08:46 +0000
2673@@ -1,4 +1,4 @@
2674-# Copyright 2016 Canonical Ltd.
2675+# Copyright 2016-2017 Canonical Ltd.
2676 #
2677 # This program is free software: you can redistribute it and/or modify
2678 # it under the terms of the GNU General Public License as published by
2679@@ -15,10 +15,6 @@
2680 import apt
2681 import re
2682
2683-import gi
2684-gi.require_version('Libertine', '1')
2685-from gi.repository import Libertine
2686-
2687 from libertine import utils
2688 from os import path
2689 from threading import Lock
2690@@ -67,7 +63,7 @@
2691 if self._cache is None:
2692 try:
2693 utils.get_logger().debug("Trying aptcache for container %s" % self._container)
2694- container_path = Libertine.container_path(self._container)
2695+ container_path = utils.get_libertine_container_rootfs_path(self._container)
2696 if not container_path or not path.exists(container_path):
2697 raise PermissionError
2698
2699
2700=== modified file 'python/libertine/service/container.py'
2701--- python/libertine/service/container.py 2016-11-08 15:37:58 +0000
2702+++ python/libertine/service/container.py 2017-02-09 16:08:46 +0000
2703@@ -1,4 +1,4 @@
2704-# Copyright 2016 Canonical Ltd.
2705+# Copyright 2016-2017 Canonical Ltd.
2706 #
2707 # This program is free software: you can redistribute it and/or modify
2708 # it under the terms of the GNU General Public License as published by
2709@@ -14,18 +14,27 @@
2710
2711 from libertine.service.tasks import *
2712 from libertine import utils
2713-from libertine.service import apt
2714+from threading import Lock
2715+
2716+
2717+if not utils.is_snap_environment():
2718+ from libertine.service import apt
2719
2720
2721 class Container(object):
2722- def __init__(self, container_id, config, lock, connection, callback):
2723+ def __init__(self, container_id, config, connection, callback):
2724 self._id = container_id
2725 self._connection = connection
2726 self._callback = callback
2727 self._config = config
2728- self._lock = lock
2729+ self._lock = Lock()
2730 self._tasks = []
2731- self._cache = apt.AptCache(self.id)
2732+
2733+ if utils.is_snap_environment():
2734+ utils.get_logger().warning("Using AptCache not currently supported in snap environment")
2735+ self._cache = None
2736+ else:
2737+ self._cache = apt.AptCache(self.id)
2738
2739 def _cleanup_task(self, task):
2740 utils.get_logger().debug("cleaning up tasks for container '%s'" % self.id)
2741@@ -47,6 +56,9 @@
2742 def search(self, query):
2743 utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query))
2744
2745+ if utils.is_snap_environment():
2746+ raise Exception("This operation is not currently supported within the snap")
2747+
2748 task = SearchTask(self.id, self._cache, query, self._connection, self._cleanup_task)
2749 self._tasks.append(task)
2750 task.start()
2751@@ -56,6 +68,9 @@
2752 def app_info(self, package_name):
2753 utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id))
2754
2755+ if utils.is_snap_environment():
2756+ raise Exception("This operation is not currently supported within the snap")
2757+
2758 related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running]
2759 task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._connection, self._cleanup_task)
2760
2761@@ -129,10 +144,10 @@
2762 task.start()
2763 return task.id
2764
2765- def list_apps(self):
2766- utils.get_logger().debug("List all apps in container '%s'" % self.id)
2767+ def list_app_ids(self):
2768+ utils.get_logger().debug("List all app ids in container '%s'" % self.id)
2769
2770- task = ListAppsTask(self.id, self._config, self._connection, self._cleanup_task)
2771+ task = ListAppIdsTask(self.id, self._config, self._connection, self._cleanup_task)
2772
2773 self._tasks.append(task)
2774 task.start()
2775
2776=== modified file 'python/libertine/service/manager.py'
2777--- python/libertine/service/manager.py 2016-10-31 18:31:00 +0000
2778+++ python/libertine/service/manager.py 2017-02-09 16:08:46 +0000
2779@@ -1,4 +1,4 @@
2780-# Copyright 2016 Canonical Ltd.
2781+# Copyright 2016-2017 Canonical Ltd.
2782 #
2783 # This program is free software: you can redistribute it and/or modify
2784 # it under the terms of the GNU General Public License as published by
2785@@ -15,6 +15,7 @@
2786 import dbus
2787 import dbus.service
2788 import libertine.service.task_dispatcher
2789+from collections import Counter
2790 from dbus.mainloop.glib import DBusGMainLoop
2791 from libertine.service import container
2792 from libertine import utils
2793@@ -28,6 +29,7 @@
2794 class Manager(dbus.service.Object):
2795 def __init__(self):
2796 utils.get_logger().debug("creating service")
2797+ self._operations = Counter()
2798 DBusGMainLoop(set_as_default=True)
2799 try:
2800 bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME,
2801@@ -67,9 +69,9 @@
2802 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
2803 in_signature='s',
2804 out_signature='o')
2805- def list_apps(self, container_id):
2806- utils.get_logger().debug("list_apps('{}')".format(container_id))
2807- return self._dispatcher.list_apps(container_id)
2808+ def list_app_ids(self, container_id):
2809+ utils.get_logger().debug("list_app_ids('{}')".format(container_id))
2810+ return self._dispatcher.list_app_ids(container_id)
2811
2812 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
2813 out_signature='o')
2814@@ -113,3 +115,37 @@
2815 def remove(self, container_id, package_name):
2816 utils.get_logger().debug("remove('%s', '%s')" % (container_id, package_name))
2817 return self._dispatcher.remove(container_id, package_name)
2818+
2819+ # Container Lifecycle
2820+
2821+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
2822+ in_signature='s',
2823+ out_signature='b')
2824+ def container_operation_start(self, container):
2825+ utils.get_logger().debug("container_operation_start({})".format(container))
2826+
2827+ if self._operations[container] == -1:
2828+ return False
2829+
2830+ self._operations[container] += 1
2831+ return True
2832+
2833+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
2834+ in_signature='s',
2835+ out_signature='b')
2836+ def container_operation_finished(self, container):
2837+ utils.get_logger().debug("container_operation_finished({})".format(container))
2838+ stop = False
2839+ self._operations[container] -= 1
2840+
2841+ if self._operations[container] == 0:
2842+ self._operations[container] -= 1
2843+ stop = True
2844+
2845+ return stop
2846+
2847+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
2848+ in_signature='s')
2849+ def container_stopped(self, container):
2850+ utils.get_logger().debug("container_stopped({})".format(container))
2851+ del self._operations[container]
2852
2853=== modified file 'python/libertine/service/progress.py'
2854--- python/libertine/service/progress.py 2016-11-29 17:38:38 +0000
2855+++ python/libertine/service/progress.py 2017-02-09 16:08:46 +0000
2856@@ -1,4 +1,4 @@
2857-# Copyright 2016 Canonical Ltd.
2858+# Copyright 2016-2017 Canonical Ltd.
2859 #
2860 # This program is free software: you can redistribute it and/or modify
2861 # it under the terms of the GNU General Public License as published by
2862@@ -18,7 +18,7 @@
2863 from time import time
2864
2865 DOWNLOAD_INTERFACE = "com.canonical.applications.Download"
2866-PROGRESS_INTERFACE = "com.canonical.libertine.Progress"
2867+PROGRESS_INTERFACE = "com.canonical.libertine.Service.Progress"
2868
2869 class Progress(dbus.service.Object):
2870 def __init__(self, connection):
2871@@ -28,7 +28,7 @@
2872 self._error = ''
2873 dbus.service.Object.__init__(self, conn=connection, object_path=("/Progress/%s" % hex(int(time()*10000000))[2:]))
2874
2875- self.emit_processing()
2876+ # self.emit_processing() # Disabled until something requires the Download interface
2877
2878 @property
2879 def id(self):
2880
2881=== modified file 'python/libertine/service/task_dispatcher.py'
2882--- python/libertine/service/task_dispatcher.py 2016-11-01 18:50:32 +0000
2883+++ python/libertine/service/task_dispatcher.py 2017-02-09 16:08:46 +0000
2884@@ -1,4 +1,4 @@
2885-# Copyright 2016 Canonical Ltd.
2886+# Copyright 2016-2017 Canonical Ltd.
2887 #
2888 # This program is free software: you can redistribute it and/or modify
2889 # it under the terms of the GNU General Public License as published by
2890@@ -24,7 +24,6 @@
2891 def __init__(self, connection):
2892 self._connection = connection
2893 self._config = libertine.ContainersConfig.ContainersConfig()
2894- self._lock = Lock()
2895 self._containerless_tasks = []
2896 self._tasks = []
2897 self._containers = []
2898@@ -35,7 +34,7 @@
2899 self._tasks.remove(task)
2900
2901 def _cleanup_container(self, container):
2902- utils.get_logger().debug("cleaning up container '%s'" % container)
2903+ utils.get_logger().debug("cleaning up container '%s'" % container.id)
2904 if container in self._containers:
2905 self._containers.remove(container)
2906
2907@@ -45,7 +44,7 @@
2908 if container is not None:
2909 utils.get_logger().debug("using existing container '%s'" % container_id)
2910 return container
2911- container = Container(container_id, self._config, self._lock, self._connection, self._cleanup_container)
2912+ container = Container(container_id, self._config, self._connection, self._cleanup_container)
2913 self._containers.append(container)
2914
2915 return container
2916@@ -85,9 +84,9 @@
2917 utils.get_logger().debug("dispatching update container '%s'" % container_id)
2918 return self._find_or_create_container(container_id).update()
2919
2920- def list_apps(self, container_id):
2921- utils.get_logger().debug("dispatching list all apps in container '%s'" % container_id)
2922- return self._find_or_create_container(container_id).list_apps()
2923+ def list_app_ids(self, container_id):
2924+ utils.get_logger().debug("dispatching list apps ids in container '%s'" % container_id)
2925+ return self._find_or_create_container(container_id).list_app_ids()
2926
2927 # Containerless Tasks
2928
2929@@ -107,7 +106,7 @@
2930 def list(self):
2931 utils.get_logger().debug("dispatching list all containers")
2932
2933- task = ListTask(self._connection, self._cleanup_task)
2934+ task = ListTask(self._config, self._connection, self._cleanup_task)
2935 self._tasks.append(task)
2936 task.start()
2937
2938
2939=== modified file 'python/libertine/service/tasks/__init__.py'
2940--- python/libertine/service/tasks/__init__.py 2016-11-01 17:38:38 +0000
2941+++ python/libertine/service/tasks/__init__.py 2017-02-09 16:08:46 +0000
2942@@ -1,4 +1,4 @@
2943-# Copyright 2016 Canonical Ltd.
2944+# Copyright 2016-2017 Canonical Ltd.
2945 #
2946 # This program is free software: you can redistribute it and/or modify it
2947 # under the terms of the GNU General Public License version 3, as published
2948@@ -22,7 +22,7 @@
2949 from .search_task import SearchTask
2950 from .update_task import UpdateTask
2951 from .list_task import ListTask
2952-from .list_apps_task import ListAppsTask
2953+from .list_app_ids_task import ListAppIdsTask
2954
2955 __all__ = [
2956 'AppInfoTask',
2957@@ -35,5 +35,5 @@
2958 'SearchTask',
2959 'UpdateTask',
2960 'ListTask',
2961- 'ListAppsTask'
2962+ 'ListAppIdsTask'
2963 ]
2964
2965=== modified file 'python/libertine/service/tasks/base_task.py'
2966--- python/libertine/service/tasks/base_task.py 2016-11-07 20:37:54 +0000
2967+++ python/libertine/service/tasks/base_task.py 2017-02-09 16:08:46 +0000
2968@@ -1,4 +1,4 @@
2969-# Copyright 2016 Canonical Ltd.
2970+# Copyright 2016-2017 Canonical Ltd.
2971 #
2972 # This program is free software: you can redistribute it and/or modify
2973 # it under the terms of the GNU General Public License as published by
2974@@ -15,7 +15,6 @@
2975 import libertine.service.progress
2976 import threading
2977 from abc import ABCMeta, abstractmethod
2978-from libertine import utils
2979
2980
2981 class BaseTask(metaclass=ABCMeta):
2982@@ -60,7 +59,7 @@
2983 if self._instant_callback:
2984 self._callback(self)
2985 else:
2986- threading.Timer(30, lambda: self._callback(self)).start()
2987+ threading.Timer(10, lambda: self._callback(self)).start()
2988
2989 def start(self):
2990 self._progress = libertine.service.progress.Progress(self._connection)
2991@@ -82,7 +81,6 @@
2992
2993 if self.running:
2994 self._progress.finished(self.container)
2995- utils.refresh_libertine_scope()
2996
2997 self._delayed_callback()
2998
2999
3000=== modified file 'python/libertine/service/tasks/container_info_task.py'
3001--- python/libertine/service/tasks/container_info_task.py 2016-11-01 17:38:38 +0000
3002+++ python/libertine/service/tasks/container_info_task.py 2017-02-09 16:08:46 +0000
3003@@ -1,4 +1,4 @@
3004-# Copyright 2016 Canonical Ltd.
3005+# Copyright 2016-2017 Canonical Ltd.
3006 #
3007 # This program is free software: you can redistribute it and/or modify
3008 # it under the terms of the GNU General Public License as published by
3009@@ -13,6 +13,8 @@
3010 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3011
3012
3013+import json
3014+
3015 from .base_task import BaseTask
3016 from libertine import utils
3017
3018@@ -23,7 +25,21 @@
3019 self._tasks = tasks
3020
3021 def _run(self):
3022- container = {'id': str(self._container)}
3023- container['status'] = self._config._get_value_by_key(self._container, 'installStatus') or ''
3024- container['task_ids'] = self._tasks
3025- self._progress.data(str(container))
3026+ utils.get_logger().debug("Gathering info for container '{}'".format(self._container))
3027+ container = {'id': str(self._container), 'task_ids': self._tasks}
3028+
3029+ container['status'] = self._config.get_container_install_status(self._container) or ''
3030+ container['name'] = self._config.get_container_name(self._container) or ''
3031+
3032+ container_type = self._config.get_container_type(self._container)
3033+ container['root'] = utils.get_libertine_container_rootfs_path(self._container)
3034+ container['home'] = utils.get_libertine_container_home_dir(self._container)
3035+
3036+ self._progress.data(json.dumps(container))
3037+
3038+ def _before(self):
3039+ if not self._config.container_exists(self._container):
3040+ self._progress.error("Container '%s' does not exist, ignoring info request" % self._container)
3041+ return False
3042+
3043+ return True
3044
3045=== added file 'python/libertine/service/tasks/list_app_ids_task.py'
3046--- python/libertine/service/tasks/list_app_ids_task.py 1970-01-01 00:00:00 +0000
3047+++ python/libertine/service/tasks/list_app_ids_task.py 2017-02-09 16:08:46 +0000
3048@@ -0,0 +1,35 @@
3049+# Copyright 2017 Canonical Ltd.
3050+#
3051+# This program is free software: you can redistribute it and/or modify
3052+# it under the terms of the GNU General Public License as published by
3053+# the Free Software Foundation; version 3 of the License.
3054+#
3055+# This program is distributed in the hope that it will be useful,
3056+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3057+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3058+# GNU General Public License for more details.
3059+#
3060+# You should have received a copy of the GNU General Public License
3061+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3062+
3063+
3064+import json
3065+from .base_task import BaseTask
3066+from libertine import LibertineContainer, utils
3067+import time
3068+
3069+
3070+class ListAppIdsTask(BaseTask):
3071+ def __init__(self, container_id, config, connection, callback):
3072+ super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
3073+
3074+ def _run(self):
3075+ utils.get_logger().debug("Listing app ids from container '%s'" % self._container)
3076+ self._progress.data(json.dumps(LibertineContainer(self._container, self._config).list_app_ids()))
3077+
3078+ def _before(self):
3079+ if not self._config.container_exists(self._container):
3080+ self._progress.error("Container '%s' does not exist, skipping list" % self._container)
3081+ return False
3082+
3083+ return True
3084
3085=== removed file 'python/libertine/service/tasks/list_apps_task.py'
3086--- python/libertine/service/tasks/list_apps_task.py 2016-11-01 17:38:38 +0000
3087+++ python/libertine/service/tasks/list_apps_task.py 1970-01-01 00:00:00 +0000
3088@@ -1,30 +0,0 @@
3089-# Copyright 2016 Canonical Ltd.
3090-#
3091-# This program is free software: you can redistribute it and/or modify
3092-# it under the terms of the GNU General Public License as published by
3093-# the Free Software Foundation; version 3 of the License.
3094-#
3095-# This program is distributed in the hope that it will be useful,
3096-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3097-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3098-# GNU General Public License for more details.
3099-#
3100-# You should have received a copy of the GNU General Public License
3101-# along with this program. If not, see <http://www.gnu.org/licenses/>.
3102-
3103-
3104-from .base_task import BaseTask
3105-from libertine import LibertineContainer, utils
3106-
3107-
3108-class ListAppsTask(BaseTask):
3109- def __init__(self, container_id, config, connection, callback):
3110- super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
3111-
3112- def _run(self):
3113- utils.get_logger().debug("Listing apps in container '%s'" % self._container)
3114- if not self._config.container_exists(self._container):
3115- self._progress.error("Container '%s' does not exist, skipping list" % self._container)
3116- else:
3117- container = LibertineContainer(self._container, self._config)
3118- self._progress.data(str(container.list_app_launchers(use_json=True)))
3119
3120=== modified file 'python/libertine/service/tasks/list_task.py'
3121--- python/libertine/service/tasks/list_task.py 2016-11-01 18:50:32 +0000
3122+++ python/libertine/service/tasks/list_task.py 2017-02-09 16:08:46 +0000
3123@@ -1,4 +1,4 @@
3124-# Copyright 2016 Canonical Ltd.
3125+# Copyright 2016-2017 Canonical Ltd.
3126 #
3127 # This program is free software: you can redistribute it and/or modify
3128 # it under the terms of the GNU General Public License as published by
3129@@ -13,13 +13,15 @@
3130 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3131
3132
3133+import json
3134+
3135 from .base_task import BaseTask
3136 from libertine import utils
3137
3138
3139 class ListTask(BaseTask):
3140- def __init__(self, connection, callback):
3141- super().__init__(lock=None, container_id=None, config=None, connection=connection, callback=callback)
3142+ def __init__(self, config, connection, callback):
3143+ super().__init__(lock=None, container_id=None, config=config, connection=connection, callback=callback)
3144
3145 def _run(self):
3146- self._progress.data(str(utils.Libertine.list_containers()))
3147+ self._progress.data(json.dumps(self._config.get_containers()))
3148
3149=== modified file 'python/libertine/utils.py'
3150--- python/libertine/utils.py 2017-01-17 15:41:51 +0000
3151+++ python/libertine/utils.py 2017-02-09 16:08:46 +0000
3152@@ -1,6 +1,6 @@
3153 # -*- coding: utf-8 -*-
3154
3155-# Copyright (C) 2015-2016 Canonical Ltd.
3156+# Copyright (C) 2015-2017 Canonical Ltd.
3157 # Author: Christopher Townsend <christopher.townsend@canonical.com>
3158
3159 # This program is free software: you can redistribute it and/or modify
3160@@ -17,14 +17,11 @@
3161
3162 import logging
3163 import os
3164+import psutil
3165 import shlex
3166 import subprocess
3167 import xdg.BaseDirectory as basedir
3168
3169-from gi import require_version
3170-require_version('Libertine', '1')
3171-from gi.repository import Libertine
3172-
3173
3174 def get_logger():
3175 logger = logging.getLogger('__libertine_logger__')
3176@@ -63,12 +60,7 @@
3177
3178
3179 def get_libertine_container_rootfs_path(container_id):
3180- path = Libertine.container_path(container_id)
3181-
3182- if path is None:
3183- path = os.path.join(get_libertine_containers_dir_path(), container_id, 'rootfs')
3184-
3185- return path
3186+ return os.path.join(get_libertine_containers_dir_path(), container_id, 'rootfs')
3187
3188
3189 def get_libertine_containers_dir_path():
3190@@ -82,13 +74,15 @@
3191
3192
3193 def get_libertine_database_dir_path():
3194- xdg_data_home = os.getenv('XDG_DATA_HOME',
3195- os.path.join(os.getenv('HOME'), '.local', 'share'))
3196+ if is_snap_environment():
3197+ xdg_data_home = os.path.join(os.getenv('SNAP_USER_COMMON'), '.local', 'share')
3198+ else:
3199+ xdg_data_home = os.getenv('XDG_DATA_HOME',
3200+ os.path.join(os.getenv('HOME'), '.local', 'share'))
3201
3202 libertine_database_dir = os.path.join(xdg_data_home, 'libertine')
3203
3204- if not os.path.exists(libertine_database_dir):
3205- os.makedirs(libertine_database_dir)
3206+ os.makedirs(libertine_database_dir, exist_ok=True)
3207
3208 return libertine_database_dir
3209
3210@@ -97,11 +91,8 @@
3211 return os.path.join(get_libertine_database_dir_path(), 'ContainersConfig.json')
3212
3213
3214-def get_libertine_container_userdata_dir_path(container_id):
3215- path = Libertine.container_home_path(container_id)
3216-
3217- if path is None:
3218- path = os.path.join(basedir.xdg_data_home, 'libertine-container', 'user-data', container_id)
3219+def get_libertine_container_home_dir(container_id):
3220+ path = os.path.join(basedir.xdg_data_home, 'libertine-container', 'user-data', container_id)
3221
3222 if is_snap_environment():
3223 path = path.replace(os.environ['HOME'], os.getenv('SNAP_USER_COMMON'))
3224@@ -150,31 +141,10 @@
3225 return dirs
3226
3227
3228-def create_libertine_user_data_dir(container_id):
3229- user_data = get_libertine_container_userdata_dir_path(container_id)
3230-
3231- if not os.path.exists(user_data):
3232- os.makedirs(user_data)
3233-
3234- config_path = os.path.join(user_data, ".config", "dconf")
3235-
3236- if not os.path.exists(config_path):
3237- os.makedirs(config_path)
3238-
3239-
3240 def get_libertine_lxc_pulse_socket_path():
3241 return os.path.join(get_libertine_runtime_dir(), 'pulse_socket')
3242
3243
3244-def terminate_window_manager(window_manager):
3245- for child in window_manager.children():
3246- child.terminate()
3247- child.wait()
3248-
3249- window_manager.terminate()
3250- window_manager.wait()
3251-
3252-
3253 def refresh_libertine_scope():
3254 scopes_object_path = "/com/canonical/unity/scopes"
3255 invalidate_signal = "com.canonical.unity.scopes.InvalidateResults"
3256@@ -187,27 +157,27 @@
3257
3258
3259 def set_session_dbus_env_var():
3260- if not 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
3261- dbus_session_path = os.path.join('/', 'run', 'user', str(os.getuid()), 'dbus-session')
3262-
3263- if os.path.exists(dbus_session_path):
3264- with open(dbus_session_path, 'r') as fd:
3265- dbus_session_str = fd.read()
3266-
3267- os.environ['DBUS_SESSION_BUS_ADDRESS'] = dbus_session_str.partition('DBUS_SESSION_BUS_ADDRESS=')[2].rstrip('\n')
3268-
3269- return True
3270- else:
3271- return False
3272-
3273- return True
3274+ dbus_session_set = 'SESSION' in os.environ
3275+
3276+ if not dbus_session_set:
3277+ for p in psutil.process_iter():
3278+ try:
3279+ if p.name() == 'unity8' or p.name() == 'compiz':
3280+ p_environ = subprocess.check_output(["cat", "/proc/{}/environ".format(p.pid)])
3281+ for line in p_environ.decode().split('\0'):
3282+ if line.startswith('DBUS_SESSION_BUS_ADDRESS'):
3283+ os.environ['DBUS_SESSION_BUS_ADDRESS'] = line.partition('DBUS_SESSION_BUS_ADDRESS=')[2].rstrip('\n')
3284+ dbus_session_set = True
3285+ break
3286+ break
3287+ except psutil.NoSuchProcess as e:
3288+ get_logger().warning(str(e))
3289+
3290+ return dbus_session_set
3291
3292
3293 def is_snap_environment():
3294- if 'SNAP' in os.environ:
3295- return True
3296- else:
3297- return False
3298+ return 'SNAP' in os.environ
3299
3300
3301 def get_deb_package_name(package):
3302
3303=== added directory 'setup'
3304=== added directory 'setup/gui'
3305=== added file 'setup/gui/libertine-manager-app.desktop'
3306--- setup/gui/libertine-manager-app.desktop 1970-01-01 00:00:00 +0000
3307+++ setup/gui/libertine-manager-app.desktop 2017-02-09 16:08:46 +0000
3308@@ -0,0 +1,12 @@
3309+[Desktop Entry]
3310+Version=1.0
3311+Name=Libertine Manager
3312+Comment=Legacy Application Sandbox
3313+Exec=libertine.manager-app %U
3314+Terminal=false
3315+Type=Application
3316+Icon=${SNAP}/usr/share/libertine/libertine_64.png
3317+Categories=
3318+GenericName=Application Sandbox
3319+X-Ubuntu-Touch=true
3320+
3321
3322=== modified file 'snapcraft.yaml'
3323--- snapcraft.yaml 2017-01-18 14:50:33 +0000
3324+++ snapcraft.yaml 2017-02-09 16:08:46 +0000
3325@@ -1,18 +1,52 @@
3326 name: libertine
3327-version: 1.5.1+17.04.20170118-0ubuntu1
3328+version: 1.6
3329 summary: Libertine suite
3330 description: |
3331 Suite for maintaining deb-based applications in a non-deb environment
3332-confinement: devmode
3333+confinement: strict # devmode # TODO: update to 'strict'
3334 grade: devel
3335
3336+slots:
3337+ libertined:
3338+ interface: dbus
3339+ name: com.canonical.libertine.Service
3340+ bus: session
3341+
3342 apps:
3343- libertine-container-manager:
3344+ launch:
3345+ command: usr/bin/snap-runner.wrapper libertine-launch
3346+ aliases:
3347+ - libertine-launch
3348+ plugs:
3349+ - lxd
3350+ - network
3351+ - network-bind
3352+ - home
3353+ - mir
3354+ - x11
3355+ - unity7
3356+ - opengl
3357+ - pulseaudio
3358+ - alsa
3359+ libertined:
3360+ command: usr/bin/snap-runner.wrapper libertined
3361+ plugs:
3362+ - lxd
3363+ - network-bind
3364+ # daemon: simple # Waiting on LP:1613420
3365+ container-manager:
3366 command: usr/bin/snap-runner.wrapper libertine-container-manager
3367- libertine-manager-app:
3368+ aliases:
3369+ - libertine-container-manager
3370+ plugs:
3371+ - lxd
3372+ - network
3373+ manager-app:
3374 command: usr/bin/snap-runner.wrapper libertine-manager-app
3375- libertine-launch:
3376- command: usr/bin/snap-runner.wrapper libertine-launch
3377+ # desktop command currently being labeled unknown by app store, restore with snapcraft 2.26 release
3378+ # desktop: usr/share/applications/libertine-manager-app.desktop
3379+ aliases:
3380+ - libertine-manager-app
3381
3382 # For debugging purposes
3383 bash:
3384
3385=== modified file 'tests/integration/CMakeLists.txt'
3386--- tests/integration/CMakeLists.txt 2016-11-07 17:08:16 +0000
3387+++ tests/integration/CMakeLists.txt 2017-02-09 16:08:46 +0000
3388@@ -1,4 +1,4 @@
3389-add_test(test_libertine_service /usr/bin/python3 -m testtools.run test_libertine_service)
3390+add_test(test_libertine_service dbus-run-session -- /usr/bin/python3 -m testtools.run test_libertine_service)
3391 set_tests_properties(test_libertine_service
3392 PROPERTIES ENVIRONMENT
3393 "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;LIBERTINE_DATA_DIR=${CMAKE_CURRENT_SOURCE_DIR};PATH=${CMAKE_SOURCE_DIR}/tools:$ENV{PATH};")
3394
3395=== modified file 'tests/integration/test_libertine_service.py'
3396--- tests/integration/test_libertine_service.py 2016-11-29 17:38:38 +0000
3397+++ tests/integration/test_libertine_service.py 2017-02-09 16:08:46 +0000
3398@@ -1,4 +1,4 @@
3399-# Copyright 2016 Canonical Ltd.
3400+# Copyright 2016-2017 Canonical Ltd.
3401 #
3402 # This program is free software: you can redistribute it and/or modify it
3403 # under the terms of the GNU General Public License version 3, as published
3404@@ -12,20 +12,26 @@
3405 # You should have received a copy of the GNU General Public License along
3406 # with this program. If not, see <http://www.gnu.org/licenses/>.
3407
3408+
3409+import ast
3410 import dbus
3411 import dbus.mainloop.glib
3412+import os
3413+import pexpect
3414+import sys
3415+import tempfile
3416+import threading
3417+import time
3418 import unittest.mock
3419-from unittest import TestCase
3420+
3421+from gi.repository import GLib
3422+from gi.repository import GObject
3423+from libertine import utils
3424 from libertine.service import tasks, apt
3425 from libertine.ContainersConfig import ContainersConfig
3426 from subprocess import Popen, PIPE
3427-import time
3428-from gi.repository import GLib
3429-from gi.repository import GObject
3430-import os
3431-import tempfile
3432-import threading
3433-import ast
3434+from unittest import TestCase
3435+
3436
3437 class TestLibertineService(TestCase):
3438 _process = None
3439@@ -39,7 +45,17 @@
3440
3441 environ = os.environ.copy()
3442 environ['XDG_DATA_HOME'] = cls._tempdir.name
3443- cls._process = Popen(['libertined', '--debug'], stdout=PIPE, stderr=PIPE, env=environ)
3444+
3445+ cls._process = pexpect.spawnu('libertined --debug', env=environ)
3446+ cls._process.logfile = sys.stdout
3447+
3448+ # give libertined enough time to start the whole process
3449+ verbosity = environ.get('LIBERTINE_DEBUG', '1')
3450+ if verbosity == '1':
3451+ cls._process.expect(['.+\n', pexpect.TIMEOUT], timeout=1)
3452+ elif environ['LIBERTINE_DEBUG'] == '2':
3453+ cls._process.expect(['.+\n.+\n.+\n', pexpect.TIMEOUT], timeout=1)
3454+
3455 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
3456 cls._loop = GObject.MainLoop()
3457
3458@@ -49,9 +65,7 @@
3459 @classmethod
3460 def tearDownClass(cls):
3461 cls._loop.quit()
3462- cls._process.kill()
3463- out, err = cls._process.communicate()
3464- print(err)
3465+ cls._process.close()
3466 cls._tempdir.cleanup()
3467
3468 def setUp(self):
3469@@ -91,7 +105,7 @@
3470 signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._finished_handler,
3471 dbus_interface='com.canonical.applications.Download', signal_name='finished'))
3472 signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._data_handler,
3473- dbus_interface='com.canonical.libertine.Progress', signal_name='data'))
3474+ dbus_interface='com.canonical.libertine.Service.Progress', signal_name='data'))
3475 signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._error_handler,
3476 dbus_interface='com.canonical.applications.Download', signal_name='error'))
3477
3478@@ -110,20 +124,25 @@
3479
3480 def test_container_management(self):
3481 try:
3482- self.assertEqual('[]', self._send(lambda: self._libertined.list()))
3483+ self.assertEqual([], ast.literal_eval(self._send(lambda: self._libertined.list())))
3484 self._send(lambda: self._libertined.create('rey', 'Rey', 'xenial', 'mock'))
3485- self.assertEqual('[\'rey\']', self._send(lambda: self._libertined.list()))
3486+ self.assertEqual(['rey'], ast.literal_eval(self._send(lambda: self._libertined.list())))
3487
3488 self._send(lambda: self._libertined.create('kylo', 'Kylo Ren', 'xenial', 'mock'))
3489- self.assertEqual('[\'rey\', \'kylo\']', self._send(lambda: self._libertined.list()))
3490+ self.assertEqual(['rey', 'kylo'], ast.literal_eval(self._send(lambda: self._libertined.list())))
3491
3492 self._send(lambda: self._libertined.update('kylo'))
3493
3494- self.assertEqual({'id': 'rey', 'status': 'ready', 'task_ids': []},
3495- ast.literal_eval(self._send(lambda: self._libertined.container_info('rey'))))
3496+ self.assertEqual({'id': 'rey',
3497+ 'status': 'ready',
3498+ 'name': 'Rey',
3499+ 'task_ids': [],
3500+ 'root': utils.get_libertine_container_rootfs_path('rey'),
3501+ 'home': '{}/libertine-container/user-data/rey'.format(TestLibertineService._tempdir.name)
3502+ }, ast.literal_eval(self._send(lambda: self._libertined.container_info('rey'))))
3503
3504 self._send(lambda: self._libertined.destroy('kylo'))
3505- self.assertEqual('[\'rey\']', self._send(lambda: self._libertined.list()))
3506+ self.assertEqual(['rey'], ast.literal_eval(self._send(lambda: self._libertined.list())))
3507 except AssertionError as e:
3508 raise
3509 except Exception as e:
3510@@ -137,8 +156,7 @@
3511 config = ContainersConfig()
3512
3513 self._send(lambda: self._libertined.create('jarjar', 'JarJar Binks', 'xenial', 'mock'))
3514- self.assertEqual({'name': 'JarJar Binks', 'app_launchers': []},
3515- ast.literal_eval(self._send(lambda: self._libertined.list_apps('jarjar'))))
3516+ self.assertEqual([], ast.literal_eval(self._send(lambda: self._libertined.list_app_ids('jarjar'))))
3517
3518 self._send(lambda: self._libertined.install('jarjar', 'the-force'))
3519 config.refresh_database()
3520
3521=== added directory 'tests/unit/service/containerroot/libertine-container'
3522=== added directory 'tests/unit/service/containerroot/libertine-container/palpatine'
3523=== added directory 'tests/unit/service/containerroot/libertine-container/palpatine/rootfs'
3524=== modified file 'tests/unit/service/tasks/CMakeLists.txt'
3525--- tests/unit/service/tasks/CMakeLists.txt 2016-11-01 19:49:32 +0000
3526+++ tests/unit/service/tasks/CMakeLists.txt 2017-02-09 16:08:46 +0000
3527@@ -4,7 +4,7 @@
3528 create_service_unit_test(test_destroy_task)
3529 create_service_unit_test(test_install_task)
3530 create_service_unit_test(test_list_task)
3531-create_service_unit_test(test_list_apps_task)
3532+create_service_unit_test(test_list_app_ids_task)
3533 create_service_unit_test(test_remove_task)
3534 create_service_unit_test(test_search_task)
3535 create_service_unit_test(test_update_task)
3536
3537=== modified file 'tests/unit/service/tasks/test_container_info_task.py'
3538--- tests/unit/service/tasks/test_container_info_task.py 2016-11-07 18:51:17 +0000
3539+++ tests/unit/service/tasks/test_container_info_task.py 2017-02-09 16:08:46 +0000
3540@@ -13,8 +13,10 @@
3541 # with this program. If not, see <http://www.gnu.org/licenses/>.
3542
3543
3544+import ast
3545 import unittest.mock
3546 from unittest import TestCase
3547+from libertine import utils
3548 from libertine.service import tasks
3549 from libertine.ContainersConfig import ContainersConfig
3550
3551@@ -33,12 +35,22 @@
3552 progress = MockProgress.return_value
3553 progress.done = False
3554
3555- self.config._get_value_by_key.return_value = 'ready'
3556+ self.config.get_container_install_status.return_value = 'ready'
3557+ self.config.get_container_name.return_value = 'Palpatine'
3558 task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.connection, callback)
3559 task._instant_callback = True
3560 task.start().join()
3561
3562- progress.data.assert_called_once_with(str({'id': 'palpatine', 'status': 'ready', 'task_ids': [1, 2, 3]}))
3563+ progress.data.assert_called_once_with(unittest.mock.ANY)
3564+ args, kwargs = progress.data.call_args
3565+ self.assertEqual({'id': 'palpatine',
3566+ 'status': 'ready',
3567+ 'task_ids': [1, 2, 3],
3568+ 'name': 'Palpatine',
3569+ 'root': utils.get_libertine_container_rootfs_path('palpatine'),
3570+ 'home': utils.get_libertine_container_home_dir('palpatine')},
3571+ ast.literal_eval(args[0]))
3572+
3573 progress.finished.assert_called_once_with('palpatine')
3574
3575 self.assertEqual(task, self.called_with)
3576
3577=== added file 'tests/unit/service/tasks/test_list_app_ids_task.py'
3578--- tests/unit/service/tasks/test_list_app_ids_task.py 1970-01-01 00:00:00 +0000
3579+++ tests/unit/service/tasks/test_list_app_ids_task.py 2017-02-09 16:08:46 +0000
3580@@ -0,0 +1,59 @@
3581+# Copyright 2017 Canonical Ltd.
3582+#
3583+# This program is free software: you can redistribute it and/or modify it
3584+# under the terms of the GNU General Public License version 3, as published
3585+# by the Free Software Foundation.
3586+#
3587+# This program is distributed in the hope that it will be useful, but
3588+# WITHOUT ANY WARRANTY; without even the implied warranties of
3589+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3590+# PURPOSE. See the GNU General Public License for more details.
3591+#
3592+# You should have received a copy of the GNU General Public License along
3593+# with this program. If not, see <http://www.gnu.org/licenses/>.
3594+
3595+import json
3596+import unittest.mock
3597+from unittest import TestCase
3598+from libertine.service import tasks
3599+from libertine.ContainersConfig import ContainersConfig
3600+
3601+
3602+class TestListAppIdsTask(TestCase):
3603+ def setUp(self):
3604+ self.config = unittest.mock.create_autospec(ContainersConfig)
3605+ self.connection = unittest.mock.Mock()
3606+ self.lock = unittest.mock.MagicMock()
3607+ self.called_with = None
3608+
3609+ def callback(self, task):
3610+ self.called_with = task
3611+
3612+ def test_sends_error_on_non_existent_container(self):
3613+ self.config.container_exists.return_value = False
3614+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3615+ progress = MockProgress.return_value
3616+ task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback)
3617+ task._instant_callback = True
3618+
3619+ with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3620+ task.start().join()
3621+
3622+ progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list')
3623+ self.assertEqual(task, self.called_with)
3624+
3625+ def test_successfully_lists_apps(self):
3626+ self.config.container_exists.return_value = True
3627+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3628+ progress = MockProgress.return_value
3629+ progress.done = False
3630+ task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback)
3631+ task._instant_callback = True
3632+
3633+ with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3634+ MockContainer.return_value.list_app_ids.return_value = '["palpatine_gedit_0.0","palpatine_xterm_0.0"]'
3635+ task.start().join()
3636+
3637+ progress.finished.assert_called_once_with('palpatine')
3638+ progress.data.assert_called_once_with(json.dumps('["palpatine_gedit_0.0","palpatine_xterm_0.0"]'))
3639+ self.assertEqual(task, self.called_with)
3640
3641=== removed file 'tests/unit/service/tasks/test_list_apps_task.py'
3642--- tests/unit/service/tasks/test_list_apps_task.py 2016-11-07 18:51:17 +0000
3643+++ tests/unit/service/tasks/test_list_apps_task.py 1970-01-01 00:00:00 +0000
3644@@ -1,59 +0,0 @@
3645-# Copyright 2016 Canonical Ltd.
3646-#
3647-# This program is free software: you can redistribute it and/or modify it
3648-# under the terms of the GNU General Public License version 3, as published
3649-# by the Free Software Foundation.
3650-#
3651-# This program is distributed in the hope that it will be useful, but
3652-# WITHOUT ANY WARRANTY; without even the implied warranties of
3653-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3654-# PURPOSE. See the GNU General Public License for more details.
3655-#
3656-# You should have received a copy of the GNU General Public License along
3657-# with this program. If not, see <http://www.gnu.org/licenses/>.
3658-
3659-
3660-import unittest.mock
3661-from unittest import TestCase
3662-from libertine.service import tasks
3663-from libertine.ContainersConfig import ContainersConfig
3664-
3665-
3666-class TestListAppsTask(TestCase):
3667- def setUp(self):
3668- self.config = unittest.mock.create_autospec(ContainersConfig)
3669- self.connection = unittest.mock.Mock()
3670- self.lock = unittest.mock.MagicMock()
3671- self.called_with = None
3672-
3673- def callback(self, task):
3674- self.called_with = task
3675-
3676- def test_sends_error_on_non_existent_container(self):
3677- self.config.container_exists.return_value = False
3678- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3679- progress = MockProgress.return_value
3680- task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback)
3681- task._instant_callback = True
3682-
3683- with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer:
3684- task.start().join()
3685-
3686- progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list')
3687- self.assertEqual(task, self.called_with)
3688-
3689- def test_successfully_lists_apps(self):
3690- self.config.container_exists.return_value = True
3691- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3692- progress = MockProgress.return_value
3693- progress.done = False
3694- task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback)
3695- task._instant_callback = True
3696-
3697- with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer:
3698- MockContainer.return_value.list_app_launchers.return_value = 'jarjar\nsidius'
3699- task.start().join()
3700-
3701- progress.finished.assert_called_once_with('palpatine')
3702- progress.data.assert_called_once_with('jarjar\nsidius')
3703- self.assertEqual(task, self.called_with)
3704
3705=== modified file 'tests/unit/service/tasks/test_list_task.py'
3706--- tests/unit/service/tasks/test_list_task.py 2016-11-07 18:51:17 +0000
3707+++ tests/unit/service/tasks/test_list_task.py 2017-02-09 16:08:46 +0000
3708@@ -1,4 +1,4 @@
3709-# Copyright 2016 Canonical Ltd.
3710+# Copyright 2016-2017 Canonical Ltd.
3711 #
3712 # This program is free software: you can redistribute it and/or modify it
3713 # under the terms of the GNU General Public License version 3, as published
3714@@ -13,7 +13,9 @@
3715 # with this program. If not, see <http://www.gnu.org/licenses/>.
3716
3717
3718+import json
3719 import unittest.mock
3720+
3721 from unittest import TestCase
3722 from libertine.service import tasks
3723 from libertine.ContainersConfig import ContainersConfig
3724@@ -32,14 +34,14 @@
3725 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3726 progress = MockProgress.return_value
3727 progress.done = False
3728- task = tasks.ListTask(self.connection, callback)
3729+
3730+ task = tasks.ListTask(self.config, self.connection, callback)
3731 task._instant_callback = True
3732
3733- with unittest.mock.patch('libertine.service.tasks.list_task.utils.Libertine') as MockLibertine:
3734- MockLibertine.list_containers.return_value = 'palpatine\nvader\nmaul'
3735- task.start().join()
3736+ self.config.get_containers.return_value = ['palatine', 'vader', 'maul']
3737+ task.start().join()
3738
3739- progress.data.assert_called_once_with('palpatine\nvader\nmaul')
3740+ progress.data.assert_called_once_with(json.dumps(['palatine', 'vader', 'maul']))
3741 progress.finished.assert_called_once_with('')
3742
3743 self.assertEqual(task, self.called_with)
3744
3745=== modified file 'tests/unit/service/test_apt.py'
3746--- tests/unit/service/test_apt.py 2016-11-03 20:10:47 +0000
3747+++ tests/unit/service/test_apt.py 2017-02-09 16:08:46 +0000
3748@@ -1,4 +1,4 @@
3749-# Copyright 2016 Canonical Ltd.
3750+# Copyright 2016-2017 Canonical Ltd.
3751 #
3752 # This program is free software: you can redistribute it and/or modify it
3753 # under the terms of the GNU General Public License version 3, as published
3754@@ -14,8 +14,9 @@
3755
3756 import os
3757 import unittest.mock
3758+from libertine import utils
3759+from libertine.service import apt
3760 from unittest import TestCase
3761-from libertine.service import apt
3762
3763
3764 def build_mock_app(name, summary, website, description):
3765@@ -30,13 +31,13 @@
3766
3767
3768 class TestAptCache(TestCase):
3769+ def setUp(self):
3770+ os.environ['XDG_CACHE_HOME'] = "{}/containerroot".format(os.environ['LIBERTINE_DATA_DIR'])
3771+
3772 def test_search_returns_empty_when_no_matching_results(self):
3773 with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
3774 MockCache.return_value.keys.return_value = []
3775-
3776- with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
3777- MockLibertine.container_path.return_value = '/some/junk'
3778- self.assertEqual(apt.AptCache('palpatine').search('vim'), [])
3779+ self.assertEqual(apt.AptCache('palpatine').search('vim'), [])
3780
3781 def test_search_returns_matching_results(self):
3782 with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
3783@@ -47,9 +48,7 @@
3784 "vim-common": build_mock_app("vim-common", "common vim stuff", "vim.common", "dependencies")
3785 }
3786
3787- with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
3788- MockLibertine.container_path.return_value = '/some/junk'
3789- results = apt.AptCache('palpatine').search('vim')
3790+ results = apt.AptCache('palpatine').search('vim')
3791
3792 self.assertEqual(len(results), 2)
3793 results = sorted(results, key=lambda xx: xx['id'])
3794@@ -67,15 +66,13 @@
3795 self.assertEqual(results[1]['website'], 'vim.common')
3796 self.assertEqual(results[1]['package'], 'vim-common')
3797
3798- MockCache.assert_called_once_with()
3799+ MockCache.assert_called_once_with(rootdir=utils.get_libertine_container_rootfs_path('palpatine'))
3800
3801 def test_app_info_returns_empty_dict_when_no_such_app_exists(self):
3802 with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
3803 MockCache.return_value = {}
3804- with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
3805- MockLibertine.container_path.return_value = '/some/junk'
3806- self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {})
3807- MockCache.assert_called_once_with()
3808+ self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {})
3809+ MockCache.assert_called_once_with(rootdir=utils.get_libertine_container_rootfs_path('palpatine'))
3810
3811 def test_app_info_returns_values_for_app(self):
3812 with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
3813@@ -83,17 +80,15 @@
3814 "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
3815 "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"),
3816 }
3817- with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
3818- MockLibertine.container_path.return_value = '/some/junk'
3819- self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {
3820- 'name': 'vim',
3821- 'id': 'vim',
3822- 'package': 'vim',
3823- 'summary': 'vi improved',
3824- 'description': 'who even uses raw vi',
3825- 'website': 'vim.com'
3826- })
3827- MockCache.assert_called_once_with()
3828+ self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {
3829+ 'name': 'vim',
3830+ 'id': 'vim',
3831+ 'package': 'vim',
3832+ 'summary': 'vi improved',
3833+ 'description': 'who even uses raw vi',
3834+ 'website': 'vim.com'
3835+ })
3836+ MockCache.assert_called_once_with(rootdir=utils.get_libertine_container_rootfs_path('palpatine'))
3837
3838 def test_loads_cache_from_container_directory(self):
3839 with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
3840@@ -101,19 +96,17 @@
3841 "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
3842 "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"),
3843 }
3844- with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
3845- containerpath = "%s/containerroot" % os.path.dirname(os.path.realpath(__file__))
3846- MockLibertine.container_path.return_value = containerpath
3847- self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {
3848- 'name': 'vim',
3849- 'id': 'vim',
3850- 'package': 'vim',
3851- 'summary': 'vi improved',
3852- 'description': 'who even uses raw vi',
3853- 'website': 'vim.com'
3854- })
3855-
3856- MockCache.assert_called_once_with(rootdir=containerpath)
3857+
3858+ self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {
3859+ 'name': 'vim',
3860+ 'id': 'vim',
3861+ 'package': 'vim',
3862+ 'summary': 'vi improved',
3863+ 'description': 'who even uses raw vi',
3864+ 'website': 'vim.com'
3865+ })
3866+
3867+ MockCache.assert_called_once_with(rootdir=utils.get_libertine_container_rootfs_path('palpatine'))
3868
3869 def test_loads_cache_only_once(self):
3870 with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
3871@@ -121,13 +114,12 @@
3872 "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
3873 "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"),
3874 }
3875- with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
3876- MockLibertine.container_path.return_value = '/some/junk'
3877- cache = apt.AptCache('palpatine')
3878- cache.app_info("vim")
3879- cache.app_info("vim")
3880-
3881- MockCache.assert_called_once_with()
3882+
3883+ cache = apt.AptCache('palpatine')
3884+ cache.app_info("vim")
3885+ cache.app_info("vim")
3886+
3887+ MockCache.assert_called_once_with(rootdir=utils.get_libertine_container_rootfs_path('palpatine'))
3888
3889
3890 if __name__ == '__main__':
3891
3892=== modified file 'tests/unit/service/test_container.py'
3893--- tests/unit/service/test_container.py 2016-11-04 15:54:34 +0000
3894+++ tests/unit/service/test_container.py 2017-02-09 16:08:46 +0000
3895@@ -1,4 +1,4 @@
3896-# Copyright 2016 Canonical Ltd.
3897+# Copyright 2016-2017 Canonical Ltd.
3898 #
3899 # This program is free software: you can redistribute it and/or modify it
3900 # under the terms of the GNU General Public License version 3, as published
3901@@ -22,12 +22,11 @@
3902 def setUp(self):
3903 self._connection = unittest.mock.Mock()
3904 self._config = unittest.mock.Mock()
3905- self._lock = unittest.mock.Mock()
3906
3907 def test_search_creates_search_task(self):
3908 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3909 cache = MockCache.return_value
3910- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3911+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3912 with unittest.mock.patch('libertine.service.container.SearchTask') as MockSearchTask:
3913 c.search('darkseid')
3914 MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._connection, unittest.mock.ANY)
3915@@ -36,7 +35,7 @@
3916 def test_app_info_creates_app_info_task(self):
3917 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3918 cache = MockCache.return_value
3919- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3920+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3921 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
3922 c.app_info('force')
3923 MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._connection, unittest.mock.ANY)
3924@@ -45,7 +44,7 @@
3925 def test_app_info_gets_related_task_info(self):
3926 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3927 cache = MockCache.return_value
3928- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3929+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3930 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
3931 MockInstallTask.return_value.package = 'darkside'
3932 MockInstallTask.return_value.matches.return_value = False
3933@@ -60,119 +59,107 @@
3934
3935 def test_install_creates_install_task(self):
3936 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3937- cache = MockCache.return_value
3938- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3939+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3940 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
3941 c.install('force')
3942- MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
3943+ MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
3944 MockInstallTask.return_value.start.assert_called_once_with()
3945
3946 def test_install_only_calls_once_when_unfinished(self):
3947 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3948- cache = MockCache.return_value
3949- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3950+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3951 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
3952 c.install('darkside')
3953 c.install('darkside')
3954 c.install('darkside')
3955- # MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
3956+ MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
3957 MockInstallTask.return_value.start.assert_called_once_with()
3958
3959 def test_remove_creates_remove_task(self):
3960 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3961- cache = MockCache.return_value
3962- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3963+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3964 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
3965 c.remove('force')
3966- MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
3967+ MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
3968 MockRemoveTask.return_value.start.assert_called_once_with()
3969
3970 def test_remove_only_calls_once_when_unfinished(self):
3971 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3972- cache = MockCache.return_value
3973- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3974+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3975 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
3976 c.remove('darkside')
3977 c.remove('darkside')
3978 c.remove('darkside')
3979- # MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
3980+ MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
3981 MockRemoveTask.return_value.start.assert_called_once_with()
3982
3983 def test_create_creates_create_task(self):
3984 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3985- cache = MockCache.return_value
3986- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3987+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3988 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
3989 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
3990 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
3991- self._config, self._lock, self._connection, unittest.mock.ANY)
3992+ self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
3993 MockCreateTask.return_value.start.assert_called_once_with()
3994
3995 def test_create_only_calls_once_when_unfinished(self):
3996 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3997- cache = MockCache.return_value
3998- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
3999+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4000 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
4001 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4002 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4003 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4004- # MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
4005- # self._config, self._lock, self._connection, unittest.mock.ANY)
4006+ MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
4007+ self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4008 MockCreateTask.return_value.start.assert_called_once_with()
4009
4010 def test_destroy_creates_destroy_task(self):
4011 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4012- cache = MockCache.return_value
4013- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
4014+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4015 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
4016 c.destroy()
4017- # MockDestroyTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
4018+ MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4019 MockDestroyTask.return_value.start.assert_called_once_with()
4020
4021 def test_destroy_only_calls_once_when_unfinished(self):
4022 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4023- cache = MockCache.return_value
4024- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
4025+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4026 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
4027 c.destroy()
4028 c.destroy()
4029 c.destroy()
4030- # MockDestroyTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
4031+ MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4032 MockDestroyTask.return_value.start.assert_called_once_with()
4033
4034 def test_update_creates_update_task(self):
4035 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4036- cache = MockCache.return_value
4037- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
4038+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4039 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
4040 c.update()
4041- MockUpdateTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
4042+ MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4043 MockUpdateTask.return_value.start.assert_called_once_with()
4044
4045 def test_update_only_calls_once_when_unfinished(self):
4046 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4047- cache = MockCache.return_value
4048- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
4049+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4050 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
4051 c.update()
4052 c.update()
4053 c.update()
4054- # MockUpdateTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
4055+ MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4056 MockUpdateTask.return_value.start.assert_called_once_with()
4057
4058- def test_list_apps_creates_list_apps_task(self):
4059+ def test_list_app_ids_creates_list_app_ids_task(self):
4060 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4061- cache = MockCache.return_value
4062- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
4063- with unittest.mock.patch('libertine.service.container.ListAppsTask') as MockListAppsTask:
4064- c.list_apps()
4065- MockListAppsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY)
4066- MockListAppsTask.return_value.start.assert_called_once_with()
4067+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4068+ with unittest.mock.patch('libertine.service.container.ListAppIdsTask') as MockListAppIdsTask:
4069+ c.list_app_ids()
4070+ MockListAppIdsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY)
4071+ MockListAppIdsTask.return_value.start.assert_called_once_with()
4072
4073 def test_removes_task_during_callback(self):
4074 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4075- cache = MockCache.return_value
4076- c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
4077+ c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4078 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4079 MockInstallTask.return_value.package = 'force'
4080 c.install('force')
4081@@ -186,11 +173,10 @@
4082
4083 def test_completing_all_tasks_fires_callback(self):
4084 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4085- cache = MockCache.return_value
4086 self._container_id = None
4087 def callback(container):
4088 self._container_id = container.id
4089- c = container.Container('palpatine', self._config, self._lock, self._connection, callback)
4090+ c = container.Container('palpatine', self._config, self._connection, callback)
4091 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4092 c.install('force')
4093 name, args, kwargs = MockInstallTask.mock_calls[0]
4094
4095=== modified file 'tests/unit/service/test_task_dispatcher.py'
4096--- tests/unit/service/test_task_dispatcher.py 2016-11-03 20:20:15 +0000
4097+++ tests/unit/service/test_task_dispatcher.py 2017-02-09 16:08:46 +0000
4098@@ -1,4 +1,4 @@
4099-# Copyright 2016 Canonical Ltd.
4100+# Copyright 2016-2017 Canonical Ltd.
4101 #
4102 # This program is free software: you can redistribute it and/or modify it
4103 # under the terms of the GNU General Public License version 3, as published
4104@@ -76,34 +76,34 @@
4105 self.assertEqual(123, self._dispatcher.update('palpatine'))
4106 c.update.assert_called_once_with()
4107
4108- def test_list_apps_calls_list_apps_on_container(self):
4109+ def test_list_app_ids_calls_list_app_ids_on_container(self):
4110 with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
4111 c = MockContainer.return_value
4112- c.list_apps.return_value = 123
4113- self.assertEqual(123, self._dispatcher.list_apps('palpatine'))
4114- c.list_apps.assert_called_once_with()
4115+ c.list_app_ids.return_value = 123
4116+ self.assertEqual(123, self._dispatcher.list_app_ids('palpatine'))
4117+ c.list_app_ids.assert_called_once_with()
4118
4119 def test_containers_reused_on_subsequent_calls(self):
4120 with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
4121 c = MockContainer.return_value
4122 c.id = 'palpatine'
4123- self._dispatcher.list_apps('palpatine')
4124- self._dispatcher.list_apps('palpatine')
4125- self._dispatcher.list_apps('palpatine')
4126- MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4127+ self._dispatcher.list_app_ids('palpatine')
4128+ self._dispatcher.list_app_ids('palpatine')
4129+ self._dispatcher.list_app_ids('palpatine')
4130+ MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)
4131
4132 def test_container_callback_removes_container(self):
4133 with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
4134 c = MockContainer.return_value
4135 c.id = 'palpatine'
4136- self._dispatcher.list_apps('palpatine')
4137- MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4138+ self._dispatcher.list_app_ids('palpatine')
4139+ MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)
4140 name, args, kwargs = MockContainer.mock_calls[0]
4141 args[len(args)-1](MockContainer.return_value)
4142- self._dispatcher.list_apps('palpatine')
4143+ self._dispatcher.list_app_ids('palpatine')
4144 MockContainer.assert_has_calls([ # verify container constructed twice
4145- unittest.mock.call('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY),
4146- unittest.mock.call('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4147+ unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY),
4148+ unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)
4149 ], any_order=True)
4150
4151 def test_container_info_creates_container_info_task(self):
4152@@ -121,7 +121,7 @@
4153 c = MockContainer.return_value
4154 c.tasks = [1, 2, 3]
4155 c.id = 'palpatine'
4156- self._dispatcher.list_apps('palpatine') # creates container
4157+ self._dispatcher.list_app_ids('palpatine') # creates container
4158 self._dispatcher.container_info('palpatine')
4159 MockContainerInfoTask.assert_called_once_with('palpatine', [1, 2, 3], unittest.mock.ANY, self._connection, unittest.mock.ANY)
4160 task.start.assert_called_once_with()
4161@@ -131,13 +131,13 @@
4162 task = MockListTask.return_value
4163 task.id = 123
4164 self.assertEqual(123, self._dispatcher.list())
4165- MockListTask.assert_called_once_with(self._connection, unittest.mock.ANY)
4166+ MockListTask.assert_called_once_with(unittest.mock.ANY, self._connection, unittest.mock.ANY)
4167 task.start.assert_called_once_with()
4168
4169 def test_containerless_tasks_are_cleaned_up(self):
4170 with unittest.mock.patch('libertine.service.task_dispatcher.ListTask') as MockListTask:
4171 self._dispatcher.list()
4172- MockListTask.assert_called_once_with(self._connection, unittest.mock.ANY)
4173+ MockListTask.assert_called_once_with(unittest.mock.ANY, self._connection, unittest.mock.ANY)
4174 name, args, kwargs = MockListTask.mock_calls[0]
4175 self.assertEqual(1, len(self._dispatcher._tasks))
4176 args[len(args)-1](MockListTask.return_value)
4177
4178=== removed file 'tests/unit/test_app_discovery.py'
4179--- tests/unit/test_app_discovery.py 2015-12-22 17:28:10 +0000
4180+++ tests/unit/test_app_discovery.py 1970-01-01 00:00:00 +0000
4181@@ -1,52 +0,0 @@
4182-"""Unit tests for the AppLauncher module."""
4183-# Copyright 2015 Canonical Ltd.
4184-#
4185-# This program is free software: you can redistribute it and/or modify it
4186-# under the terms of the GNU General Public License version 3, as published
4187-# by the Free Software Foundation.
4188-#
4189-# This program is distributed in the hope that it will be useful, but
4190-# WITHOUT ANY WARRANTY; without even the implied warranties of
4191-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4192-# PURPOSE. See the GNU General Public License for more details.
4193-#
4194-# You should have received a copy of the GNU General Public License along
4195-# with this program. If not, see <http://www.gnu.org/licenses/>.
4196-
4197-from libertine.AppDiscovery import IconCache
4198-from testtools import TestCase
4199-from testtools.matchers import Equals
4200-
4201-
4202-class TestIconSearching(TestCase):
4203-
4204- def mock_file_loader(self, root_path):
4205- return [
4206- "/some/path/128x128/icon.png",
4207- "/somepath/icon.png",
4208- "/somepath/testme.png"
4209- ];
4210-
4211- def test_full_path(self):
4212- some_root = "/some_root"
4213- icon_cache = IconCache(some_root, file_loader=self.mock_file_loader)
4214-
4215- some_full_path = "/this/is/a/full/path"
4216- self.assertThat(icon_cache.expand_icons(some_full_path)[0],
4217- Equals(some_full_path))
4218-
4219- def test_icon_name(self):
4220- some_root = "/some_root"
4221- icon_cache = IconCache(some_root, file_loader=self.mock_file_loader)
4222-
4223- some_icon_list = "testme"
4224- self.assertThat(icon_cache.expand_icons(some_icon_list)[0],
4225- Equals("/somepath/testme.png"))
4226-
4227- def test_edge_case_relative_file_name(self):
4228- some_root = "/some_root"
4229- icon_cache = IconCache(some_root, file_loader=self.mock_file_loader)
4230-
4231- some_icon_list = "testme.png"
4232- self.assertThat(icon_cache.expand_icons(some_icon_list)[0],
4233- Equals("/somepath/testme.png"))
4234
4235=== modified file 'tests/unit/test_libertine_container.py'
4236--- tests/unit/test_libertine_container.py 2016-11-10 17:40:14 +0000
4237+++ tests/unit/test_libertine_container.py 2017-02-09 16:08:46 +0000
4238@@ -1,5 +1,5 @@
4239 """Unit tests for the LibertineContainer interface."""
4240-# Copyright 2015 Canonical Ltd.
4241+# Copyright 2015-2017 Canonical Ltd.
4242 #
4243 # This program is free software: you can redistribute it and/or modify it
4244 # under the terms of the GNU General Public License version 3, as published
4245@@ -52,6 +52,7 @@
4246
4247 def test_container_name_default(self):
4248 container_id = "test-id-3"
4249+ self._config.get_container_name.return_value = None
4250 container = Libertine.LibertineContainer(container_id, self._config)
4251
4252 self.assertThat(container.name, Equals("Unknown"))
4253
4254=== removed file 'tests/unit/test_libertine_gir.py'
4255--- tests/unit/test_libertine_gir.py 2016-11-21 19:54:35 +0000
4256+++ tests/unit/test_libertine_gir.py 1970-01-01 00:00:00 +0000
4257@@ -1,65 +0,0 @@
4258-# Copyright 2015 Canonical Ltd.
4259-#
4260-# This program is free software: you can redistribute it and/or modify it
4261-# under the terms of the GNU General Public License version 3, as published
4262-# by the Free Software Foundation.
4263-#
4264-# This program is distributed in the hope that it will be useful, but
4265-# WITHOUT ANY WARRANTY; without even the implied warranties of
4266-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4267-# PURPOSE. See the GNU General Public License for more details.
4268-#
4269-# You should have received a copy of the GNU General Public License along
4270-# with this program. If not, see <http://www.gnu.org/licenses/>.
4271-
4272-import os
4273-from testtools import TestCase
4274-from testtools.matchers import Equals
4275-from gi.repository import Libertine
4276-from unittest import skip
4277-from unittest.mock import patch
4278-
4279-
4280-class TestLibertineGir(TestCase):
4281-
4282- def setUp(self):
4283- super(TestLibertineGir, self).setUp()
4284- self.cmake_source_dir = os.environ['LIBERTINE_DATA_DIR']
4285-
4286- def test_list_containers(self):
4287- with patch.dict('os.environ', {'XDG_DATA_HOME': self.cmake_source_dir + '/libertine-config'}):
4288- containers = Libertine.list_containers()
4289-
4290- self.assertThat(containers[0], Equals('wily'))
4291- self.assertThat(containers[1], Equals('wily-2'))
4292-
4293- @skip("need to work around cached globals in glib")
4294- def test_container_path(self):
4295- container_id = 'wily'
4296- with patch.dict('os.environ', {'XDG_CACHE_HOME': self.cmake_source_dir + '/libertine-data'}):
4297- container_path = Libertine.container_path(container_id)
4298-
4299- self.assertThat(container_path, Equals(self.cmake_source_dir + '/libertine-data/libertine-container/wily/rootfs'))
4300-
4301- def test_container_home_path(self):
4302- container_id = 'wily'
4303- with patch.dict('os.environ', {'XDG_DATA_HOME': self.cmake_source_dir + '/libertine-home'}):
4304- container_home_path = Libertine.container_home_path(container_id)
4305-
4306- self.assertThat(container_home_path, Equals(self.cmake_source_dir + '/libertine-home/libertine-container/user-data/wily'))
4307-
4308- def test_container_name(self):
4309- with patch.dict('os.environ', {'XDG_DATA_HOME': self.cmake_source_dir + '/libertine-config'}):
4310- container_name = Libertine.container_name('wily')
4311-
4312- self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf'"))
4313-
4314- container_name = Libertine.container_name('wily-2')
4315-
4316- self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf' (2)"))
4317-
4318- def test_list_apps_for_container(self):
4319- with patch.dict('os.environ', {'XDG_DATA_HOME': self.cmake_source_dir + '/libertine-config'}):
4320- apps = Libertine.list_apps_for_container('wily')
4321-
4322- self.assertThat(len(apps), Equals(0))
4323
4324=== modified file 'tools/CMakeLists.txt'
4325--- tools/CMakeLists.txt 2016-12-21 20:57:15 +0000
4326+++ tools/CMakeLists.txt 2017-02-09 16:08:46 +0000
4327@@ -1,8 +1,8 @@
4328-install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-lxd-manager
4329+install(PROGRAMS libertine-container-manager libertine-launch
4330 libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined
4331 DESTINATION ${CMAKE_INSTALL_BINDIR})
4332-install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1
4333- libertine-lxd-manager.1 libertine-xmir.1
4334+install(FILES libertine-launch.1 libertine-container-manager.1
4335+ libertine-xmir.1
4336 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
4337 COMPONENT doc)
4338 install(FILES completions/libertine-container-manager
4339
4340=== modified file 'tools/completions/libertine-container-manager'
4341--- tools/completions/libertine-container-manager 2016-10-14 14:44:17 +0000
4342+++ tools/completions/libertine-container-manager 2017-02-09 16:08:46 +0000
4343@@ -1,5 +1,6 @@
4344-# Copyright (C) 2016 Canonical Ltd.
4345-# Author: Larry Price <larry.price@canonical.com>
4346+# Copyright (C) 2016-2017 Canonical Ltd.
4347+# Authors: Larry Price <larry.price@canonical.com>
4348+# Chris Townsend <christopher.townsend@canonical.com>
4349
4350 # This program is free software: you can redistribute it and/or modify
4351 # it under the terms of the GNU General Public License as published by
4352@@ -48,11 +49,14 @@
4353 opts="--help"
4354 ;;
4355 "configure" )
4356- opts="--help --id --multiarch --archive --bind-mount"
4357+ opts="--help --id --multiarch --archive --bind-mount --freeze"
4358 ;;
4359 "set-default" )
4360 opts="--help --id --clear"
4361 ;;
4362+ "restart" )
4363+ opts="--help --id"
4364+ ;;
4365 * )
4366 opts="--help --quiet --verbose"
4367 ;;
4368@@ -60,7 +64,7 @@
4369 fi
4370
4371 if [[ ${cmd} == "configure" ]]; then
4372- if [[ "${COMP_WORDS[COMP_CWORD-1]}" == "--multiarch" ]]; then
4373+ if [[ "${COMP_WORDS[COMP_CWORD-1]}" == "--multiarch" ]] || [ "${COMP_WORDS[COMP_CWORD-1]}" == "--freeze" ]; then
4374 opts="enable disable"
4375 elif [ "${COMP_WORDS[COMP_CWORD-1]}" == "--archive" ] || [ "${COMP_WORDS[COMP_CWORD-1]}" == "--bind-mount" ]; then
4376 opts="add remove"
4377@@ -72,7 +76,7 @@
4378 fi
4379
4380 if [[ -z ${opts} && "${COMP_CWORD}" == "1" ]]; then
4381- opts="create destroy install-package remove-package search-cache update list list-apps configure set-default"
4382+ opts="create destroy install-package remove-package search-cache update list list-apps configure set-default restart"
4383 fi
4384
4385 if [[ -n "${opts}" ]]; then
4386
4387=== modified file 'tools/libertine-container-manager'
4388--- tools/libertine-container-manager 2017-01-13 19:03:19 +0000
4389+++ tools/libertine-container-manager 2017-02-09 16:08:46 +0000
4390@@ -1,7 +1,7 @@
4391 #!/usr/bin/python3
4392 # -*- coding: utf-8 -*-
4393
4394-# Copyright (C) 2014-2016 Canonical Ltd.
4395+# Copyright (C) 2014-2017 Canonical Ltd.
4396 # Author: Christopher Townsend <christopher.townsend@canonical.com>
4397
4398 # This program is free software: you can redistribute it and/or modify
4399@@ -17,8 +17,9 @@
4400 # along with this program. If not, see <http://www.gnu.org/licenses/>.
4401
4402 import argparse
4403+import getpass
4404+import json
4405 import libertine.utils
4406-import getpass
4407 import os
4408 import sys
4409 import re
4410@@ -36,7 +37,7 @@
4411
4412 def _get_updated_locale(self, container_id):
4413 host_locale = self.host_info.get_host_locale()
4414-
4415+
4416 if host_locale == self.containers_config.get_container_locale(container_id):
4417 return None
4418 else:
4419@@ -218,15 +219,18 @@
4420 self.containers_config.update_container_install_status(container_id, "ready")
4421
4422 def list(self, args):
4423- containers = libertine.utils.Libertine.list_containers()
4424- for container in containers:
4425+ for container in ContainersConfig().get_containers():
4426 print("%s" % container)
4427
4428 def list_apps(self, args):
4429 container_id = self.containers_config.check_container_id(args.id)
4430
4431- container = LibertineContainer(container_id)
4432- print(container.list_app_launchers(use_json=args.json))
4433+ app_ids = LibertineContainer(container_id).list_app_ids()
4434+ if args.json:
4435+ print(json.dumps(app_ids))
4436+ else:
4437+ for app in app_ids:
4438+ print(app)
4439
4440 def exec(self, args):
4441 container_id = self.containers_config.check_container_id(args.id)
4442@@ -249,8 +253,6 @@
4443 def configure(self, args):
4444 container_id = self.containers_config.check_container_id(args.id)
4445
4446- container = LibertineContainer(container_id)
4447-
4448 if args.multiarch and self.host_info.get_host_architecture() == 'amd64':
4449 multiarch = 'disabled'
4450 if args.multiarch == 'enable':
4451@@ -261,7 +263,7 @@
4452 libertine.utils.get_logger().error("i386 multiarch support is already %s" % multiarch)
4453 sys.exit(1)
4454
4455- if container.configure_multiarch(args.multiarch) is not 0:
4456+ if LibertineContainer(container_id).configure_multiarch(args.multiarch) is not 0:
4457 sys.exit(1)
4458
4459 self.containers_config.update_container_multiarch_support(container_id, multiarch)
4460@@ -281,7 +283,7 @@
4461
4462 self.containers_config.add_container_archive(container_id, archive_name)
4463 self.containers_config.update_archive_install_status(container_id, archive_name, 'installing')
4464- if container.configure_add_archive(archive_name_esc, args.public_key_file) is not 0:
4465+ if LibertineContainer(container_id).configure_add_archive(archive_name_esc, args.public_key_file) is not 0:
4466 self.containers_config.delete_container_archive(container_id, archive_name)
4467 sys.exit(1)
4468
4469@@ -328,6 +330,27 @@
4470 sys.exit(1)
4471 self.containers_config.delete_bind_mount(container_id, mount_path)
4472
4473+ container_type = self.containers_config.get_container_type(container_id)
4474+
4475+ if (container_type == 'lxc' or container_type == 'lxd' and
4476+ self.containers_config.get_freeze_on_stop(container_id)):
4477+ if not LibertineContainer(container_id).restart_libertine_container():
4478+ libertine.utils.get_logger().warning("Container cannot be restarted at this time. You will need to "
4479+ "restart the container at a later time using the \'restart\' subcommand.")
4480+
4481+ elif args.freeze is not None:
4482+ container_type = self.containers_config.get_container_type(container_id)
4483+
4484+ if container_type != 'lxc' and container_type != 'lxd':
4485+ libertine.utils.get_logger().error("Configuring freeze is only valid on LXC and LXD container types.")
4486+ sys.exit(1)
4487+
4488+ current_freeze = self.containers_config.get_freeze_on_stop(container_id)
4489+ config_freeze = args.freeze == 'enable'
4490+
4491+ if current_freeze != config_freeze:
4492+ self.containers_config.update_freeze_on_stop(container_id, config_freeze)
4493+
4494 else:
4495 libertine.utils.get_logger().error("Configure called with no subcommand. See configure --help for usage.")
4496 sys.exit(1)
4497@@ -362,6 +385,19 @@
4498
4499 self.containers_config.set_default_container_id(container_id, True)
4500
4501+ def restart(self, args):
4502+ container_id = self.containers_config.check_container_id(args.id)
4503+
4504+ container_type = self.containers_config.get_container_type(container_id)
4505+
4506+ if container_type != 'lxc' and container_type != 'lxd':
4507+ libertine.utils.get_logger().error("The restart subcommand is only valid for LXC and LXD type containers.")
4508+ sys.exit(1)
4509+
4510+ container = LibertineContainer(container_id)
4511+
4512+ container.restart_libertine_container()
4513+
4514
4515 if __name__ == '__main__':
4516 parser = argparse.ArgumentParser(description="Legacy X application support for Unity 8")
4517@@ -552,6 +588,14 @@
4518 metavar='Mount path',
4519 help=("The absolute host path to bind-mount."))
4520
4521+ freeze_group = parser_configure.add_argument_group("Freeze container support",
4522+ "Enable or disable freezing LXC/LXD containers when not in use.")
4523+ freeze_group.add_argument(
4524+ '-f', '--freeze',
4525+ choices=['enable', 'disable'],
4526+ help=("Enables or disables freezing of LXC/LXD containers when not in use."
4527+ " When disabled, the container will stop."))
4528+
4529 parser_configure.set_defaults(func=container_manager.configure)
4530
4531 # Handle merging another ContainersConfig.json file into the main ContainersConfig.json file
4532@@ -582,6 +626,16 @@
4533 help=("Clear the default container."))
4534 parser_default.set_defaults(func=container_manager.set_default)
4535
4536+ # Handle the restart command and its options
4537+ parser_update = subparsers.add_parser(
4538+ 'restart',
4539+ help=("Restart a frozen Libertine container. This only works on LXC "
4540+ "and LXD type containers."))
4541+ parser_update.add_argument(
4542+ '-i', '--id',
4543+ help=("Container identifier. Default container is used if omitted."))
4544+ parser_update.set_defaults(func=container_manager.restart)
4545+
4546 # Actually parse the args
4547 args = parser.parse_args()
4548
4549
4550=== modified file 'tools/libertine-container-manager.1'
4551--- tools/libertine-container-manager.1 2017-01-12 20:46:05 +0000
4552+++ tools/libertine-container-manager.1 2017-02-09 16:08:46 +0000
4553@@ -1,4 +1,4 @@
4554-.TH libertine-container-manager "1" " April 2016" "libertine-container-manager 0.99" "User Commands"
4555+.TH libertine-container-manager "1" " February 2017" "libertine-container-manager 1.5.2" "User Commands"
4556
4557 .SH NAME
4558 libertine-container-manager \- Manage Libertine containers for supporting legacy X applications on Unity 8
4559@@ -68,6 +68,9 @@
4560 .TP
4561 .B libertine-container-manager set-default [options]
4562 Sets default container.
4563+.TP
4564+.B libertine-container-manager restart [options]
4565+Restarts a frozen LXC or LXD Libertine container.
4566
4567 .SH COMMAND REFERENCE
4568 .TP
4569@@ -275,7 +278,7 @@
4570 Enable i386 support.
4571 .RE
4572 .IP
4573-.BR \-a " {add,remove}, " \-\-archive "{add,remove}" ""
4574+.BR \-a " {add,remove}, " \-\-archive " {add,remove}" ""
4575 .RS 14
4576 Adds or removes an archive in the specified container.
4577 .RE
4578@@ -290,7 +293,7 @@
4579 File containing public key used to sign new archive.
4580 .RE
4581 .IP
4582-.BR \-b " {add,remove}, " \-\-bind-mount "{add,remove}" ""
4583+.BR \-b " {add,remove}, " \-\-bind-mount " {add,remove}" ""
4584 .RS 14
4585 Adds or removes a bind-mount in the specified container.
4586 .RE
4587@@ -299,6 +302,11 @@
4588 .RS 14
4589 Absolute host path to be added or removed.
4590 .RE
4591+.IP
4592+.BR \-f " {enable,disable}, " \-\-freeze " {enable,disable}" ""
4593+.RS 14
4594+Enable or disable freezing LXC/LXD containers when not in use.
4595+.RE
4596 .TP
4597
4598 .B libertine-container-manager set-default [options]
4599@@ -319,6 +327,20 @@
4600 Clear default container.
4601 .RE
4602 .TP
4603+
4604+.B libertine-container-manager restart [options]
4605+.TP
4606+.SS Options:
4607+.BR \-h ", " \-\-help ""
4608+.RS 14
4609+Prints help for this command and exits.
4610+.RE
4611+.IP
4612+.BR \-i " ID, " \-\-id " ID" ""
4613+.RS 14
4614+Container identifier.
4615+.RE
4616+.TP
4617 .BR
4618
4619 .SH ENVIRONMENT VARIABLES
4620
4621=== removed file 'tools/libertine-lxc-manager'
4622--- tools/libertine-lxc-manager 2017-01-12 16:09:37 +0000
4623+++ tools/libertine-lxc-manager 1970-01-01 00:00:00 +0000
4624@@ -1,109 +0,0 @@
4625-#!/usr/bin/python3
4626-# -*- coding: utf-8 -*-
4627-
4628-# Copyright (C) 2016 Canonical Ltd.
4629-# Author: Christopher Townsend <christopher.townsend@canonical.com>
4630-
4631-# This program is free software: you can redistribute it and/or modify
4632-# it under the terms of the GNU General Public License as published by
4633-# the Free Software Foundation; version 3 of the License.
4634-#
4635-# This program is distributed in the hope that it will be useful,
4636-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4637-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4638-# GNU General Public License for more details.
4639-#
4640-# You should have received a copy of the GNU General Public License
4641-# along with this program. If not, see <http://www.gnu.org/licenses/>.
4642-
4643-import libertine.LxcContainer
4644-import libertine.utils
4645-import os
4646-import shlex
4647-import subprocess
4648-
4649-from libertine.ContainersConfig import ContainersConfig
4650-from libertine.lifecycle import *
4651-
4652-
4653-LIBERTINE_LXC_MANAGER_NAME = libertine.LxcContainer.get_lxc_manager_dbus_name()
4654-LIBERTINE_LXC_MANAGER_PATH = libertine.LxcContainer.get_lxc_manager_dbus_path()
4655-
4656-
4657-class Service(ContainerLifecycleService):
4658-
4659- def __init__(self):
4660- super().__init__(LIBERTINE_LXC_MANAGER_NAME, LIBERTINE_LXC_MANAGER_PATH)
4661- self._home = os.environ['HOME']
4662- self._containers_config = ContainersConfig()
4663- self._is_pulse_setup = False
4664-
4665- def start(self, container_id, launchable):
4666- container = libertine.LxcContainer.lxc_container(container_id)
4667-
4668- if not container.defined:
4669- return LifecycleResult("Container {} is not valid".format(container_id))
4670-
4671- if launchable and not self._is_pulse_setup:
4672- self._setup_pulse()
4673-
4674- if not container.running:
4675- if launchable:
4676- self._dynamic_bind_mounts(container, container_id)
4677-
4678- return libertine.LxcContainer.lxc_start(container)
4679-
4680- return LifecycleResult()
4681-
4682- def stop(self, container_id, options={}):
4683- container = libertine.LxcContainer.lxc_container(container_id)
4684- libertine.LxcContainer.lxc_stop(container)
4685-
4686- return LifecycleResult() # no error case
4687-
4688- def _setup_pulse(self):
4689- pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket')
4690-
4691- lsof_cmd = 'lsof -n %s' % pulse_socket_path
4692- args = shlex.split(lsof_cmd)
4693- lsof = subprocess.Popen(args, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
4694- lsof.wait()
4695-
4696- if not os.path.exists(pulse_socket_path) or lsof.returncode == 1:
4697- pactl_cmd = (
4698- 'pactl load-module module-native-protocol-unix auth-anonymous=1 socket=%s'
4699- % pulse_socket_path)
4700- args = shlex.split(pactl_cmd)
4701- subprocess.Popen(args).wait()
4702-
4703- self.is_pulse_setup = True
4704-
4705- def _dynamic_bind_mounts(self, container, container_id):
4706- self._containers_config.refresh_database()
4707- mounts = self._sanitize_bind_mounts(libertine.utils.get_common_xdg_user_directories() + \
4708- self._containers_config.get_container_bind_mounts(container_id))
4709-
4710- data_dir = libertine.utils.get_libertine_container_userdata_dir_path(container_id)
4711- for user_dir in libertine.utils.generate_binding_directories(mounts, self._home):
4712- if os.path.isabs(user_dir[1]):
4713- path = user_dir[1].strip('/')
4714- fullpath = os.path.join(libertine.utils.get_libertine_container_rootfs_path(container_id), path)
4715- else:
4716- path = "{}/{}".format(self._home.strip('/'), user_dir[1])
4717- fullpath = os.path.join(data_dir, user_dir[1])
4718-
4719- os.makedirs(fullpath, exist_ok=True)
4720-
4721- libertine.utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container_id))
4722- xdg_user_dir_entry = (
4723- "%s %s none bind,create=dir,optional"
4724- % (user_dir[0], path)
4725- )
4726- container.append_config_item("lxc.mount.entry", xdg_user_dir_entry)
4727-
4728- def _sanitize_bind_mounts(self, mounts):
4729- return [mount.replace(" ", "\\040") for mount in mounts]
4730-
4731-
4732-if __name__ == '__main__':
4733- ContainerLifecycleServiceRunner(Service()).run()
4734
4735=== removed file 'tools/libertine-lxc-manager.1'
4736--- tools/libertine-lxc-manager.1 2016-04-06 12:31:26 +0000
4737+++ tools/libertine-lxc-manager.1 1970-01-01 00:00:00 +0000
4738@@ -1,9 +0,0 @@
4739-.TH libertine-lxc-manager "1" "April 2016" "libertine-lxc-manager 0.99" "User Commands"
4740-
4741-.SH NAME
4742-libertine-lxc-manager \- monitors LXC activity performed by libertine
4743-
4744-.SH DESCRIPTION
4745-usage: libertine\-lxc\-manager
4746-.PP
4747-monitors LXC activity performed by libertine
4748
4749=== removed file 'tools/libertine-lxd-manager'
4750--- tools/libertine-lxd-manager 2017-01-12 16:09:37 +0000
4751+++ tools/libertine-lxd-manager 1970-01-01 00:00:00 +0000
4752@@ -1,57 +0,0 @@
4753-#!/usr/bin/python3
4754-# -*- coding: utf-8 -*-
4755-
4756-# Copyright (C) 2016 Canonical Ltd.
4757-
4758-# This program is free software: you can redistribute it and/or modify
4759-# it under the terms of the GNU General Public License as published by
4760-# the Free Software Foundation; version 3 of the License.
4761-#
4762-# This program is distributed in the hope that it will be useful,
4763-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4764-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4765-# GNU General Public License for more details.
4766-#
4767-# You should have received a copy of the GNU General Public License
4768-# along with this program. If not, see <http://www.gnu.org/licenses/>.
4769-
4770-import libertine.LxdContainer
4771-import libertine.utils
4772-import pylxd
4773-
4774-from libertine.ContainersConfig import ContainersConfig
4775-from libertine.lifecycle import *
4776-
4777-
4778-LIBERTINE_LXD_MANAGER_NAME = libertine.LxdContainer.get_lxd_manager_dbus_name()
4779-LIBERTINE_LXD_MANAGER_PATH = libertine.LxdContainer.get_lxd_manager_dbus_path()
4780-
4781-
4782-class Service(ContainerLifecycleService):
4783-
4784- def __init__(self):
4785- super().__init__(LIBERTINE_LXD_MANAGER_NAME, LIBERTINE_LXD_MANAGER_PATH)
4786- self._config = ContainersConfig()
4787- self._client = pylxd.Client()
4788-
4789- def start(self, container_id, launchable):
4790- container = libertine.LxdContainer.lxd_container(self._client, container_id)
4791-
4792- if not container:
4793- return LifecycleResult("Container {} is not valid".format(container_id))
4794-
4795- if container.status != 'Running':
4796- libertine.LxdContainer.update_libertine_profile(self._client)
4797- if launchable:
4798- libertine.LxdContainer.update_bind_mounts(container, self._config)
4799-
4800- return libertine.LxdContainer.lxd_start(container)
4801-
4802- return LifecycleResult()
4803-
4804- def stop(self, container_id, options={}):
4805- return libertine.LxdContainer.lxd_stop(libertine.LxdContainer.lxd_container(self._client, container_id), options.get('wait', False))
4806-
4807-
4808-if __name__ == '__main__':
4809- ContainerLifecycleServiceRunner(Service()).run()
4810
4811=== removed file 'tools/libertine-lxd-manager.1'
4812--- tools/libertine-lxd-manager.1 2016-12-21 20:57:15 +0000
4813+++ tools/libertine-lxd-manager.1 1970-01-01 00:00:00 +0000
4814@@ -1,9 +0,0 @@
4815-.TH libertine-lxd-manager "1" "Dec 2016" "libertine-lxd-manager 0.99" "User Commands"
4816-
4817-.SH NAME
4818-libertine-lxd-manager \- monitors LXD activity performed by libertine
4819-
4820-.SH DESCRIPTION
4821-usage: libertine\-lxd\-manager
4822-.PP
4823-monitors LXD activity performed by libertine
4824
4825=== modified file 'tools/libertine-lxd-setup'
4826--- tools/libertine-lxd-setup 2017-01-05 15:25:21 +0000
4827+++ tools/libertine-lxd-setup 2017-02-09 16:08:46 +0000
4828@@ -32,7 +32,7 @@
4829 fi
4830
4831 # Run lxd init if there are no containers already on the system
4832-if [ 3 -ge `${lxc} list | wc -l` ]; then
4833+if [ 2 -ge `${lxc} list | grep -e "^+" | wc -l` ]; then
4834 lxd init
4835 fi
4836
4837
4838=== modified file 'tools/libertine-xmir'
4839--- tools/libertine-xmir 2016-02-18 15:49:20 +0000
4840+++ tools/libertine-xmir 2017-02-09 16:08:46 +0000
4841@@ -1,5 +1,5 @@
4842 #!/bin/sh
4843-# Copyright (C) 2016 Canonical Ltd.
4844+# Copyright (C) 2016-2017 Canonical Ltd.
4845 # Author: Christopher Townsend <christopher.townsend@canonical.com>
4846
4847 # This program is free software: you can redistribute it and/or modify
4848@@ -16,4 +16,4 @@
4849
4850
4851 # Add and/or remove Xmir options here
4852-exec Xmir -title @ $@
4853+exec Xmir -rootless $@

Subscribers

People subscribed via source and target branches