Merge lp:~larryprice/libertine/release-1.7.1 into lp:libertine/trunk

Proposed by Larry Price
Status: Merged
Approved by: Christopher Townsend
Approved revision: 204
Merged at revision: 201
Proposed branch: lp:~larryprice/libertine/release-1.7.1
Merge into: lp:libertine/trunk
Diff against target: 4695 lines (+1707/-1017)
62 files modified
CMakeLists.txt (+6/-0)
debian/changelog (+37/-0)
debian/control (+9/-11)
debian/libertine-tools.install (+1/-2)
debian/libertine-xmir-tools.install (+4/-0)
debian/libertined.preinst (+1/-1)
debian/libertined.prerm (+1/-1)
debian/python3-libertine.install (+1/-2)
liblibertine/libertined.cpp (+15/-10)
pasted/pasted.cpp (+97/-43)
pasted/pasted.h (+13/-5)
python/libertine/ChrootContainer.py (+2/-2)
python/libertine/ContainerControlClient.py (+14/-9)
python/libertine/ContainersConfig.py (+52/-17)
python/libertine/Libertine.py (+21/-15)
python/libertine/LxcContainer.py (+7/-9)
python/libertine/LxdContainer.py (+17/-16)
python/libertine/service/constants.py (+27/-0)
python/libertine/service/container.py (+38/-34)
python/libertine/service/container_control.py (+29/-58)
python/libertine/service/container_control_client.py (+75/-0)
python/libertine/service/download.py (+35/-40)
python/libertine/service/operations.py (+18/-62)
python/libertine/service/operations_monitor.py (+93/-0)
python/libertine/service/task_dispatcher.py (+8/-25)
python/libertine/service/tasks/__init__.py (+2/-1)
python/libertine/service/tasks/app_info_task.py (+5/-5)
python/libertine/service/tasks/base_task.py (+39/-14)
python/libertine/service/tasks/container_info_task.py (+4/-4)
python/libertine/service/tasks/create_task.py (+14/-11)
python/libertine/service/tasks/destroy_task.py (+10/-8)
python/libertine/service/tasks/install_task.py (+10/-8)
python/libertine/service/tasks/list_app_ids_task.py (+6/-6)
python/libertine/service/tasks/list_task.py (+4/-3)
python/libertine/service/tasks/remove_task.py (+9/-8)
python/libertine/service/tasks/search_task.py (+4/-4)
python/libertine/service/tasks/update_task.py (+11/-10)
snap/plugins/x-libertine-deps.py (+2/-1)
tests/integration/CMakeLists.txt (+21/-1)
tests/integration/data/libertine/ContainersConfig.json (+19/-0)
tests/integration/test_libertine_service.py (+41/-38)
tests/integration/test_liblibertine.cpp (+145/-0)
tests/unit/CMakeLists.txt (+2/-1)
tests/unit/ContainerConfigListTests.cpp (+1/-0)
tests/unit/service/CMakeLists.txt (+2/-1)
tests/unit/service/tasks/test_app_info_task.py (+24/-26)
tests/unit/service/tasks/test_container_info_task.py (+26/-27)
tests/unit/service/tasks/test_create_task.py (+170/-176)
tests/unit/service/tasks/test_destroy_task.py (+44/-46)
tests/unit/service/tasks/test_install_task.py (+38/-41)
tests/unit/service/tasks/test_list_app_ids_task.py (+23/-25)
tests/unit/service/tasks/test_list_task.py (+15/-16)
tests/unit/service/tasks/test_remove_task.py (+38/-41)
tests/unit/service/tasks/test_search_task.py (+9/-9)
tests/unit/service/tasks/test_update_task.py (+46/-46)
tests/unit/service/test_container.py (+32/-31)
tests/unit/service/test_operations_monitor.py (+146/-0)
tests/unit/service/test_task_dispatcher.py (+6/-5)
tools/libertine-container-manager (+41/-19)
tools/libertine-launch (+12/-2)
tools/libertine-lxd-setup (+37/-14)
tools/libertined (+28/-7)
To merge this branch: bzr merge lp:~larryprice/libertine/release-1.7.1
Reviewer Review Type Date Requested Status
Christopher Townsend Approve
Review via email: mp+319854@code.launchpad.net

Commit message

* When starting pasted, ensure DISPLAY is set and valid before continuing to
run. (LP: 1666472)
* Fix pasted to work with rootless Xmir. (LP: 1671257)
* Bump version to 1.7.1
* Gracefully handle creating a LibertineContainer object when the container
backend is unavailable.
* Update libertine xmir components to not depend on container backends.
(LP: 1671938)
* Catch all errors and gracefully shutdown libertined. (LP: 1671009)
* Fix method call from ContainerControl d-bus to interfaces.
* Update signal handlers in test_libertine_service to reflect new API.
* Inject client for accessing ContainerControl within containers.
* Rearchitect libertine service python backend for simpler access to running
tasks. (LP: 1669091)
* Ignore completions from dependencies during snapcraft build.
* LXD needs to forward host environment to container when running arbitrary
commands.
* Add client object to list_app_ids_task to avoid making reentrant service
calls.
* Take advantage of new network subcommand during lxd init on newer
installs.
* Prevent installing empty package names and appropriately update status for
unstopped containers.
* Modifications to make test_libertine_service more stable.
* Add a '-x' option to pkill so it only kills the libertined process(es)
and nothing else. (LP: 1676005)
* Remove hard-coded dependency on libraries.
* Reopen the database file after every failure to grab the lock.
(LP: 1662655)

Description of the change

Release libertine 1.7.1 with packaging/dependency changes and bug fixes.

To post a comment you must log in.
199. By Larry Price

* When starting pasted, ensure DISPLAY is set and valid before continuing to
  run. (LP: #1666472)
* Fix pasted to work with rootless Xmir. (LP: #1671257)
* LXD needs to forward host environment to container when running arbitrary
  commands.
* Add client object to list_app_ids_task to avoid making reentrant service
  calls.
* Take advantage of new network subcommand during lxd init on newer
  installs.
* Prevent installing empty package names and appropriately update status for
  unstopped containers.

200. By Larry Price

Only depend on libertine-xmir-tools on the appropriate architectures

201. By Larry Price

Path does not exist issue

202. By Larry Price

Merge with lp:libertine/trunk

203. By Larry Price

Merge in test_libertine_service stabilization changes

204. By Larry Price

Allow libertine-xmir-tools to build on any architecture

Revision history for this message
Christopher Townsend (townsend) wrote :

Ok, +1

review: Approve
205. By Larry Price

Merge lp:libertine

206. By Larry Price

Merge lp:~townsend/libertine/fix-lib-deps

207. By Larry Price

Update changelog

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2017-03-06 17:36:14 +0000
+++ CMakeLists.txt 2017-03-31 20:05:48 +0000
@@ -30,6 +30,12 @@
30pkg_check_modules(PYTHON3 REQUIRED python3)30pkg_check_modules(PYTHON3 REQUIRED python3)
31include_directories(${PYTHON3_INCLUDE_DIRS})31include_directories(${PYTHON3_INCLUDE_DIRS})
3232
33pkg_check_modules(DBUSTEST REQUIRED dbustest-1>=14.04.0)
34include_directories(${DBUSTEST_INCLUDE_DIRS})
35
36pkg_check_modules(GIO2 REQUIRED gio-2.0 gio-unix-2.0)
37include_directories(${GIO2_INCLUDE_DIRS})
38
33set(CMAKE_AUTOMOC ON)39set(CMAKE_AUTOMOC ON)
3440
35set(LIBERTINE_COMMON libertine-common)41set(LIBERTINE_COMMON libertine-common)
3642
=== modified file 'debian/changelog'
--- debian/changelog 2017-03-20 15:34:36 +0000
+++ debian/changelog 2017-03-31 20:05:48 +0000
@@ -1,3 +1,40 @@
1libertine (1.7.1-0ubuntu1) UNRELEASED; urgency=medium
2
3 [ Chris Townsend ]
4 * When starting pasted, ensure DISPLAY is set and valid before continuing to
5 run. (LP: #1666472)
6 * Fix pasted to work with rootless Xmir. (LP: #1671257)
7 * Add a '-x' option to pkill so it only kills the libertined process(es)
8 and nothing else. (LP: #1676005)
9 * Remove hard-coded dependency on libraries.
10
11 [ Larry Price ]
12 * Bump version to 1.7.1
13 * Gracefully handle creating a LibertineContainer object when the container
14 backend is unavailable.
15 * Update libertine xmir components to not depend on container backends.
16 (LP: #1671938)
17 * Catch all errors and gracefully shutdown libertined. (LP: #1671009)
18 * Fix method call from ContainerControl d-bus to interfaces.
19 * Update signal handlers in test_libertine_service to reflect new API.
20 * Inject client for accessing ContainerControl within containers.
21 * Rearchitect libertine service python backend for simpler access to running
22 tasks. (LP: #1669091)
23 * Ignore completions from dependencies during snapcraft build.
24 * LXD needs to forward host environment to container when running arbitrary
25 commands.
26 * Add client object to list_app_ids_task to avoid making reentrant service
27 calls.
28 * Take advantage of new network subcommand during lxd init on newer
29 installs.
30 * Prevent installing empty package names and appropriately update status for
31 unstopped containers.
32 * Modifications to make test_libertine_service more stable.
33 * Reopen the database file after every failure to grab the lock.
34 (LP: #1662655)
35
36 -- Larry Price <larry.price@canonical.com> Tue, 14 Mar 2017 11:35:33 -0400
37
1libertine (1.7+17.04.20170320.1-0ubuntu1) zesty; urgency=medium38libertine (1.7+17.04.20170320.1-0ubuntu1) zesty; urgency=medium
239
3 * No change rebuild40 * No change rebuild
441
=== modified file 'debian/control'
--- debian/control 2017-03-13 14:01:08 +0000
+++ debian/control 2017-03-31 20:05:48 +0000
@@ -9,6 +9,7 @@
9 gobject-introspection,9 gobject-introspection,
10 intltool,10 intltool,
11 libcontent-hub-dev (>= 0.2),11 libcontent-hub-dev (>= 0.2),
12 libdbustest1-dev (>= 14.04.0),
12 libgirepository1.0-dev,13 libgirepository1.0-dev,
13 libglib2.0-dev,14 libglib2.0-dev,
14 libgtest-dev,15 libgtest-dev,
@@ -61,7 +62,6 @@
61Depends: libertine-qt-common,62Depends: libertine-qt-common,
62 libertine-tools,63 libertine-tools,
63 python3-libertine-lxd,64 python3-libertine-lxd,
64 libsystemsettings1,
65 ${misc:Depends},65 ${misc:Depends},
66 ${shlibs:Depends}66 ${shlibs:Depends}
67Enhances: ubuntu-system-settings67Enhances: ubuntu-system-settings
@@ -74,7 +74,7 @@
74Architecture: any74Architecture: any
75Depends: qml-module-qtquick2,75Depends: qml-module-qtquick2,
76 qml-module-qtquick-dialogs,76 qml-module-qtquick-dialogs,
77 qtdeclarative5-ubuntu-ui-toolkit-plugin,77 qtdeclarative5-ubuntu-ui-toolkit-plugin | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles,
78 ${misc:Depends},78 ${misc:Depends},
79 ${shlibs:Depends}79 ${shlibs:Depends}
80Description: common files for qt-based libertine applications80Description: common files for qt-based libertine applications
@@ -84,13 +84,14 @@
8484
85Package: libertine-tools85Package: libertine-tools
86Architecture: any86Architecture: any
87Depends: libglib2.0-bin,87Depends: libertine-xmir-tools (>= 1.7.1) [amd64 armhf arm64 i386],
88 libglib2.0-bin,
88 python3-dbus,89 python3-dbus,
89 python3-libertine,90 python3-libertine,
90 ${misc:Depends},91 ${misc:Depends},
91 ${python3:Depends},92 ${python3:Depends},
92 ${shlibs:Depends}93 ${shlibs:Depends}
93Recommends: libertine-xmir-tools (>= 1.4.3)94Recommends: python3-libertine-lxd
94Breaks: libertine-demo95Breaks: libertine-demo
95Replaces: libertine-demo96Replaces: libertine-demo
96Description: CLI tools for running deb-packaged X11 apps on Ubuntu Personal97Description: CLI tools for running deb-packaged X11 apps on Ubuntu Personal
@@ -98,17 +99,14 @@
98 the Ubuntu Personal sandbox for legacy Deb-packaged X11 applicatons.99 the Ubuntu Personal sandbox for legacy Deb-packaged X11 applicatons.
99100
100Package: libertine-xmir-tools101Package: libertine-xmir-tools
101Architecture: amd64 armhf arm64 i386102Architecture: any
102Depends: libcontent-hub0,103Depends: python3-libertine,
103 libqt5core5a,
104 libqt5gui5,
105 libqt5widgets5,
106 xmir [amd64 armhf arm64 i386],104 xmir [amd64 armhf arm64 i386],
107 ${misc:Depends},105 ${misc:Depends},
108 ${python3:Depends},106 ${python3:Depends},
109 ${shlibs:Depends}107 ${shlibs:Depends}
110Breaks: libertine-tools (<< 1.4.3)108Breaks: libertine-tools (<< 1.7.1)
111Replaces: libertine-tools (<< 1.4.3)109Replaces: libertine-tools (<< 1.7.1)
112Description: helper apps for using and interacting with Xmir110Description: helper apps for using and interacting with Xmir
113 Helper applications for using and interacting with Xmir such as launching111 Helper applications for using and interacting with Xmir such as launching
114 Xmir and allowing copy and paste.112 Xmir and allowing copy and paste.
115113
=== modified file 'debian/libertine-tools.install'
--- debian/libertine-tools.install 2017-01-20 18:43:08 +0000
+++ debian/libertine-tools.install 2017-03-31 20:05:48 +0000
@@ -1,4 +1,3 @@
1usr/bin/libertine-container-manager1usr/bin/libertine-container-manager
2usr/bin/libertine-launch
3usr/share/bash-completion/completions/libertine-container-manager2usr/share/bash-completion/completions/libertine-container-manager
4usr/share/man3usr/share/man/*/libertine-container-manager.1
54
=== modified file 'debian/libertine-xmir-tools.install'
--- debian/libertine-xmir-tools.install 2016-10-26 20:48:08 +0000
+++ debian/libertine-xmir-tools.install 2017-03-31 20:05:48 +0000
@@ -1,3 +1,7 @@
1usr/bin/libertine-launch
1usr/bin/libertine-xmir2usr/bin/libertine-xmir
2usr/bin/pasted3usr/bin/pasted
4usr/lib/python*/*/libertine/launcher
5usr/share/man/*/libertine-launch.1
6usr/share/man/*/libertine-xmir.1
3usr/share/upstart/sessions/libertine-xmir.conf7usr/share/upstart/sessions/libertine-xmir.conf
48
=== modified file 'debian/libertined.preinst'
--- debian/libertined.preinst 2017-03-06 16:15:27 +0000
+++ debian/libertined.preinst 2017-03-31 20:05:48 +0000
@@ -16,7 +16,7 @@
1616
17if [ "$1" = "upgrade" ]; then17if [ "$1" = "upgrade" ]; then
18 echo "Shutting down the libertined service..."18 echo "Shutting down the libertined service..."
19 pkill libertined19 pkill -x libertined
20fi20fi
2121
22#DEBHELPER#22#DEBHELPER#
2323
=== modified file 'debian/libertined.prerm'
--- debian/libertined.prerm 2017-03-06 16:15:27 +0000
+++ debian/libertined.prerm 2017-03-31 20:05:48 +0000
@@ -16,7 +16,7 @@
1616
17if [ "$1" = "remove" ]; then17if [ "$1" = "remove" ]; then
18 echo "Shutting down the libertined service..."18 echo "Shutting down the libertined service..."
19 pkill libertined19 pkill -x libertined
20fi20fi
2121
22#DEBHELPER#22#DEBHELPER#
2323
=== modified file 'debian/python3-libertine.install'
--- debian/python3-libertine.install 2017-02-15 21:12:37 +0000
+++ debian/python3-libertine.install 2017-03-31 20:05:48 +0000
@@ -1,7 +1,6 @@
1usr/lib/python*/*/libertine/Client.py1usr/lib/python*/*/libertine/ContainerControlClient.py
2usr/lib/python*/*/libertine/ContainersConfig.py2usr/lib/python*/*/libertine/ContainersConfig.py
3usr/lib/python*/*/libertine/HostInfo.py3usr/lib/python*/*/libertine/HostInfo.py
4usr/lib/python*/*/libertine/Libertine.py4usr/lib/python*/*/libertine/Libertine.py
5usr/lib/python*/*/libertine/__init__.py5usr/lib/python*/*/libertine/__init__.py
6usr/lib/python*/*/libertine/launcher
7usr/lib/python*/*/libertine/utils.py6usr/lib/python*/*/libertine/utils.py
87
=== modified file 'liblibertine/libertined.cpp'
--- liblibertine/libertined.cpp 2017-02-23 21:08:28 +0000
+++ liblibertine/libertined.cpp 2017-03-31 20:05:48 +0000
@@ -25,12 +25,16 @@
25#include <QJsonArray>25#include <QJsonArray>
26#include <QJsonObject>26#include <QJsonObject>
2727
28
28namespace29namespace
29{30{
30constexpr auto SERVICE_INTERFACE = "com.canonical.libertine.Service";31constexpr auto SERVICE_INTERFACE = "com.canonical.libertine.Service";
31constexpr auto PROGRESS_INTERFACE = "com.canonical.libertine.Service.Progress";32constexpr auto OPERATIONS_INTERFACE = "com.canonical.libertine.Service.Operations";
32constexpr auto SESSION_DBUS_ENV_VAR = "DBUS_SESSION_BUS_ADDRESS";33constexpr auto OPERATIONS_OBJECT = "/com/canonical/libertine/Service/Operations";
33constexpr auto SERVICE_NAME = "libertined";34constexpr auto OPERATIONS_MONITOR_INTERFACE = "com.canonical.libertine.Service.OperationsMonitor";
35constexpr auto OPERATIONS_MONITOR_OBJECT = "/com/canonical/libertine/Service/OperationsMonitor";
36constexpr auto SESSION_DBUS_ENV_VAR = "DBUS_SESSION_BUS_ADDRESS";
37constexpr auto SERVICE_NAME = "libertined";
3438
3539
36class SessionBus40class SessionBus
@@ -69,7 +73,7 @@
69static bool73static bool
70isRunning(QDBusConnection const& bus, QString const& path)74isRunning(QDBusConnection const& bus, QString const& path)
71{75{
72 auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "running", QVariantList());76 auto args = dbusCall(bus, OPERATIONS_MONITOR_INTERFACE, OPERATIONS_MONITOR_OBJECT, "running", QVariantList{path});
7377
74 if (args.isEmpty())78 if (args.isEmpty())
75 {79 {
@@ -83,12 +87,12 @@
83static QString87static QString
84result(QDBusConnection const& bus, QString const& path)88result(QDBusConnection const& bus, QString const& path)
85{89{
86 auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "result");90 auto args = dbusCall(bus, OPERATIONS_MONITOR_INTERFACE, OPERATIONS_MONITOR_OBJECT, "result", QVariantList{path});
8791
88 if (args.isEmpty())92 if (args.isEmpty())
89 {93 {
90 qWarning() << "lastError - no arguments?";94 qWarning() << "lastError - no arguments?";
91 return "";95 return QString();
92 }96 }
9397
94 return args.first().toString();98 return args.first().toString();
@@ -97,12 +101,12 @@
97static QString101static QString
98lastError(QDBusConnection const& bus, QString const& path)102lastError(QDBusConnection const& bus, QString const& path)
99{103{
100 auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "last_error");104 auto args = dbusCall(bus, OPERATIONS_MONITOR_INTERFACE, OPERATIONS_MONITOR_OBJECT, "last_error", QVariantList{path});
101105
102 if (args.isEmpty())106 if (args.isEmpty())
103 {107 {
104 qWarning() << "lastError - no arguments?";108 qWarning() << "lastError - no arguments?";
105 return "";109 return QString();
106 }110 }
107111
108 return args.first().toString();112 return args.first().toString();
@@ -111,7 +115,7 @@
111static QString115static QString
112call(QDBusConnection const& bus, QString const& method, QVariantList const& args)116call(QDBusConnection const& bus, QString const& method, QVariantList const& args)
113{117{
114 auto results = dbusCall(bus, SERVICE_INTERFACE, "/Manager", method, args);118 auto results = dbusCall(bus, OPERATIONS_INTERFACE, OPERATIONS_OBJECT, method, args);
115119
116 if (results.isEmpty())120 if (results.isEmpty())
117 {121 {
@@ -158,6 +162,7 @@
158}162}
159}163}
160164
165
161QJsonArray166QJsonArray
162libertined_list()167libertined_list()
163{168{
164169
=== modified file 'pasted/pasted.cpp'
--- pasted/pasted.cpp 2016-09-12 17:59:56 +0000
+++ pasted/pasted.cpp 2017-03-31 20:05:48 +0000
@@ -36,9 +36,8 @@
36constexpr auto UNITY_FOCUSINFO_METHOD = "isSurfaceFocused";36constexpr auto UNITY_FOCUSINFO_METHOD = "isSurfaceFocused";
3737
3838
39static QString getPersistentSurfaceId()39static QString getPersistentSurfaceId(Display *dpy, const Window& id)
40{40{
41 Display *dpy = XOpenDisplay(NULL);
42 Atom prop = XInternAtom(dpy, MIR_WM_PERSISTENT_ID, 0),41 Atom prop = XInternAtom(dpy, MIR_WM_PERSISTENT_ID, 0),
43 type; // unused42 type; // unused
44 int form, // unused43 int form, // unused
@@ -48,7 +47,7 @@
48 unsigned char *data = nullptr;47 unsigned char *data = nullptr;
49 QString persistentSurfaceId;48 QString persistentSurfaceId;
5049
51 status = XGetWindowProperty(dpy, XDefaultRootWindow(dpy), prop, 0, 1024, 0,50 status = XGetWindowProperty(dpy, id, prop, 0, 1024, 0,
52 XA_STRING, &type, &form, &len, &remain, &data);51 XA_STRING, &type, &form, &len, &remain, &data);
5352
54 if (status)53 if (status)
@@ -60,56 +59,109 @@
60 persistentSurfaceId = (const char *)data;59 persistentSurfaceId = (const char *)data;
61 }60 }
6261
63 XCloseDisplay(dpy);
64 XFree(data);62 XFree(data);
6563
66 return persistentSurfaceId;64 return persistentSurfaceId;
67}65}
6866
67
68Display *checkXServer()
69{
70 char *display = getenv("DISPLAY");
71
72 if (display == nullptr)
73 {
74 qCritical() << "DISPLAY environment variable not set!";
75 exit(-1);
76 }
77
78 Display *dpy = XOpenDisplay(display);
79 if (dpy == nullptr)
80 {
81 qCritical() << "Xmir is not running on DISPLAY" << display << "!";
82 exit(-1);
83 }
84
85 return dpy;
86}
87
69} //anonymous namespace88} //anonymous namespace
7089
7190
91XEventWorker::
92XEventWorker(Display *dpy)
93: dpy_(dpy)
94{
95 unityFocus_ = new QDBusInterface(UNITY_FOCUSINFO_SERVICE,
96 UNITY_FOCUSINFO_PATH,
97 UNITY_FOCUSINFO_INTERFACE,
98 QDBusConnection::sessionBus(),
99 this);
100}
101
102
103XEventWorker::
104~XEventWorker()
105{
106 XCloseDisplay(dpy_);
107}
108
109
110bool XEventWorker::
111isSurfaceFocused(const Window& focus_window)
112{
113 surfaceId_ = getPersistentSurfaceId(dpy_, focus_window);
114
115 QDBusReply<bool> isFocused = unityFocus_->call(UNITY_FOCUSINFO_METHOD, surfaceId_);
116
117 return isFocused;
118}
119
120
72void XEventWorker::121void XEventWorker::
73checkForAppFocus()122checkForAppFocus()
74{123{
75 bool hasFocus = false;124 bool hasFocus = false;
125 int focus_state;
126 Window focus_window;
127
128 XGetInputFocus(dpy_, &focus_window, &focus_state);
129
130 if (focus_window > PointerRoot)
131 {
132 if (isSurfaceFocused(focus_window))
133 {
134 focusChanged(surfaceId_);
135 hasFocus = true;
136 }
137 }
138
139 XSelectInput(dpy_, XDefaultRootWindow(dpy_), FocusChangeMask);
140
141 bool focused = false;
76 XEvent event;142 XEvent event;
77143
78 QDBusInterface *unityFocus = new QDBusInterface(UNITY_FOCUSINFO_SERVICE,
79 UNITY_FOCUSINFO_PATH,
80 UNITY_FOCUSINFO_INTERFACE,
81 QDBusConnection::sessionBus(),
82 this);
83
84 QString surfaceId = getPersistentSurfaceId();
85
86 QDBusReply<bool> isFocused = unityFocus->call(UNITY_FOCUSINFO_METHOD, surfaceId);
87
88 if (isFocused == true)
89 {
90 focusChanged();
91 hasFocus = true;
92 }
93
94 Display *dpy = XOpenDisplay(NULL);
95 XSelectInput(dpy, XDefaultRootWindow(dpy), FocusChangeMask);
96
97 while (1)144 while (1)
98 {145 {
99 XNextEvent(dpy, &event);146 XNextEvent(dpy_, &event);
100147
101 isFocused = unityFocus->call(UNITY_FOCUSINFO_METHOD, surfaceId);148 XGetInputFocus(dpy_, &focus_window, &focus_state);
102149
103 if (hasFocus == false && isFocused == true)150 if (focus_window > PointerRoot)
104 {151 {
105 qDebug() << "Surface is focused";152 focused = isSurfaceFocused(focus_window);
106 focusChanged();153
107 hasFocus = true;154 if (hasFocus == false && focused == true)
108 }155 {
109 else if (hasFocus == true && isFocused == false)156 qDebug() << "Surface is focused";
110 {157 focusChanged(surfaceId_);
111 qDebug() << "Surface lost focus";158 hasFocus = true;
112 hasFocus = false;159 }
160 else if (hasFocus == true && focused == false)
161 {
162 qDebug() << "Surface lost focus";
163 hasFocus = false;
164 }
113 }165 }
114 }166 }
115}167}
@@ -230,19 +282,19 @@
230282
231283
232void Pasted::284void Pasted::
233setPersistentSurfaceId()285setPersistentSurfaceId(const QString& surfaceId)
234{286{
235 if (persistentSurfaceId_.isEmpty())287 if (persistentSurfaceId_ != surfaceId)
236 {288 {
237 persistentSurfaceId_ = getPersistentSurfaceId();289 persistentSurfaceId_ = surfaceId;
238 }290 }
239}291}
240292
241293
242void Pasted::294void Pasted::
243appFocused()295appFocused(const QString& surfaceId)
244{296{
245 setPersistentSurfaceId();297 setPersistentSurfaceId(surfaceId);
246 handleContentHubPasteboard();298 handleContentHubPasteboard();
247}299}
248300
@@ -252,10 +304,12 @@
252{304{
253 qSetMessagePattern(QString("%{appname}: %{message}"));305 qSetMessagePattern(QString("%{appname}: %{message}"));
254306
307 Display *dpy = checkXServer();
308
255 Pasted pasted(argc, argv);309 Pasted pasted(argc, argv);
256310
257 QThread t;311 QThread t;
258 XEventWorker worker;312 XEventWorker worker(dpy);
259313
260 worker.moveToThread(&t);314 worker.moveToThread(&t);
261315
262316
=== modified file 'pasted/pasted.h'
--- pasted/pasted.h 2016-09-12 17:59:56 +0000
+++ pasted/pasted.h 2017-03-31 20:05:48 +0000
@@ -39,14 +39,22 @@
39 Q_OBJECT39 Q_OBJECT
4040
41 public:41 public:
42 XEventWorker() = default;42 XEventWorker(Display *dpy);
43 virtual ~XEventWorker() = default;43 virtual ~XEventWorker();
44
45 private:
46 bool isSurfaceFocused(const Window& focus_window);
4447
45 signals:48 signals:
46 void focusChanged();49 void focusChanged(const QString& surfaceId);
4750
48 public slots:51 public slots:
49 void checkForAppFocus();52 void checkForAppFocus();
53
54 private:
55 Display *dpy_;
56 QDBusInterface *unityFocus_;
57 QString surfaceId_;
50};58};
5159
5260
@@ -60,13 +68,13 @@
60 virtual ~Pasted() = default;68 virtual ~Pasted() = default;
6169
62 public slots:70 public slots:
63 void appFocused();71 void appFocused(const QString& surfaceId);
6472
65 private:73 private:
66 void updateLastMimeData(const QMimeData *source);74 void updateLastMimeData(const QMimeData *source);
67 void updateXMimeData(const QMimeData *source);75 void updateXMimeData(const QMimeData *source);
68 void handleContentHubPasteboard();76 void handleContentHubPasteboard();
69 void setPersistentSurfaceId();77 void setPersistentSurfaceId(const QString& surfaceId);
7078
71 static bool compareMimeData(const QMimeData *a, const QMimeData *b);79 static bool compareMimeData(const QMimeData *a, const QMimeData *b);
72 static void copyMimeData(QMimeData& target, const QMimeData *source);80 static void copyMimeData(QMimeData& target, const QMimeData *source);
7381
=== modified file 'python/libertine/ChrootContainer.py'
--- python/libertine/ChrootContainer.py 2017-02-27 20:47:21 +0000
+++ python/libertine/ChrootContainer.py 2017-03-31 20:05:48 +0000
@@ -47,8 +47,8 @@
47 A concrete container type implemented using a plain old chroot.47 A concrete container type implemented using a plain old chroot.
48 """48 """
4949
50 def __init__(self, container_id, config):50 def __init__(self, container_id, config, service):
51 super().__init__(container_id, 'chroot', config)51 super().__init__(container_id, 'chroot', config, service)
52 # FIXME: Disabling seccomp is a temporary measure until we fully understand why we need52 # FIXME: Disabling seccomp is a temporary measure until we fully understand why we need
53 # it or figure out when we need it.53 # it or figure out when we need it.
54 os.environ['PROOT_NO_SECCOMP'] = '1'54 os.environ['PROOT_NO_SECCOMP'] = '1'
5555
=== renamed file 'python/libertine/Client.py' => 'python/libertine/ContainerControlClient.py'
--- python/libertine/Client.py 2017-02-24 14:56:36 +0000
+++ python/libertine/ContainerControlClient.py 2017-03-31 20:05:48 +0000
@@ -19,19 +19,24 @@
19from . import utils19from . import utils
2020
2121
22class Client(object):22class ContainerControlClient(object):
23 """
24 A client for ContainerControl using D-BUS, to be used in clients
25 external to the libertine service.
26 """
23 def __init__(self):27 def __init__(self):
24 self._get_manager()28 self._get_manager()
2529
26 def _get_manager(self):30 def _get_manager(self):
27 self._manager = None31 self._control = None
2832
29 try:33 try:
30 from .service.manager import LIBERTINE_MANAGER_NAME, LIBERTINE_MANAGER_OBJECT34 from .service import constants
35
31 if utils.set_session_dbus_env_var():36 if utils.set_session_dbus_env_var():
32 bus = dbus.SessionBus()37 bus = dbus.SessionBus()
33 self._manager = bus.get_object(LIBERTINE_MANAGER_NAME, LIBERTINE_MANAGER_OBJECT)38 self._control = bus.get_object(constants.SERVICE_NAME, constants.CONTAINER_CONTROL_OBJECT)
34 self._interface = LIBERTINE_MANAGER_NAME39 self._interface = constants.CONTAINER_CONTROL_INTERFACE
35 except ImportError as e:40 except ImportError as e:
36 utils.get_logger().warning("Libertine service libraries not installed.")41 utils.get_logger().warning("Libertine service libraries not installed.")
37 except dbus.exceptions.DBusException as e:42 except dbus.exceptions.DBusException as e:
@@ -51,11 +56,11 @@
5156
52 @property57 @property
53 def valid(self):58 def valid(self):
54 return self._manager is not None59 return self._control is not None
5560
56 def container_operation_start(self, id):61 def container_operation_start(self, id):
57 retries = 062 retries = 0
58 while not self._do_operation(lambda: self._manager.get_dbus_method('container_operation_start', self._interface)(id)):63 while not self._do_operation(lambda: self._control.get_dbus_method('start', self._interface)(id)):
59 retries += 164 retries += 1
60 if retries > 5:65 if retries > 5:
61 return False66 return False
@@ -64,7 +69,7 @@
64 return True69 return True
6570
66 def container_operation_finished(self, id, app_name, pid):71 def container_operation_finished(self, id, app_name, pid):
67 return self._do_operation(lambda: self._manager.get_dbus_method("container_operation_finished", self._interface)(id, app_name, pid))72 return self._do_operation(lambda: self._control.get_dbus_method('finished', self._interface)(id, app_name, pid))
6873
69 def container_stopped(self, id):74 def container_stopped(self, id):
70 return self._do_operation(lambda: self._manager.get_dbus_method('container_stopped', self._interface)(id))75 return self._do_operation(lambda: self._control.get_dbus_method('stopped', self._interface)(id))
7176
=== modified file 'python/libertine/ContainersConfig.py'
--- python/libertine/ContainersConfig.py 2017-02-23 21:08:28 +0000
+++ python/libertine/ContainersConfig.py 2017-03-31 20:05:48 +0000
@@ -14,42 +14,77 @@
1414
15import fcntl15import fcntl
16import json16import json
17import libertine.utils
18import os17import os
19import sys18import sys
19import errno
20import time
21
20from hashlib import md522from hashlib import md5
21from libertine.HostInfo import HostInfo23from . import utils, HostInfo
24
25class _ContainersConfigFile(object):
26 def __init__(self, readonly=True):
27 self._readonly = readonly
28
29 if readonly:
30 self._flags = 'r'
31 else:
32 self._flags = 'w'
33
34 self._fd = None
35
36 def __enter__(self):
37 container_config_file = utils.get_libertine_database_file_path()
38 if self._readonly and (not os.path.exists(container_config_file) or
39 os.path.getsize(container_config_file) == 0):
40 return None
41
42 retries = 0
43 while retries < 100:
44 try:
45 fd = open(container_config_file, self._flags)
46 fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
47
48 self._fd = fd
49 return self._fd
50 except IOError as e:
51 if e.errno != errno.EAGAIN:
52 raise
53 else:
54 fd.close()
55 retries += 1
56 time.sleep(0.05)
57
58 def __exit__(self, *args):
59 if self._fd:
60 self._fd.close()
2261
2362
24def read_container_config_file():63def read_container_config_file():
25 container_list = {}64 container_list = {}
26 container_config_file = libertine.utils.get_libertine_database_file_path()65 with _ContainersConfigFile() as fd:
2766 if fd is not None:
28 if (os.path.exists(container_config_file) and
29 os.path.getsize(container_config_file) != 0):
30 with open(container_config_file, 'r') as fd:
31 fcntl.flock(fd, fcntl.LOCK_EX)
32 container_list = json.load(fd)67 container_list = json.load(fd)
3368
34 return container_list69 return container_list
3570
3671
37def write_container_config_file(container_list):72def write_container_config_file(container_list):
38 container_config_file = libertine.utils.get_libertine_database_file_path()73 container_config_file = utils.get_libertine_database_file_path()
3974
40 # Add a warning to adventurous users advising against mucking with this file75 # Add a warning to adventurous users advising against mucking with this file
41 if container_list is not None:76 if container_list is not None:
42 container_list["_warning"] = "This file is automatically generated by Libertine and should not be manually edited."77 container_list["_warning"] = "This file is automatically generated by Libertine and should not be manually edited."
4378
44 with open(container_config_file, 'w') as fd:79 with _ContainersConfigFile(False) as fd:
45 fcntl.flock(fd, fcntl.LOCK_EX)80 if fd:
46 json.dump(container_list, fd, sort_keys=True, indent=4)81 json.dump(container_list, fd, sort_keys=True, indent=4)
47 fd.write('\n')82 fd.write('\n')
4883
4984
50def container_config_hash():85def container_config_hash():
51 checksum = md5()86 checksum = md5()
52 container_config_file = libertine.utils.get_libertine_database_file_path()87 container_config_file = utils.get_libertine_database_file_path()
53 if (os.path.exists(container_config_file) and os.path.getsize(container_config_file) != 0):88 if (os.path.exists(container_config_file) and os.path.getsize(container_config_file) != 0):
54 with open(container_config_file, "rb") as fd:89 with open(container_config_file, "rb") as fd:
55 fcntl.flock(fd, fcntl.LOCK_EX)90 fcntl.flock(fd, fcntl.LOCK_EX)
@@ -214,12 +249,12 @@
214249
215 def check_container_id(self, container_id):250 def check_container_id(self, container_id):
216 if container_id and not self.container_exists(container_id):251 if container_id and not self.container_exists(container_id):
217 libertine.utils.get_logger().error("Container id \'%s\' does not exist." % container_id)252 utils.get_logger().error("Container id '{container_id}' does not exist.".format(container_id=container_id))
218 sys.exit(1)253 sys.exit(1)
219 elif not container_id:254 elif not container_id:
220 container_id = self.get_default_container_id()255 container_id = self.get_default_container_id()
221 if container_id is None:256 if container_id is None:
222 libertine.utils.get_logger().error("No default container available.")257 utils.get_logger().error("No default container available.")
223 sys.exit(1)258 sys.exit(1)
224259
225 return container_id260 return container_id
@@ -263,7 +298,7 @@
263298
264 def delete_container(self, container_id):299 def delete_container(self, container_id):
265 if not self.container_list:300 if not self.container_list:
266 libertine.utils.get_logger().error("Unable to delete container. No containers defined.")301 utils.get_logger().error("Unable to delete container. No containers defined.")
267 sys.exit(1)302 sys.exit(1)
268303
269 container = self._get_container_entry(container_id)304 container = self._get_container_entry(container_id)
270305
=== modified file 'python/libertine/Libertine.py'
--- python/libertine/Libertine.py 2017-03-03 15:34:16 +0000
+++ python/libertine/Libertine.py 2017-03-31 20:05:48 +0000
@@ -17,7 +17,7 @@
17import os17import os
18import shutil18import shutil
1919
20from . import utils20from . import utils, ContainerControlClient
21from libertine.ContainersConfig import ContainersConfig21from libertine.ContainersConfig import ContainersConfig
22from libertine.HostInfo import HostInfo22from libertine.HostInfo import HostInfo
2323
@@ -79,10 +79,11 @@
7979
80 :param container_id: The machine-readable container name.80 :param container_id: The machine-readable container name.
81 """81 """
82 def __init__(self, container_id, container_type, config):82 def __init__(self, container_id, container_type, config, service):
83 self.container_type = container_type83 self.container_type = container_type
84 self.container_id = container_id84 self.container_id = container_id
85 self._config = config85 self._config = config
86 self._service = service
86 self._app_name = ''87 self._app_name = ''
87 self._pid = 088 self._pid = 0
88 self.root_path = utils.get_libertine_container_rootfs_path(self.container_id)89 self.root_path = utils.get_libertine_container_rootfs_path(self.container_id)
@@ -332,8 +333,8 @@
332 """333 """
333 A concrete mock container type. Used for unit testing.334 A concrete mock container type. Used for unit testing.
334 """335 """
335 def __init__(self, container_id, config):336 def __init__(self, container_id, config, service):
336 super().__init__(container_id, 'mock', config)337 super().__init__(container_id, 'mock', config, service)
337338
338 def create_libertine_container(self, password=None, multiarch=False):339 def create_libertine_container(self, password=None, multiarch=False):
339 return True340 return True
@@ -386,7 +387,7 @@
386 A sandbox for DEB-packaged X11-based applications.387 A sandbox for DEB-packaged X11-based applications.
387 """388 """
388389
389 def __init__(self, container_id, containers_config=None):390 def __init__(self, container_id, containers_config=None, service=None):
390 """391 """
391 Initializes the container object.392 Initializes the container object.
392393
@@ -394,23 +395,22 @@
394 """395 """
395 super().__init__()396 super().__init__()
396397
397 if containers_config is None:398 self.containers_config = containers_config or ContainersConfig()
398 containers_config = ContainersConfig()399 service = service or ContainerControlClient.ContainerControlClient()
399 self.containers_config = containers_config
400400
401 container_type = self.containers_config.get_container_type(container_id)401 container_type = self.containers_config.get_container_type(container_id)
402402
403 if container_type == "lxc":403 if container_type == "lxc":
404 from libertine.LxcContainer import LibertineLXC404 from libertine.LxcContainer import LibertineLXC
405 self.container = LibertineLXC(container_id, self.containers_config)405 self.container = LibertineLXC(container_id, self.containers_config, service)
406 elif container_type == "lxd":406 elif container_type == "lxd":
407 from libertine.LxdContainer import LibertineLXD407 from libertine.LxdContainer import LibertineLXD
408 self.container = LibertineLXD(container_id, self.containers_config)408 self.container = LibertineLXD(container_id, self.containers_config, service)
409 elif container_type == "chroot":409 elif container_type == "chroot":
410 from libertine.ChrootContainer import LibertineChroot410 from libertine.ChrootContainer import LibertineChroot
411 self.container = LibertineChroot(container_id, self.containers_config)411 self.container = LibertineChroot(container_id, self.containers_config, service)
412 elif container_type == "mock":412 elif container_type == "mock":
413 self.container = LibertineMock(container_id, self.containers_config)413 self.container = LibertineMock(container_id, self.containers_config, service)
414 else:414 else:
415 raise RuntimeError("Unsupported container type %s" % container_type)415 raise RuntimeError("Unsupported container type %s" % container_type)
416416
@@ -463,7 +463,10 @@
463 try:463 try:
464 with ContainerRunning(self.container):464 with ContainerRunning(self.container):
465 self.containers_config.update_container_install_status(self.container_id, "installing packages")465 self.containers_config.update_container_install_status(self.container_id, "installing packages")
466 return self.container.install_package(package_name, no_dialog, update_cache)466 retval = self.container.install_package(package_name, no_dialog, update_cache)
467
468 self.containers_config.update_container_install_status(self.container_id, "running")
469 return retval
467 except RuntimeError as e:470 except RuntimeError as e:
468 return handle_runtime_error(e)471 return handle_runtime_error(e)
469472
@@ -479,7 +482,10 @@
479 os.environ['DEBIAN_FRONTEND'] = 'teletype'482 os.environ['DEBIAN_FRONTEND'] = 'teletype'
480483
481 self.containers_config.update_container_install_status(self.container_id, "removing packages")484 self.containers_config.update_container_install_status(self.container_id, "removing packages")
482 return self.container.remove_package(package_name)485 retval = self.container.remove_package(package_name)
486
487 self.containers_config.update_container_install_status(self.container_id, "running")
488 return retval
483 except RuntimeError as e:489 except RuntimeError as e:
484 return handle_runtime_error(e)490 return handle_runtime_error(e)
485491
@@ -547,7 +553,7 @@
547 for root, dirs, files in os.walk(apps_dir):553 for root, dirs, files in os.walk(apps_dir):
548 app_ids.extend(["{}_{}_0.0".format(self.container_id, f[:-8]) for f in files if f.endswith(".desktop")])554 app_ids.extend(["{}_{}_0.0".format(self.container_id, f[:-8]) for f in files if f.endswith(".desktop")])
549555
550 return app_ids556 return sorted(app_ids)
551557
552 def exec_command(self, exec_line):558 def exec_command(self, exec_line):
553 """559 """
554560
=== modified file 'python/libertine/LxcContainer.py'
--- python/libertine/LxcContainer.py 2017-03-03 15:34:16 +0000
+++ python/libertine/LxcContainer.py 2017-03-31 20:05:48 +0000
@@ -23,7 +23,7 @@
23import tempfile23import tempfile
2424
25from .Libertine import BaseContainer25from .Libertine import BaseContainer
26from . import utils, HostInfo, Client26from . import utils, HostInfo
2727
2828
29home_path = os.environ['HOME']29home_path = os.environ['HOME']
@@ -157,14 +157,12 @@
157 A concrete container type implemented using an LXC container.157 A concrete container type implemented using an LXC container.
158 """158 """
159159
160 def __init__(self, container_id, config):160 def __init__(self, container_id, config, service):
161 super().__init__(container_id, 'lxc', config)161 super().__init__(container_id, 'lxc', config, service)
162 self.container = lxc_container(container_id)162 self.container = lxc_container(container_id)
163 self.host_info = HostInfo.HostInfo()163 self.host_info = HostInfo.HostInfo()
164 self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)164 self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)
165165
166 self._manager = Client.Client()
167
168 def _setup_pulse(self):166 def _setup_pulse(self):
169 pulse_socket_path = os.path.join(utils.get_libertine_runtime_dir(), 'pulse_socket')167 pulse_socket_path = os.path.join(utils.get_libertine_runtime_dir(), 'pulse_socket')
170168
@@ -213,7 +211,7 @@
213 return fd.read().strip('\n') != self.host_info.get_host_timezone()211 return fd.read().strip('\n') != self.host_info.get_host_timezone()
214212
215 def start_container(self):213 def start_container(self):
216 if not self._manager.container_operation_start(self.container_id):214 if not self._service.container_operation_start(self.container_id):
217 return False215 return False
218216
219 if self.container.state == 'RUNNING':217 if self.container.state == 'RUNNING':
@@ -236,11 +234,11 @@
236 stopped = False234 stopped = False
237 self._config.refresh_database()235 self._config.refresh_database()
238236
239 if self._manager.container_operation_finished(self.container_id, self._app_name, self._pid):237 if self._service.container_operation_finished(self.container_id, self._app_name, self._pid):
240 self._config.update_container_install_status(self.container_id, self._get_stop_type_string(self._freeze_on_stop))238 self._config.update_container_install_status(self.container_id, self._get_stop_type_string(self._freeze_on_stop))
241239
242 if lxc_stop(self.container, self._freeze_on_stop):240 if lxc_stop(self.container, self._freeze_on_stop):
243 stopped = self._manager.container_stopped(self.container_id)241 stopped = self._service.container_stopped(self.container_id)
244242
245 self._config.update_container_install_status(self.container_id, self.container.state.lower())243 self._config.update_container_install_status(self.container_id, self.container.state.lower())
246244
@@ -397,7 +395,7 @@
397 os.environ.update(environ)395 os.environ.update(environ)
398396
399 if not self.start_container():397 if not self.start_container():
400 self._manager.container_stopped(self.container_id)398 self._service.container_stopped(self.container_id)
401 return399 return
402400
403 self._app_name = app_exec_line[0]401 self._app_name = app_exec_line[0]
404402
=== modified file 'python/libertine/LxdContainer.py'
--- python/libertine/LxdContainer.py 2017-03-03 15:34:16 +0000
+++ python/libertine/LxdContainer.py 2017-03-31 20:05:48 +0000
@@ -22,7 +22,7 @@
22import subprocess22import subprocess
23import time23import time
2424
25from . import Libertine, utils, HostInfo, Client25from . import Libertine, utils, HostInfo
2626
2727
28def _get_devices_map():28def _get_devices_map():
@@ -62,10 +62,11 @@
6262
63def _setup_lxd():63def _setup_lxd():
64 if utils.is_snap_environment():64 if utils.is_snap_environment():
65 utils.get_logger().warning("Running in snap environment, skipping automatic lxd setup.")65 utils.get_logger().warning("Snapped libertine detected, you may need to run `sudo lxd init` manually.")
66 return True66 return True
6767
68 utils.get_logger().info("Running LXD setup.")68 utils.get_logger().debug("Running LXD setup.")
69
69 import pexpect70 import pexpect
70 child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'})71 child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'})
7172
@@ -74,6 +75,7 @@
74 # The following are required for lxd=2.0.x75 # The following are required for lxd=2.0.x
75 '.+\[yes/no\].*',76 '.+\[yes/no\].*',
76 '.+\(e.g. (?P<example>[a-z0-9\.:]+)\).+'])77 '.+\(e.g. (?P<example>[a-z0-9\.:]+)\).+'])
78
77 if index == 0:79 if index == 0:
78 child.sendline()80 child.sendline()
79 elif index == 1:81 elif index == 1:
@@ -383,8 +385,8 @@
383385
384386
385class LibertineLXD(Libertine.BaseContainer):387class LibertineLXD(Libertine.BaseContainer):
386 def __init__(self, name, config):388 def __init__(self, name, config, service):
387 super().__init__(name, 'lxd', config)389 super().__init__(name, 'lxd', config, service)
388 self._host_info = HostInfo.HostInfo()390 self._host_info = HostInfo.HostInfo()
389 self._container = None391 self._container = None
390 self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)392 self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)
@@ -392,15 +394,14 @@
392 if not _setup_lxd():394 if not _setup_lxd():
393 raise Exception("Failed to setup lxd.")395 raise Exception("Failed to setup lxd.")
394396
395 self._client = pylxd.Client()397 self._lxd_client = pylxd.Client()
396 self._manager = Client.Client()
397398
398 def create_libertine_container(self, password=None, multiarch=False):399 def create_libertine_container(self, password=None, multiarch=False):
399 if self._try_get_container():400 if self._try_get_container():
400 utils.get_logger().error("Container already exists")401 utils.get_logger().error("Container already exists")
401 return False402 return False
402403
403 update_libertine_profile(self._client)404 update_libertine_profile(self._lxd_client)
404405
405 utils.get_logger().info("Creating container '%s' with distro '%s'" % (self.container_id, self.installed_release))406 utils.get_logger().info("Creating container '%s' with distro '%s'" % (self.container_id, self.installed_release))
406 create = subprocess.Popen(shlex.split('lxc launch ubuntu-daily:{distro} {id} --profile '407 create = subprocess.Popen(shlex.split('lxc launch ubuntu-daily:{distro} {id} --profile '
@@ -498,13 +499,13 @@
498 return _lxc_args(self.container_id, command, environ)499 return _lxc_args(self.container_id, command, environ)
499500
500 def run_in_container(self, command):501 def run_in_container(self, command):
501 return subprocess.Popen(self._lxc_args(command)).wait()502 return subprocess.Popen(self._lxc_args(command, os.environ.copy())).wait()
502503
503 def start_container(self, home=env_home_path()):504 def start_container(self, home=env_home_path()):
504 if not self._try_get_container():505 if not self._try_get_container():
505 return False506 return False
506507
507 if not self._manager.container_operation_start(self.container_id):508 if not self._service.container_operation_start(self.container_id):
508 return False509 return False
509510
510 if self._container.status == 'Running':511 if self._container.status == 'Running':
@@ -513,12 +514,12 @@
513 requires_remount = self._container.status == 'Stopped'514 requires_remount = self._container.status == 'Stopped'
514515
515 if requires_remount:516 if requires_remount:
516 update_libertine_profile(self._client)517 update_libertine_profile(self._lxd_client)
517 update_bind_mounts(self._container, self._config, home)518 update_bind_mounts(self._container, self._config, home)
518519
519 self._config.update_container_install_status(self.container_id, "starting")520 self._config.update_container_install_status(self.container_id, "starting")
520 if not lxd_start(self._container):521 if not lxd_start(self._container):
521 self._manager.container_stopped()522 self._service.container_stopped()
522 self._config.update_container_install_status(self.container_id, self._container.status.lower())523 self._config.update_container_install_status(self.container_id, self._container.status.lower())
523 return False524 return False
524525
@@ -539,11 +540,11 @@
539 stopped = False540 stopped = False
540 self._config.refresh_database()541 self._config.refresh_database()
541542
542 if self._manager.container_operation_finished(self.container_id, self._app_name, self._pid):543 if self._service.container_operation_finished(self.container_id, self._app_name, self._pid):
543 self._config.update_container_install_status(self.container_id, self._get_stop_type_string(self._freeze_on_stop))544 self._config.update_container_install_status(self.container_id, self._get_stop_type_string(self._freeze_on_stop))
544 545
545 if lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop):546 if lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop):
546 stopped = self._manager.container_stopped(self.container_id)547 stopped = self._service.container_stopped(self.container_id)
547548
548 self._config.update_container_install_status(self.container_id, self._container.status.lower())549 self._config.update_container_install_status(self.container_id, self._container.status.lower())
549550
@@ -598,6 +599,6 @@
598599
599 def _try_get_container(self):600 def _try_get_container(self):
600 if self._container is None:601 if self._container is None:
601 self._container = lxd_container(self._client, self.container_id)602 self._container = lxd_container(self._lxd_client, self.container_id)
602603
603 return self._container is not None604 return self._container is not None
604605
=== added file 'python/libertine/service/constants.py'
--- python/libertine/service/constants.py 1970-01-01 00:00:00 +0000
+++ python/libertine/service/constants.py 2017-03-31 20:05:48 +0000
@@ -0,0 +1,27 @@
1# Copyright 2017 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; version 3 of the License.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.
14
15SERVICE_NAME = "com.canonical.libertine.Service"
16
17OPERATIONS_INTERFACE = "com.canonical.libertine.Service.Operations"
18OPERATIONS_OBJECT = "/com/canonical/libertine/Service/Operations"
19
20DOWNLOAD_INTERFACE = "com.canonical.applications.Service.Download"
21DOWNLOAD_OBJECT = "/com/canonical/libertine/Service/Download/%s"
22
23OPERATIONS_MONITOR_INTERFACE = "com.canonical.libertine.Service.OperationsMonitor"
24OPERATIONS_MONITOR_OBJECT = "/com/canonical/libertine/Service/OperationsMonitor"
25
26CONTAINER_CONTROL_INTERFACE = "com.canonical.libertine.Service.ContainerControl"
27CONTAINER_CONTROL_OBJECT = "/com/canonical/libertine/Service/ContainerControl"
028
=== modified file 'python/libertine/service/container.py'
--- python/libertine/service/container.py 2017-02-07 12:35:48 +0000
+++ python/libertine/service/container.py 2017-03-31 20:05:48 +0000
@@ -22,11 +22,13 @@
2222
2323
24class Container(object):24class Container(object):
25 def __init__(self, container_id, config, connection, callback):25 def __init__(self, container_id, config, monitor, client, callback):
26 self._id = container_id26 self._id = container_id
27 self._connection = connection27 self._config = config
28 self._monitor = monitor
29 self._client = client
28 self._callback = callback30 self._callback = callback
29 self._config = config31
30 self._lock = Lock()32 self._lock = Lock()
31 self._tasks = []33 self._tasks = []
3234
@@ -53,31 +55,6 @@
53 def tasks(self):55 def tasks(self):
54 return [task.id for task in self._tasks if task.running]56 return [task.id for task in self._tasks if task.running]
5557
56 def search(self, query):
57 utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query))
58
59 if utils.is_snap_environment():
60 raise Exception("This operation is not currently supported within the snap")
61
62 task = SearchTask(self.id, self._cache, query, self._connection, self._cleanup_task)
63 self._tasks.append(task)
64 task.start()
65
66 return task.id
67
68 def app_info(self, package_name):
69 utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id))
70
71 if utils.is_snap_environment():
72 raise Exception("This operation is not currently supported within the snap")
73
74 related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running]
75 task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._connection, self._cleanup_task)
76
77 self._tasks.append(task)
78 task.start()
79 return task.id
80
81 def install(self, package_name):58 def install(self, package_name):
82 utils.get_logger().debug("Install package '%s' from container '%s'" % (package_name, self.id))59 utils.get_logger().debug("Install package '%s' from container '%s'" % (package_name, self.id))
8360
@@ -86,7 +63,7 @@
86 utils.get_logger().debug("Install already in progress for '%s':'%s'" % (package_name, self.id))63 utils.get_logger().debug("Install already in progress for '%s':'%s'" % (package_name, self.id))
87 return tasks[0].id64 return tasks[0].id
8865
89 task = InstallTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task)66 task = InstallTask(package_name, self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
90 self._tasks.append(task)67 self._tasks.append(task)
91 task.start()68 task.start()
92 return task.id69 return task.id
@@ -99,7 +76,7 @@
99 utils.get_logger().debug("Remove already in progress for '%s':'%s'" % (package_name, self.id))76 utils.get_logger().debug("Remove already in progress for '%s':'%s'" % (package_name, self.id))
100 return tasks[0].id77 return tasks[0].id
10178
102 task = RemoveTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task)79 task = RemoveTask(package_name, self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
103 self._tasks.append(task)80 self._tasks.append(task)
104 task.start()81 task.start()
105 return task.id82 return task.id
@@ -113,7 +90,7 @@
113 return tasks[0].id90 return tasks[0].id
11491
115 task = CreateTask(self.id, container_name, distro, container_type, enable_multiarch,92 task = CreateTask(self.id, container_name, distro, container_type, enable_multiarch,
116 self._config, self._lock, self._connection, self._cleanup_task)93 self._config, self._lock, self._monitor, self._client, self._cleanup_task)
117 self._tasks.append(task)94 self._tasks.append(task)
118 task.start()95 task.start()
119 return task.id96 return task.id
@@ -126,7 +103,7 @@
126 utils.get_logger().debug("Destroy already in progress for '%s'" % self.id)103 utils.get_logger().debug("Destroy already in progress for '%s'" % self.id)
127 return tasks[0].id104 return tasks[0].id
128105
129 task = DestroyTask(self.id, self._config, self._lock, self._connection, self._cleanup_task)106 task = DestroyTask(self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
130 self._tasks.append(task)107 self._tasks.append(task)
131 task.start()108 task.start()
132 return task.id109 return task.id
@@ -139,15 +116,42 @@
139 utils.get_logger().debug("Update already in progress for '%s'" % self.id)116 utils.get_logger().debug("Update already in progress for '%s'" % self.id)
140 return tasks[0].id117 return tasks[0].id
141118
142 task = UpdateTask(self.id, self._config, self._lock, self._connection, self._cleanup_task)119 task = UpdateTask(self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
143 self._tasks.append(task)120 self._tasks.append(task)
144 task.start()121 task.start()
145 return task.id122 return task.id
146123
124 # Tasks which don't require starting/stopping the container
125
147 def list_app_ids(self):126 def list_app_ids(self):
148 utils.get_logger().debug("List all app ids in container '%s'" % self.id)127 utils.get_logger().debug("List all app ids in container '%s'" % self.id)
149128
150 task = ListAppIdsTask(self.id, self._config, self._connection, self._cleanup_task)129 task = ListAppIdsTask(self.id, self._config, self._monitor, self._client, self._cleanup_task)
130
131 self._tasks.append(task)
132 task.start()
133 return task.id
134
135 def search(self, query):
136 utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query))
137
138 if utils.is_snap_environment():
139 raise Exception("This operation is not currently supported within the snap")
140
141 task = SearchTask(self.id, self._cache, query, self._monitor, self._cleanup_task)
142 self._tasks.append(task)
143 task.start()
144
145 return task.id
146
147 def app_info(self, package_name):
148 utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id))
149
150 if utils.is_snap_environment():
151 raise Exception("This operation is not currently supported within the snap")
152
153 related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running]
154 task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._monitor, self._cleanup_task)
151155
152 self._tasks.append(task)156 self._tasks.append(task)
153 task.start()157 task.start()
154158
=== renamed file 'python/libertine/service/operations_state.py' => 'python/libertine/service/container_control.py'
--- python/libertine/service/operations_state.py 2017-02-23 15:25:17 +0000
+++ python/libertine/service/container_control.py 2017-03-31 20:05:48 +0000
@@ -12,64 +12,35 @@
12# You should have received a copy of the GNU General Public License12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
15import libertine.ContainersConfig15
16import psutil16import dbus
1717
18from collections import Counter18from . import constants
19from libertine import utils19from libertine import utils
2020
2121
22class OperationsState(object):22class ContainerControl(dbus.service.Object):
23 def __init__(self):23 def __init__(self, connection, client):
24 self._get_running_apps_per_container()24 dbus.service.Object.__init__(self, conn=connection, object_path=constants.CONTAINER_CONTROL_OBJECT)
2525 self._client = client
26 def _get_running_apps_per_container(self):26
27 self._invalid_apps = dict()27 @dbus.service.method(constants.CONTAINER_CONTROL_INTERFACE,
28 self._operations = Counter()28 in_signature='s',
29 config = libertine.ContainersConfig.ContainersConfig()29 out_signature='b')
3030 def start(self, container):
31 for container in config.get_containers():31 utils.get_logger().debug("start({})".format(container))
32 running_apps = config.get_running_apps(container).copy()32 return self._client.container_operation_start(container)
3333
34 for app in running_apps:34 @dbus.service.method(constants.CONTAINER_CONTROL_INTERFACE,
35 try:35 in_signature='ssi',
36 proc = psutil.Process(app['pid'])36 out_signature='b')
37 if app['appExecName'] in proc.cmdline():37 def finished(self, container, app_name, pid):
38 self._operations[container] += 138 utils.get_logger().debug("finished({})".format(container))
39 else:39 return self._client.container_operation_finished(container, app_name, pid)
40 raise40
41 except:41 @dbus.service.method(constants.CONTAINER_CONTROL_INTERFACE,
42 utils.get_logger().error("Container app {} is not valid.".format(app['appExecName']))42 in_signature='s',
43 if container not in self._invalid_apps:43 out_signature='b')
44 self._invalid_apps[container] = [{app['appExecName'], app['pid']}]44 def stopped(self, container):
45 else:45 utils.get_logger().debug("stopped({})".format(container))
46 self._invalid_apps[container].append({app['appExecName'], app['pid']})46 return self._client.container_stopped(container)
47 config.delete_running_app(container, app)
48 continue
49
50 def operation_start(self, container):
51 if self._operations[container] == -1:
52 return False
53
54 self._operations[container] += 1
55
56 return True
57
58 def operation_finished(self, container, app_name, pid):
59 if container in self._invalid_apps and {app_name, pid} in self._invalid_apps[container]:
60 self._invalid_apps[container].remove({app_name, pid})
61 if not self._invalid_apps[container]:
62 del self._invalid_apps[container]
63 else:
64 self._operations[container] -= 1
65
66 if self._operations[container] == 0:
67 self._operations[container] = -1
68 return True
69
70 return False
71
72 def operation_stopped(self, container):
73 del self._operations[container]
74
75 return True
7647
=== added file 'python/libertine/service/container_control_client.py'
--- python/libertine/service/container_control_client.py 1970-01-01 00:00:00 +0000
+++ python/libertine/service/container_control_client.py 2017-03-31 20:05:48 +0000
@@ -0,0 +1,75 @@
1# Copyright 2017 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; version 3 of the License.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.
14
15
16import libertine.ContainersConfig
17import psutil
18
19from collections import Counter
20from libertine import utils
21
22
23class ContainerControlClient(object):
24 def __init__(self):
25 self._get_running_apps_per_container()
26
27 def _get_running_apps_per_container(self):
28 self._invalid_apps = dict()
29 self._operations = Counter()
30 config = libertine.ContainersConfig.ContainersConfig()
31
32 for container in config.get_containers():
33 running_apps = config.get_running_apps(container).copy()
34
35 for app in running_apps:
36 try:
37 proc = psutil.Process(app['pid'])
38 if app['appExecName'] in proc.cmdline():
39 self._operations[container] += 1
40 else:
41 raise
42 except:
43 utils.get_logger().error("Container app {} is not valid.".format(app['appExecName']))
44 if container not in self._invalid_apps:
45 self._invalid_apps[container] = [{app['appExecName'], app['pid']}]
46 else:
47 self._invalid_apps[container].append({app['appExecName'], app['pid']})
48 config.delete_running_app(container, app)
49 continue
50
51 def container_operation_start(self, container):
52 if self._operations[container] == -1:
53 return False
54
55 self._operations[container] += 1
56
57 return True
58
59 def container_operation_finished(self, container, app_name, pid):
60 if container in self._invalid_apps and {app_name, pid} in self._invalid_apps[container]:
61 self._invalid_apps[container].remove({app_name, pid})
62 if not self._invalid_apps[container]:
63 del self._invalid_apps[container]
64 else:
65 self._operations[container] -= 1
66
67 if self._operations[container] == 0:
68 self._operations[container] = -1
69 return True
70
71 return False
72
73 def container_stopped(self, container):
74 del self._operations[container]
75 return True
076
=== renamed file 'python/libertine/service/progress.py' => 'python/libertine/service/download.py'
--- python/libertine/service/progress.py 2017-01-24 18:00:57 +0000
+++ python/libertine/service/download.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016-2017 Canonical Ltd.1# Copyright 2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -12,73 +12,68 @@
12# You should have received a copy of the GNU General Public License12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
15
15import dbus.service16import dbus.service
16import threading17import threading
18
19from . import constants
17from libertine import utils20from libertine import utils
18from time import time21from time import time
1922
20DOWNLOAD_INTERFACE = "com.canonical.applications.Download"
21PROGRESS_INTERFACE = "com.canonical.libertine.Service.Progress"
2223
23class Progress(dbus.service.Object):24class Download(dbus.service.Object):
24 def __init__(self, connection):25 def __init__(self, connection, id):
25 utils.get_logger().debug("creating a Progress object")
26 self._finished = False26 self._finished = False
27 self._result = []27 self._result = ''
28 self._error = ''28 self._error = ''
29 dbus.service.Object.__init__(self, conn=connection, object_path=("/Progress/%s" % hex(int(time()*10000000))[2:]))29 dbus.service.Object.__init__(self, conn=connection, object_path=(constants.DOWNLOAD_OBJECT % id))
3030
31 # self.emit_processing() # Disabled until something requires the Download interface31 # Disabled until something requires the Download interface
3232 # self.emit_processing()
33 @property33
34 def id(self):34 # This is currently how services using the Ubuntu SDK to show progress
35 return self._object_path35 # determine whether or not an operation is running.
36
37 def emit_processing(self):36 def emit_processing(self):
38 if not self.done:37 if not self.done:
39 self.processing(self.id)38 self.processing(self.id)
40 threading.Timer(0.5, self.emit_processing).start()39 threading.Timer(0.5, self.emit_processing).start()
4140
42 @property41 @property
42 def id(self):
43 return self._object_path
44
45 @property
43 def done(self):46 def done(self):
44 return self._finished47 return self._finished
4548
46 @dbus.service.signal(DOWNLOAD_INTERFACE)49 @property
50 def result(self):
51 return self._result.strip()
52
53 @property
54 def last_error(self):
55 return self._error
56
57 def data(self, message):
58 self._result += message + '\n'
59
60 # Signals to satisfy the download interface
61
62 @dbus.service.signal(constants.DOWNLOAD_INTERFACE, signature='o')
47 def processing(self, path):63 def processing(self, path):
48 utils.get_logger().debug("emit processing('%s')" % path)64 utils.get_logger().debug("emit processing('%s')" % path)
4965
50 @dbus.service.signal(DOWNLOAD_INTERFACE)66 @dbus.service.signal(constants.DOWNLOAD_INTERFACE, signature='o')
51 def finished(self, path):67 def finished(self, path):
52 utils.get_logger().debug("emit finished('%s')" % path)68 utils.get_logger().debug("emit finished('%s')" % path)
53 self._finished = True69 self._finished = True
5470
55 @dbus.service.signal(DOWNLOAD_INTERFACE)71 @dbus.service.signal(constants.DOWNLOAD_INTERFACE)
56 def progress(self, received, total):72 def progress(self, received, total):
57 utils.get_logger().debug("emit progress(%d, %d)" % (received, total))73 utils.get_logger().debug("emit progress(%d, %d)" % (received, total))
5874
59 @dbus.service.signal(DOWNLOAD_INTERFACE)75 @dbus.service.signal(constants.DOWNLOAD_INTERFACE, signature='s')
60 def error(self, message):76 def error(self, message):
61 utils.get_logger().error("emit error(%s)" % message)77 utils.get_logger().error("emit error(%s)" % message)
62 self._error = message78 self._error = message
63 self._finished = True79 self._finished = True
64
65 @dbus.service.signal(PROGRESS_INTERFACE)
66 def data(self, message):
67 utils.get_logger().debug("emit data(%s)" % message)
68 self._result.append(message)
69
70 @dbus.service.method(PROGRESS_INTERFACE, out_signature='b')
71 def running(self):
72 utils.get_logger().debug(not self.done)
73 return not self.done
74
75 @dbus.service.method(PROGRESS_INTERFACE, out_signature='s')
76 def result(self):
77 full_result = "\n".join(self._result)
78 utils.get_logger().debug(full_result)
79 return full_result
80
81 @dbus.service.method(PROGRESS_INTERFACE, out_signature='s')
82 def last_error(self):
83 utils.get_logger().debug(self._error)
84 return self._error
8580
=== renamed file 'python/libertine/service/manager.py' => 'python/libertine/service/operations.py'
--- python/libertine/service/manager.py 2017-02-21 18:20:21 +0000
+++ python/libertine/service/operations.py 2017-03-31 20:05:48 +0000
@@ -12,69 +12,51 @@
12# You should have received a copy of the GNU General Public License12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
15
15import dbus16import dbus
16import dbus.service17import dbus.service
17import libertine.service.task_dispatcher18
18import libertine.service.operations_state19from . import constants, operations_monitor, task_dispatcher
19from dbus.mainloop.glib import DBusGMainLoop
20from libertine.service import container
21from libertine import utils20from libertine import utils
2221
2322
24LIBERTINE_MANAGER_NAME = "com.canonical.libertine.Service"23class Operations(dbus.service.Object):
25LIBERTINE_MANAGER_INTERFACE = LIBERTINE_MANAGER_NAME24 def __init__(self, bus_name, client):
26LIBERTINE_MANAGER_OBJECT = "/Manager"25 super().__init__(bus_name, constants.OPERATIONS_OBJECT)
2726
2827 self._dispatcher = task_dispatcher.TaskDispatcher(operations_monitor.OperationsMonitor(self.connection), client)
29class Manager(dbus.service.Object):
30 def __init__(self):
31 utils.get_logger().debug("creating service")
32 DBusGMainLoop(set_as_default=True)
33 self._operations_state = libertine.service.operations_state.OperationsState()
34
35 try:
36 bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME,
37 bus=dbus.SessionBus(),
38 do_not_queue=True)
39 except dbus.exceptions.NameExistsException:
40 utils.get_logger().warning("service is already running")
41 raise
42
43 super().__init__(bus_name, LIBERTINE_MANAGER_OBJECT)
44
45 self._dispatcher = libertine.service.task_dispatcher.TaskDispatcher(self.connection)
4628
47 # Information29 # Information
4830
49 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,31 @dbus.service.method(constants.OPERATIONS_INTERFACE,
50 in_signature='ss',32 in_signature='ss',
51 out_signature='o')33 out_signature='o')
52 def search(self, container_id, search_string):34 def search(self, container_id, search_string):
53 utils.get_logger().debug("search('{}', '{}') called".format(container_id, search_string))35 utils.get_logger().debug("search('{}', '{}') called".format(container_id, search_string))
54 return self._dispatcher.search(container_id, search_string)36 return self._dispatcher.search(container_id, search_string)
5537
56 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,38 @dbus.service.method(constants.OPERATIONS_INTERFACE,
57 in_signature='ss',39 in_signature='ss',
58 out_signature='o')40 out_signature='o')
59 def app_info(self, container_id, app_id):41 def app_info(self, container_id, app_id):
60 utils.get_logger().debug("app_info('{}', '{}') called".format(container_id, app_id))42 utils.get_logger().debug("app_info('{}', '{}') called".format(container_id, app_id))
61 return self._dispatcher.app_info(container_id, app_id)43 return self._dispatcher.app_info(container_id, app_id)
6244
63 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,45 @dbus.service.method(constants.OPERATIONS_INTERFACE,
64 in_signature='s',46 in_signature='s',
65 out_signature='o')47 out_signature='o')
66 def container_info(self, container_id):48 def container_info(self, container_id):
67 utils.get_logger().debug("container_info('{}')".format(container_id))49 utils.get_logger().debug("container_info('{}')".format(container_id))
68 return self._dispatcher.container_info(container_id)50 return self._dispatcher.container_info(container_id)
6951
70 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,52 @dbus.service.method(constants.OPERATIONS_INTERFACE,
71 in_signature='s',53 in_signature='s',
72 out_signature='o')54 out_signature='o')
73 def list_app_ids(self, container_id):55 def list_app_ids(self, container_id):
74 utils.get_logger().debug("list_app_ids('{}')".format(container_id))56 utils.get_logger().debug("list_app_ids('{}')".format(container_id))
75 return self._dispatcher.list_app_ids(container_id)57 return self._dispatcher.list_app_ids(container_id)
7658
77 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,59 @dbus.service.method(constants.OPERATIONS_INTERFACE,
78 out_signature='o')60 out_signature='o')
79 def list(self):61 def list(self):
80 utils.get_logger().debug("list()")62 utils.get_logger().debug("list()")
@@ -82,63 +64,37 @@
8264
83 # Operations65 # Operations
8466
85 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,67 @dbus.service.method(constants.OPERATIONS_INTERFACE,
86 in_signature='ssssb',68 in_signature='ssssb',
87 out_signature='o')69 out_signature='o')
88 def create(self, container_id, container_name='', distro='', container_type='', enable_multiarch=False):70 def create(self, container_id, container_name='', distro='', container_type='', enable_multiarch=False):
89 utils.get_logger().debug("create('{}', '{}', '{}', '{}', '{}')".format(container_id, container_name, distro, container_type, enable_multiarch))71 utils.get_logger().debug("create('{}', '{}', '{}', '{}', '{}')".format(container_id, container_name, distro, container_type, enable_multiarch))
90 return self._dispatcher.create(container_id, container_name, distro, container_type, enable_multiarch)72 return self._dispatcher.create(container_id, container_name, distro, container_type, enable_multiarch)
9173
92 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,74 @dbus.service.method(constants.OPERATIONS_INTERFACE,
93 in_signature='s',75 in_signature='s',
94 out_signature='o')76 out_signature='o')
95 def destroy(self, container_id):77 def destroy(self, container_id):
96 utils.get_logger().debug("destroy('{}')".format(container_id))78 utils.get_logger().debug("destroy('{}')".format(container_id))
97 return self._dispatcher.destroy(container_id)79 return self._dispatcher.destroy(container_id)
9880
99 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,81 @dbus.service.method(constants.OPERATIONS_INTERFACE,
100 in_signature='s',82 in_signature='s',
101 out_signature='o')83 out_signature='o')
102 def update(self, container_id):84 def update(self, container_id):
103 utils.get_logger().debug("update('{}')".format(container_id))85 utils.get_logger().debug("update('{}')".format(container_id))
104 return self._dispatcher.update(container_id)86 return self._dispatcher.update(container_id)
10587
106 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,88 @dbus.service.method(constants.OPERATIONS_INTERFACE,
107 in_signature='ss',89 in_signature='ss',
108 out_signature='o')90 out_signature='o')
109 def install(self, container_id, package_name):91 def install(self, container_id, package_name):
110 utils.get_logger().debug("install('%s', '%s')" % (container_id, package_name))92 utils.get_logger().debug("install('%s', '%s')" % (container_id, package_name))
111 return self._dispatcher.install(container_id, package_name)93 return self._dispatcher.install(container_id, package_name)
11294
113 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,95 @dbus.service.method(constants.OPERATIONS_INTERFACE,
114 in_signature='ss',96 in_signature='ss',
115 out_signature='o')97 out_signature='o')
116 def remove(self, container_id, package_name):98 def remove(self, container_id, package_name):
117 utils.get_logger().debug("remove('%s', '%s')" % (container_id, package_name))99 utils.get_logger().debug("remove('%s', '%s')" % (container_id, package_name))
118 return self._dispatcher.remove(container_id, package_name)100 return self._dispatcher.remove(container_id, package_name)
119
120 # Container Lifecycle
121
122 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
123 in_signature='s',
124 out_signature='b')
125 def container_operation_start(self, container):
126 utils.get_logger().debug("container_operation_start({})".format(container))
127
128 return self._operations_state.operation_start(container)
129
130 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
131 in_signature='ssi',
132 out_signature='b')
133 def container_operation_finished(self, container, app_name='', pid=0):
134 utils.get_logger().debug("container_operation_finished({})".format(container))
135
136 return self._operations_state.operation_finished(container, app_name, pid)
137
138 @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
139 in_signature='s',
140 out_signature='b')
141 def container_stopped(self, container):
142 utils.get_logger().debug("container_stopped({})".format(container))
143
144 return self._operations_state.operation_stopped(container)
145101
=== added file 'python/libertine/service/operations_monitor.py'
--- python/libertine/service/operations_monitor.py 1970-01-01 00:00:00 +0000
+++ python/libertine/service/operations_monitor.py 2017-03-31 20:05:48 +0000
@@ -0,0 +1,93 @@
1# Copyright 2017 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; version 3 of the License.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.
14
15
16import dbus.service
17import threading
18import uuid
19
20from . import constants, download
21from libertine import utils
22from time import time
23
24
25class OperationsMonitor(dbus.service.Object):
26 def __init__(self, connection):
27 self._operations = []
28 dbus.service.Object.__init__(self, conn=connection, object_path=constants.OPERATIONS_MONITOR_OBJECT)
29
30 def new_operation(self):
31 self._operations.append(download.Download(self.connection, str(uuid.uuid4().fields[-1])))
32 return self._operations[-1].id
33
34 def remove_from_connection(self, path):
35 operation = self._operation(path)
36 self._operations.remove(operation)
37 operation.remove_from_connection()
38
39 def done(self, path):
40 op = self._operation(path)
41 if op:
42 return op.done
43 else:
44 return False
45
46 def _operation(self, path):
47 operations = [op for op in self._operations if op.id == path]
48 if not operations:
49 return None
50
51 return operations[0]
52
53 @dbus.service.signal(constants.OPERATIONS_MONITOR_INTERFACE, signature='o')
54 def finished(self, path):
55 op = self._operation(path)
56 if op:
57 op.finished(path)
58
59 @dbus.service.signal(constants.OPERATIONS_MONITOR_INTERFACE, signature='os')
60 def error(self, path, message):
61 op = self._operation(path)
62 if op:
63 op.error(message)
64
65 @dbus.service.signal(constants.OPERATIONS_MONITOR_INTERFACE, signature='os')
66 def data(self, path, message):
67 op = self._operation(path)
68 if op:
69 op.data(message)
70
71 @dbus.service.method(constants.OPERATIONS_MONITOR_INTERFACE, in_signature='o', out_signature='b')
72 def running(self, path):
73 op = self._operation(path)
74 if op:
75 return not op.done
76 else:
77 return False
78
79 @dbus.service.method(constants.OPERATIONS_MONITOR_INTERFACE, in_signature='o', out_signature='s')
80 def result(self, path):
81 op = self._operation(path)
82 if op:
83 return op.result
84 else:
85 return ''
86
87 @dbus.service.method(constants.OPERATIONS_MONITOR_INTERFACE, in_signature='o', out_signature='s')
88 def last_error(self, path):
89 op = self._operation(path)
90 if op:
91 return op.last_error
92 else:
93 return ''
094
=== modified file 'python/libertine/service/task_dispatcher.py'
--- python/libertine/service/task_dispatcher.py 2017-02-23 20:47:39 +0000
+++ python/libertine/service/task_dispatcher.py 2017-03-31 20:05:48 +0000
@@ -16,22 +16,13 @@
16import libertine.ContainersConfig16import libertine.ContainersConfig
17from libertine.service.container import Container17from libertine.service.container import Container
18from libertine.service.tasks import *18from libertine.service.tasks import *
19from threading import Lock
20from libertine import utils19from libertine import utils
2120
2221
23# Decorator to refresh database before making a call
24def _refresh_config(func):
25 def wrapper(*args, **kwargs):
26 args[0]._config.refresh_database()
27 return func(*args, **kwargs)
28
29 return wrapper
30
31
32class TaskDispatcher(object):22class TaskDispatcher(object):
33 def __init__(self, connection):23 def __init__(self, monitor, client):
34 self._connection = connection24 self._monitor = monitor
25 self._client = client
35 self._config = libertine.ContainersConfig.ContainersConfig()26 self._config = libertine.ContainersConfig.ContainersConfig()
36 self._containerless_tasks = []27 self._containerless_tasks = []
37 self._tasks = []28 self._tasks = []
@@ -49,11 +40,13 @@
4940
50 def _find_or_create_container(self, container_id):41 def _find_or_create_container(self, container_id):
51 utils.get_logger().debug("finding or creating container '%s'" % container_id)42 utils.get_logger().debug("finding or creating container '%s'" % container_id)
43
52 container = self._find_container(container_id)44 container = self._find_container(container_id)
53 if container is not None:45 if container is not None:
54 utils.get_logger().debug("using existing container '%s'" % container_id)46 utils.get_logger().debug("using existing container '%s'" % container_id)
55 return container47 return container
56 container = Container(container_id, self._config, self._connection, self._cleanup_container)48
49 container = Container(container_id, self._config, self._monitor, self._client, self._cleanup_container)
57 self._containers.append(container)50 self._containers.append(container)
5851
59 return container52 return container
@@ -65,49 +58,40 @@
6558
66 # Tasks (usually) run within a container59 # Tasks (usually) run within a container
6760
68 @_refresh_config
69 def search(self, container_id, query):61 def search(self, container_id, query):
70 utils.get_logger().debug("dispatching search in container '%s' for package '%s'" % (container_id, query))62 utils.get_logger().debug("dispatching search in container '%s' for package '%s'" % (container_id, query))
71 return self._find_or_create_container(container_id).search(query)63 return self._find_or_create_container(container_id).search(query)
7264
73 @_refresh_config
74 def app_info(self, container_id, app_id):65 def app_info(self, container_id, app_id):
75 utils.get_logger().debug("dispatching app_info in container '%s' for package '%s'" % (container_id, app_id))66 utils.get_logger().debug("dispatching app_info in container '%s' for package '%s'" % (container_id, app_id))
76 return self._find_or_create_container(container_id).app_info(app_id)67 return self._find_or_create_container(container_id).app_info(app_id)
7768
78 @_refresh_config
79 def install(self, container_id, package_name):69 def install(self, container_id, package_name):
80 utils.get_logger().debug("dispatching install of package '%s' from container '%s'" % (package_name, container_id))70 utils.get_logger().debug("dispatching install of package '%s' from container '%s'" % (package_name, container_id))
81 return self._find_or_create_container(container_id).install(package_name)71 return self._find_or_create_container(container_id).install(package_name)
8272
83 @_refresh_config
84 def remove(self, container_id, package_name):73 def remove(self, container_id, package_name):
85 utils.get_logger().debug("dispatching remove of package '%s' from container '%s'" % (package_name, container_id))74 utils.get_logger().debug("dispatching remove of package '%s' from container '%s'" % (package_name, container_id))
86 return self._find_or_create_container(container_id).remove(package_name)75 return self._find_or_create_container(container_id).remove(package_name)
8776
88 @_refresh_config
89 def create(self, container_id, container_name, distro, container_type, enable_multiarch):77 def create(self, container_id, container_name, distro, container_type, enable_multiarch):
90 utils.get_logger().debug("dispatching create of container '%s'" % container_id)78 utils.get_logger().debug("dispatching create of container '%s'" % container_id)
91 return self._find_or_create_container(container_id).create(container_name, distro, container_type, enable_multiarch)79 return self._find_or_create_container(container_id).create(container_name, distro, container_type, enable_multiarch)
9280
93 @_refresh_config
94 def destroy(self, container_id):81 def destroy(self, container_id):
95 utils.get_logger().debug("dispatching destroy container '%s'" % container_id)82 utils.get_logger().debug("dispatching destroy container '%s'" % container_id)
96 return self._find_or_create_container(container_id).destroy()83 return self._find_or_create_container(container_id).destroy()
9784
98 @_refresh_config
99 def update(self, container_id):85 def update(self, container_id):
100 utils.get_logger().debug("dispatching update container '%s'" % container_id)86 utils.get_logger().debug("dispatching update container '%s'" % container_id)
101 return self._find_or_create_container(container_id).update()87 return self._find_or_create_container(container_id).update()
10288
103 @_refresh_config
104 def list_app_ids(self, container_id):89 def list_app_ids(self, container_id):
105 utils.get_logger().debug("dispatching list apps ids in container '%s'" % container_id)90 utils.get_logger().debug("dispatching list apps ids in container '%s'" % container_id)
106 return self._find_or_create_container(container_id).list_app_ids()91 return self._find_or_create_container(container_id).list_app_ids()
10792
108 # Containerless Tasks93 # Containerless Tasks
10994
110 @_refresh_config
111 def container_info(self, container_id):95 def container_info(self, container_id):
112 utils.get_logger().debug("dispatching get info for container '%s'" % container_id)96 utils.get_logger().debug("dispatching get info for container '%s'" % container_id)
11397
@@ -115,17 +99,16 @@
115 container = self._find_container(container_id)99 container = self._find_container(container_id)
116 if container is not None:100 if container is not None:
117 related_task_ids = container.tasks101 related_task_ids = container.tasks
118 task = ContainerInfoTask(container_id, related_task_ids, self._config, self._connection, self._cleanup_task)102 task = ContainerInfoTask(container_id, related_task_ids, self._config, self._monitor, self._cleanup_task)
119 self._tasks.append(task)103 self._tasks.append(task)
120 task.start()104 task.start()
121105
122 return task.id106 return task.id
123107
124 @_refresh_config
125 def list(self):108 def list(self):
126 utils.get_logger().debug("dispatching list all containers")109 utils.get_logger().debug("dispatching list all containers")
127110
128 task = ListTask(self._config, self._connection, self._cleanup_task)111 task = ListTask(self._config, self._monitor, self._cleanup_task)
129 self._tasks.append(task)112 self._tasks.append(task)
130 task.start()113 task.start()
131114
132115
=== modified file 'python/libertine/service/tasks/__init__.py'
--- python/libertine/service/tasks/__init__.py 2017-02-07 12:35:48 +0000
+++ python/libertine/service/tasks/__init__.py 2017-03-31 20:05:48 +0000
@@ -12,7 +12,7 @@
12# You should have received a copy of the GNU General Public License along12# You should have received a copy of the GNU General Public License along
13# with this program. If not, see <http://www.gnu.org/licenses/>.13# with this program. If not, see <http://www.gnu.org/licenses/>.
1414
15from .base_task import BaseTask15from .base_task import BaseTask, ContainerBaseTask
16from .app_info_task import AppInfoTask16from .app_info_task import AppInfoTask
17from .container_info_task import ContainerInfoTask17from .container_info_task import ContainerInfoTask
18from .create_task import CreateTask18from .create_task import CreateTask
@@ -27,6 +27,7 @@
27__all__ = [27__all__ = [
28 'AppInfoTask',28 'AppInfoTask',
29 'BaseTask',29 'BaseTask',
30 'ContainerBaseTask',
30 'ContainerInfoTask',31 'ContainerInfoTask',
31 'CreateTask',32 'CreateTask',
32 'DestroyTask',33 'DestroyTask',
3334
=== modified file 'python/libertine/service/tasks/app_info_task.py'
--- python/libertine/service/tasks/app_info_task.py 2016-11-01 17:38:38 +0000
+++ python/libertine/service/tasks/app_info_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -18,8 +18,8 @@
1818
1919
20class AppInfoTask(BaseTask):20class AppInfoTask(BaseTask):
21 def __init__(self, container_id, cache, app_id, tasks, config, connection, callback):21 def __init__(self, container_id, cache, app_id, tasks, config, monitor, callback):
22 super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)22 super().__init__(lock=None, container_id=container_id, config=config, monitor=monitor, callback=callback)
23 self._cache = cache23 self._cache = cache
24 self._app_id = app_id24 self._app_id = app_id
25 self._tasks = tasks25 self._tasks = tasks
@@ -27,9 +27,9 @@
27 def _run(self):27 def _run(self):
28 app = self._cache.app_info(self._app_id)28 app = self._cache.app_info(self._app_id)
29 if app == {}:29 if app == {}:
30 self._progress.error("Could not find app info for '%s' in container '%s'" % (self._app_id, self._container))30 self._error("Could not find app info for '%s' in container '%s'" % (self._app_id, self._container))
31 return31 return
3232
33 app['status'] = self._config.get_package_install_status(self._container, app['package']) or ''33 app['status'] = self._config.get_package_install_status(self._container, app['package']) or ''
34 app['task_ids'] = self._tasks34 app['task_ids'] = self._tasks
35 self._progress.data(str(app))35 self._data(str(app))
3636
=== modified file 'python/libertine/service/tasks/base_task.py'
--- python/libertine/service/tasks/base_task.py 2017-02-17 17:39:13 +0000
+++ python/libertine/service/tasks/base_task.py 2017-03-31 20:05:48 +0000
@@ -12,8 +12,9 @@
12# You should have received a copy of the GNU General Public License12# You should have received a copy of the GNU General Public License
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
15import libertine.service.progress15
16import threading16import threading
17
17from abc import ABCMeta, abstractmethod18from abc import ABCMeta, abstractmethod
1819
1920
@@ -24,24 +25,21 @@
24 in a separate thread. Override _before to implement pre-execution actions25 in a separate thread. Override _before to implement pre-execution actions
25 without locking; if _before returns False, _run will not be executed.26 without locking; if _before returns False, _run will not be executed.
26 """27 """
27 def __init__(self, lock, container_id, config, connection, callback):28 def __init__(self, lock, container_id, config, monitor, callback):
28 self._lock = lock29 self._lock = lock
29 self._container = container_id30 self._container = container_id
30 self._config = config31 self._config = config
31 self._callback = callback32 self._callback = callback
32 self._connection = connection33 self._monitor = monitor
33 self._progress = None34 self._operation_id = None
34 self._instant_callback = False35 self._instant_callback = False # for testing
3536
36 def matches(self, container, klass):37 def matches(self, container, klass):
37 return self._container == container and self.__class__ == klass38 return self._container == container and self.__class__ == klass
3839
39 @property40 @property
40 def id(self):41 def id(self):
41 if self._progress is not None:42 return self._operation_id
42 return self._progress.id
43 else:
44 return None
4543
46 @property44 @property
47 def container(self):45 def container(self):
@@ -53,40 +51,67 @@
5351
54 @property52 @property
55 def running(self):53 def running(self):
56 return not self._progress.done54 return not self._monitor.done(self._operation_id)
5755
58 def _delayed_callback(self):56 def _delayed_callback(self):
59 if self._instant_callback:57 if self._instant_callback:
60 self._callback(self)58 self._callback(self)
61 else:59 else:
62 threading.Timer(10, lambda: (self._progress.remove_from_connection(), self._callback(self))).start()60 threading.Timer(10, lambda: (self._monitor.remove_from_connection(self._operation_id), self._callback(self))).start()
6361
64 def start(self):62 def start(self):
65 self._progress = libertine.service.progress.Progress(self._connection)63 self._operation_id = self._monitor.new_operation()
66 thread = threading.Thread(target=self.run)64 thread = threading.Thread(target=self.run)
67 thread.start()65 thread.start()
68 return thread66 return thread
6967
70 def run(self):68 def run(self):
69 self._refresh_database()
70
71 if not self._before():71 if not self._before():
72 self._progress.finished(self.container)72 self._monitor.finished(self._operation_id)
73 self._delayed_callback()73 self._delayed_callback()
74 return74 return
7575
76 if self._lock is not None:76 if self._lock is not None:
77 with self._lock:77 with self._lock:
78 self._refresh_database(False)
78 self._run()79 self._run()
79 else:80 else:
81 self._refresh_database()
80 self._run()82 self._run()
8183
82 if self.running:84 if self.running:
83 self._progress.finished(self.container)85 self._finished()
8486
85 self._delayed_callback()87 self._delayed_callback()
8688
89 def _refresh_database(self, require_lock=True):
90 if self._config:
91 if require_lock and self._lock is not None:
92 with self._lock:
93 self._config.refresh_database()
94 else:
95 self._config.refresh_database()
96
87 @abstractmethod97 @abstractmethod
88 def _run(self):98 def _run(self):
89 pass99 pass
90100
91 def _before(self):101 def _before(self):
92 return True102 return True
103
104 def _data(self, message):
105 self._monitor.data(self._operation_id, message)
106
107 def _finished(self):
108 self._monitor.finished(self._operation_id)
109
110 def _error(self, message):
111 self._monitor.error(self._operation_id, message)
112
113
114class ContainerBaseTask(BaseTask):
115 def __init__(self, lock, container_id, config, monitor, client, callback):
116 super().__init__(lock=lock, container_id=container_id, config=config, monitor=monitor, callback=callback)
117 self._client = client
93118
=== modified file 'python/libertine/service/tasks/container_info_task.py'
--- python/libertine/service/tasks/container_info_task.py 2017-01-18 20:08:47 +0000
+++ python/libertine/service/tasks/container_info_task.py 2017-03-31 20:05:48 +0000
@@ -20,8 +20,8 @@
2020
2121
22class ContainerInfoTask(BaseTask):22class ContainerInfoTask(BaseTask):
23 def __init__(self, container_id, tasks, config, connection, callback):23 def __init__(self, container_id, tasks, config, monitor, callback):
24 super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)24 super().__init__(lock=None, container_id=container_id, config=config, monitor=monitor, callback=callback)
25 self._tasks = tasks25 self._tasks = tasks
2626
27 def _run(self):27 def _run(self):
@@ -35,11 +35,11 @@
35 container['root'] = utils.get_libertine_container_rootfs_path(self._container)35 container['root'] = utils.get_libertine_container_rootfs_path(self._container)
36 container['home'] = utils.get_libertine_container_home_dir(self._container)36 container['home'] = utils.get_libertine_container_home_dir(self._container)
3737
38 self._progress.data(json.dumps(container))38 self._data(json.dumps(container))
3939
40 def _before(self):40 def _before(self):
41 if not self._config.container_exists(self._container):41 if not self._config.container_exists(self._container):
42 self._progress.error("Container '%s' does not exist, ignoring info request" % self._container)42 self._error("Container '%s' does not exist, ignoring info request" % self._container)
43 return False43 return False
4444
45 return True45 return True
4646
=== modified file 'python/libertine/service/tasks/create_task.py'
--- python/libertine/service/tasks/create_task.py 2017-02-24 15:05:33 +0000
+++ python/libertine/service/tasks/create_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -13,14 +13,16 @@
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
1515
16from .base_task import BaseTask16from .base_task import ContainerBaseTask
17from libertine import LibertineContainer, utils17from libertine import LibertineContainer, utils
18from libertine.HostInfo import HostInfo18from libertine.HostInfo import HostInfo
1919
2020
21class CreateTask(BaseTask):21class CreateTask(ContainerBaseTask):
22 def __init__(self, container_id, container_name, distro, container_type, enable_multiarch, config, lock, connection, callback):22 def __init__(self, container_id, container_name, distro, container_type, enable_multiarch,
23 super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)23 config, lock, monitor, client, callback):
24 super().__init__(lock=lock, container_id=container_id, config=config,
25 monitor=monitor, client=client, callback=callback)
24 self._name = container_name26 self._name = container_name
25 self._distro = distro27 self._distro = distro
26 self._type = container_type28 self._type = container_type
@@ -30,35 +32,36 @@
30 utils.get_logger().debug("Creating container '%s'" % self._container)32 utils.get_logger().debug("Creating container '%s'" % self._container)
3133
32 try:34 try:
33 container = LibertineContainer(self._container, self._config)35 container = LibertineContainer(self._container, self._config, self._client)
3436
35 if not container.create_libertine_container(password='', multiarch=self._multiarch):37 if not container.create_libertine_container(password='', multiarch=self._multiarch):
36 self._config.delete_container(self._container)38 self._config.delete_container(self._container)
37 self._progress.error("Creating container '%s' failed" % self._container)39 self._error("Creating container '%s' failed" % self._container)
38 else:40 else:
39 self._config.update_container_install_status(self._container, "ready")41 self._config.update_container_install_status(self._container, "ready")
42 self._finished()
40 except RuntimeError as e:43 except RuntimeError as e:
41 self._progress.error(str(e))
42 self._config.delete_container(self._container)44 self._config.delete_container(self._container)
45 self._error(str(e))
4346
44 def _before(self):47 def _before(self):
45 utils.get_logger().debug("CreateTask::_before")48 utils.get_logger().debug("CreateTask::_before")
46 if self._config.container_exists(self._container):49 if self._config.container_exists(self._container):
47 self._progress.error("Container '%s' already exists" % self._container)50 self._error("Container '%s' already exists" % self._container)
48 return False51 return False
4952
50 info = HostInfo()53 info = HostInfo()
51 if not self._distro:54 if not self._distro:
52 self._distro = info.get_host_distro_release()55 self._distro = info.get_host_distro_release()
53 elif not info.is_distro_valid(self._distro):56 elif not info.is_distro_valid(self._distro):
54 self._progress.error("Invalid distro '%s'." % self._distro)57 self._error("Invalid distro '%s'." % self._distro)
55 return False58 return False
5659
57 if not self._type:60 if not self._type:
58 self._type = info.select_container_type_by_kernel()61 self._type = info.select_container_type_by_kernel()
59 elif (self._type == 'lxd' and not info.has_lxd_support()) or \62 elif (self._type == 'lxd' and not info.has_lxd_support()) or \
60 (self._type == 'lxc' and not info.has_lxc_support()):63 (self._type == 'lxc' and not info.has_lxc_support()):
61 self._progress.error("System kernel does not support %s type containers. "64 self._error("System kernel does not support %s type containers. "
62 "Please either use chroot or leave empty." % self._type)65 "Please either use chroot or leave empty." % self._type)
63 return False66 return False
6467
6568
=== modified file 'python/libertine/service/tasks/destroy_task.py'
--- python/libertine/service/tasks/destroy_task.py 2016-11-01 17:38:38 +0000
+++ python/libertine/service/tasks/destroy_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -13,29 +13,31 @@
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
1515
16from .base_task import BaseTask16from .base_task import ContainerBaseTask
17from libertine import LibertineContainer, utils17from libertine import LibertineContainer, utils
1818
1919
20class DestroyTask(BaseTask):20class DestroyTask(ContainerBaseTask):
21 def __init__(self, container_id, config, lock, connection, callback):21 def __init__(self, container_id, config, lock, monitor, client, callback):
22 super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)22 super().__init__(lock=lock, container_id=container_id, config=config,
23 monitor=monitor, client=client, callback=callback)
2324
24 def _run(self):25 def _run(self):
25 utils.get_logger().debug("Destroying container '%s'" % self._container)26 utils.get_logger().debug("Destroying container '%s'" % self._container)
2627
27 container = LibertineContainer(self._container, self._config)28 container = LibertineContainer(self._container, self._config, self._client)
28 if not container.destroy_libertine_container():29 if not container.destroy_libertine_container():
29 self._progress.error("Destroying container '%s' failed" % self._container)30 self._error("Destroying container '%s' failed" % self._container)
30 self._config.update_container_install_status(self._container, "ready")31 self._config.update_container_install_status(self._container, "ready")
31 return32 return
3233
33 self._config.delete_container(self._container)34 self._config.delete_container(self._container)
35 self._finished()
3436
35 def _before(self):37 def _before(self):
36 utils.get_logger().debug("CreateTask::_before")38 utils.get_logger().debug("CreateTask::_before")
37 if self._config._get_value_by_key(self._container, 'installStatus') != 'ready':39 if self._config._get_value_by_key(self._container, 'installStatus') != 'ready':
38 self._progress.error("Container '%s' does not exist" % self._container)40 self._error("Container '%s' does not exist" % self._container)
39 return False41 return False
4042
41 self._config.update_container_install_status(self._container, 'removing')43 self._config.update_container_install_status(self._container, 'removing')
4244
=== modified file 'python/libertine/service/tasks/install_task.py'
--- python/libertine/service/tasks/install_task.py 2016-11-08 15:45:54 +0000
+++ python/libertine/service/tasks/install_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -13,13 +13,14 @@
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
1515
16from .base_task import BaseTask16from .base_task import ContainerBaseTask
17from libertine import LibertineContainer, utils17from libertine import LibertineContainer, utils
1818
1919
20class InstallTask(BaseTask):20class InstallTask(ContainerBaseTask):
21 def __init__(self, package_name, container_id, config, lock, connection, callback):21 def __init__(self, package_name, container_id, config, lock, monitor, client, callback):
22 super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)22 super().__init__(lock=lock, container_id=container_id, config=config,
23 monitor=monitor, client=client, callback=callback)
23 self._package = package_name24 self._package = package_name
2425
25 def matches(self, package, klass):26 def matches(self, package, klass):
@@ -31,17 +32,18 @@
3132
32 def _run(self):33 def _run(self):
33 utils.get_logger().debug("Installing package '%s'" % self._package)34 utils.get_logger().debug("Installing package '%s'" % self._package)
34 container = LibertineContainer(self._container, self._config)35 container = LibertineContainer(self._container, self._config, self._client)
35 if container.install_package(self._package):36 if container.install_package(self._package):
36 self._config.update_package_install_status(self._container, self._package, "installed")37 self._config.update_package_install_status(self._container, self._package, "installed")
38 self._finished()
37 else:39 else:
38 self._config.delete_package(self._container, self._package)40 self._config.delete_package(self._container, self._package)
39 self._progress.error("Package installation failed for '%s'" % self._package)41 self._error("Package installation failed for '%s'" % self._package)
4042
41 def _before(self):43 def _before(self):
42 utils.get_logger().debug("InstallTask::_before")44 utils.get_logger().debug("InstallTask::_before")
43 if self._config.package_exists(self._container, self._package):45 if self._config.package_exists(self._container, self._package):
44 self._progress.error("Package '%s' already exists, skipping install" % self._package)46 self._error("Package '%s' already exists, skipping install" % self._package)
45 return False47 return False
46 else:48 else:
47 self._config.add_new_package(self._container, self._package)49 self._config.add_new_package(self._container, self._package)
4850
=== modified file 'python/libertine/service/tasks/list_app_ids_task.py'
--- python/libertine/service/tasks/list_app_ids_task.py 2017-01-20 15:20:13 +0000
+++ python/libertine/service/tasks/list_app_ids_task.py 2017-03-31 20:05:48 +0000
@@ -14,22 +14,22 @@
1414
1515
16import json16import json
17from .base_task import BaseTask17from .base_task import ContainerBaseTask
18from libertine import LibertineContainer, utils18from libertine import LibertineContainer, utils
19import time19import time
2020
2121
22class ListAppIdsTask(BaseTask):22class ListAppIdsTask(ContainerBaseTask):
23 def __init__(self, container_id, config, connection, callback):23 def __init__(self, container_id, config, monitor, client, callback):
24 super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)24 super().__init__(lock=None, container_id=container_id, config=config, monitor=monitor, client=client, callback=callback)
2525
26 def _run(self):26 def _run(self):
27 utils.get_logger().debug("Listing app ids from container '%s'" % self._container)27 utils.get_logger().debug("Listing app ids from container '%s'" % self._container)
28 self._progress.data(json.dumps(LibertineContainer(self._container, self._config).list_app_ids()))28 self._data(json.dumps(LibertineContainer(self._container, self._config, self._client).list_app_ids()))
2929
30 def _before(self):30 def _before(self):
31 if not self._config.container_exists(self._container):31 if not self._config.container_exists(self._container):
32 self._progress.error("Container '%s' does not exist, skipping list" % self._container)32 self._error("Container '%s' does not exist, skipping list" % self._container)
33 return False33 return False
3434
35 return True35 return True
3636
=== modified file 'python/libertine/service/tasks/list_task.py'
--- python/libertine/service/tasks/list_task.py 2017-01-18 20:08:47 +0000
+++ python/libertine/service/tasks/list_task.py 2017-03-31 20:05:48 +0000
@@ -20,8 +20,9 @@
2020
2121
22class ListTask(BaseTask):22class ListTask(BaseTask):
23 def __init__(self, config, connection, callback):23 def __init__(self, config, monitor, callback):
24 super().__init__(lock=None, container_id=None, config=config, connection=connection, callback=callback)24 super().__init__(lock=None, container_id=None, config=config, monitor=monitor, callback=callback)
2525
26 def _run(self):26 def _run(self):
27 self._progress.data(json.dumps(self._config.get_containers()))27 self._data(json.dumps(self._config.get_containers()))
28 self._finished()
2829
=== modified file 'python/libertine/service/tasks/remove_task.py'
--- python/libertine/service/tasks/remove_task.py 2016-11-08 15:45:54 +0000
+++ python/libertine/service/tasks/remove_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -13,13 +13,13 @@
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
1515
16from .base_task import BaseTask16from .base_task import ContainerBaseTask
17from libertine import LibertineContainer, utils17from libertine import LibertineContainer, utils
1818
1919
20class RemoveTask(BaseTask):20class RemoveTask(ContainerBaseTask):
21 def __init__(self, package_name, container_id, config, lock, connection, callback):21 def __init__(self, package_name, container_id, config, lock, monitor, client, callback):
22 super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)22 super().__init__(lock=lock, container_id=container_id, config=config, monitor=monitor, client=client, callback=callback)
23 self._package = package_name23 self._package = package_name
2424
25 def matches(self, package, klass):25 def matches(self, package, klass):
@@ -31,12 +31,13 @@
3131
32 def _run(self):32 def _run(self):
33 utils.get_logger().debug("Removing package '%s'" % self._package)33 utils.get_logger().debug("Removing package '%s'" % self._package)
34 container = LibertineContainer(self._container, self._config)34 container = LibertineContainer(self._container, self._config, self._client)
35 if container.remove_package(self._package):35 if container.remove_package(self._package):
36 self._config.delete_package(self._container, self._package)36 self._config.delete_package(self._container, self._package)
37 self._finished()
37 else:38 else:
38 self._config.update_package_install_status(self._container, self._package, 'installed')39 self._config.update_package_install_status(self._container, self._package, 'installed')
39 self._progress.error("Package removal failed for '%s'" % self._package)40 self._error("Package removal failed for '%s'" % self._package)
4041
41 def _before(self):42 def _before(self):
42 utils.get_logger().debug("RemoveTask::_before")43 utils.get_logger().debug("RemoveTask::_before")
@@ -44,5 +45,5 @@
44 self._config.update_package_install_status(self._container, self._package, "removing")45 self._config.update_package_install_status(self._container, self._package, "removing")
45 return True46 return True
46 else:47 else:
47 self._progress.error("Package '%s' not installed, skipping remove" % self._package)48 self._error("Package '%s' not installed, skipping remove" % self._package)
48 return False49 return False
4950
=== modified file 'python/libertine/service/tasks/search_task.py'
--- python/libertine/service/tasks/search_task.py 2016-11-01 19:49:32 +0000
+++ python/libertine/service/tasks/search_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -18,10 +18,10 @@
1818
1919
20class SearchTask(BaseTask):20class SearchTask(BaseTask):
21 def __init__(self, container_id, cache, query, connection, callback):21 def __init__(self, container_id, cache, query, monitor, callback):
22 super().__init__(lock=None, container_id=container_id, config=None, connection=connection, callback=callback)22 super().__init__(lock=None, container_id=container_id, config=None, monitor=monitor, callback=callback)
23 self._cache = cache23 self._cache = cache
24 self._query = query24 self._query = query
2525
26 def _run(self):26 def _run(self):
27 self._progress.data(str(self._cache.search(self._query)))27 self._data(str(self._cache.search(self._query)))
2828
=== modified file 'python/libertine/service/tasks/update_task.py'
--- python/libertine/service/tasks/update_task.py 2016-11-08 15:37:58 +0000
+++ python/libertine/service/tasks/update_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -13,27 +13,28 @@
13# along with this program. If not, see <http://www.gnu.org/licenses/>.13# along with this program. If not, see <http://www.gnu.org/licenses/>.
1414
1515
16from .base_task import BaseTask16from .base_task import ContainerBaseTask
17from libertine import LibertineContainer, utils17from libertine import LibertineContainer, utils
1818
1919
20class UpdateTask(BaseTask):20class UpdateTask(ContainerBaseTask):
21 def __init__(self, container_id, config, lock, connection, callback):21 def __init__(self, container_id, config, lock, monitor, client, callback):
22 super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)22 super().__init__(lock=lock, container_id=container_id, config=config,
23 monitor=monitor, client=client, callback=callback)
2324
24 def _run(self):25 def _run(self):
25 utils.get_logger().debug("Updating container '%s'" % self._container)26 utils.get_logger().debug("Updating container '%s'" % self._container)
26 container = LibertineContainer(self._container, self._config)27 container = LibertineContainer(self._container, self._config, self._client)
27 self._config.update_container_install_status(self._container, "updating")28 self._config.update_container_install_status(self._container, "updating")
28 if not container.update_libertine_container():29 if not container.update_libertine_container():
29 self._progress.error("Failed to update container '%s'" % self._container)30 self._error("Failed to update container '%s'" % self._container)
3031
31 self._config.update_container_install_status(self._container, "ready")32 self._config.update_container_install_status(self._container, "ready")
3233
33 def _before(self):34 def _before(self):
34 utils.get_logger().debug("UpdateTask::_before")35 utils.get_logger().debug("UpdateTask::_before")
35 if not self._config.container_exists(self._container):36 if not self._config.container_exists(self._container):
36 self._progress.error("Container '%s' does not exist, skipping update" % self._container)37 self._error("Container '%s' does not exist, skipping update" % self._container)
37 return False38 return False
38 else:39
39 return True40 return True
4041
=== modified file 'snap/plugins/x-libertine-deps.py'
--- snap/plugins/x-libertine-deps.py 2017-02-14 19:17:30 +0000
+++ snap/plugins/x-libertine-deps.py 2017-03-31 20:05:48 +0000
@@ -145,7 +145,8 @@
145 '-usr/lib/{}/liblibertine.so*'.format(self._arch),145 '-usr/lib/{}/liblibertine.so*'.format(self._arch),
146 '-usr/bin/libertine*',146 '-usr/bin/libertine*',
147 '-etc/sudoers.d/libertine*',147 '-etc/sudoers.d/libertine*',
148 '-usr/lib/python3/dist-packages/libertine'148 '-usr/lib/python3/dist-packages/libertine',
149 '-usr/share/bash-completion/completions/*'
149 ])150 ])
150151
151 def build(self):152 def build(self):
152153
=== modified file 'snap/snap-runner.wrapper' (properties changed: -x to +x)
=== modified file 'tests/integration/CMakeLists.txt'
--- tests/integration/CMakeLists.txt 2017-03-06 15:33:12 +0000
+++ tests/integration/CMakeLists.txt 2017-03-31 20:05:48 +0000
@@ -1,4 +1,24 @@
1add_test(test_libertine_service dbus-run-session -- /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/test_libertine_service.py)1add_test(test_libertine_service dbus-run-session -- /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/test_libertine_service.py)
2set_tests_properties(test_libertine_service2set_tests_properties(test_libertine_service
3 PROPERTIES ENVIRONMENT3 PROPERTIES ENVIRONMENT
4 "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;LIBERTINE_DATA_DIR=${CMAKE_CURRENT_SOURCE_DIR};PATH=${CMAKE_SOURCE_DIR}/tools:$ENV{PATH};")4 "LIBERTINE_DEBUG=2;PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;;PATH=${CMAKE_SOURCE_DIR}/tools:$ENV{PATH};XDG_DATA_HOME=/tmp")
5
6add_executable(
7 test_liblibertine
8 test_liblibertine.cpp
9)
10
11target_link_libraries(
12 test_liblibertine
13 ${LIBERTINE_CORE}
14 gtest gtest_main
15 ${DBUSTEST_LIBRARIES}
16 ${GIO2_LIBRARIES}
17 Qt5::Core
18)
19
20add_test(test_liblibertine test_liblibertine)
21
22set_tests_properties(test_liblibertine
23 PROPERTIES ENVIRONMENT
24 "PYTHONPATH=${CMAKE_SOURCE_DIR}/python;PATH=${CMAKE_SOURCE_DIR}/tools:$ENV{PATH};XDG_DATA_HOME=${CMAKE_CURRENT_SOURCE_DIR}/data;XDG_CACHE_HOME=${CMAKE_CURRENT_SOURCE_DIR}/data")
525
=== added directory 'tests/integration/data'
=== added directory 'tests/integration/data/libertine'
=== added directory 'tests/integration/data/libertine-container'
=== added directory 'tests/integration/data/libertine-container/padme'
=== added directory 'tests/integration/data/libertine-container/padme/rootfs'
=== added directory 'tests/integration/data/libertine-container/padme/rootfs/usr'
=== added directory 'tests/integration/data/libertine-container/padme/rootfs/usr/share'
=== added directory 'tests/integration/data/libertine-container/padme/rootfs/usr/share/applications'
=== added file 'tests/integration/data/libertine-container/padme/rootfs/usr/share/applications/dagobah.desktop'
=== added file 'tests/integration/data/libertine-container/padme/rootfs/usr/share/applications/tatooine.desktop'
=== added directory 'tests/integration/data/libertine-container/user-data'
=== added directory 'tests/integration/data/libertine-container/user-data/padme'
=== added file 'tests/integration/data/libertine/ContainersConfig.json'
--- tests/integration/data/libertine/ContainersConfig.json 1970-01-01 00:00:00 +0000
+++ tests/integration/data/libertine/ContainersConfig.json 2017-03-31 20:05:48 +0000
@@ -0,0 +1,19 @@
1{
2 "containerList": [
3 {
4 "id": "jarjar",
5 "name": "JarJar Binks",
6 "type": "mock"
7 },
8 {
9 "id": "padme",
10 "name": "Padme Amedala",
11 "type": "mock"
12 },
13 {
14 "id": "anakin",
15 "name": "Anakin Skywalker",
16 "type": "mock"
17 }
18 ]
19}
020
=== modified file 'tests/integration/test_libertine_service.py'
--- tests/integration/test_libertine_service.py 2017-03-06 15:33:12 +0000
+++ tests/integration/test_libertine_service.py 2017-03-31 20:05:48 +0000
@@ -26,7 +26,7 @@
2626
27from gi.repository import GLib27from gi.repository import GLib
28from libertine import utils28from libertine import utils
29from libertine.service import tasks, apt29from libertine.service import tasks, apt, constants
30from libertine.ContainersConfig import ContainersConfig30from libertine.ContainersConfig import ContainersConfig
31from subprocess import Popen, PIPE31from subprocess import Popen, PIPE
32from unittest import TestCase32from unittest import TestCase
@@ -42,18 +42,14 @@
42 def setUpClass(cls):42 def setUpClass(cls):
43 cls._tempdir = tempfile.TemporaryDirectory()43 cls._tempdir = tempfile.TemporaryDirectory()
4444
45 environ = os.environ.copy()45 os.environ['XDG_DATA_HOME'] = cls._tempdir.name
46 environ['XDG_DATA_HOME'] = cls._tempdir.name46 cls._process = pexpect.spawnu('libertined --debug', env=os.environ.copy())
47
48 cls._process = pexpect.spawnu('libertined --debug', env=environ)
49 cls._process.logfile = sys.stdout47 cls._process.logfile = sys.stdout
5048
51 # give libertined enough time to start the whole process49 # give libertined enough time to start the whole process
52 verbosity = environ.get('LIBERTINE_DEBUG', '1')50 verbosity = os.environ.get('LIBERTINE_DEBUG', '1')
53 if verbosity == '1':51 if verbosity != '0':
54 cls._process.expect(['.+\n', pexpect.TIMEOUT], timeout=1)52 cls._process.expect(['libertined ready', pexpect.TIMEOUT], timeout=5)
55 elif environ['LIBERTINE_DEBUG'] == '2':
56 cls._process.expect(['.+\n.+\n.+\n', pexpect.TIMEOUT], timeout=1)
5753
58 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)54 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
59 cls._loop = GLib.MainLoop()55 cls._loop = GLib.MainLoop()
@@ -68,14 +64,25 @@
68 cls._tempdir.cleanup()64 cls._tempdir.cleanup()
6965
70 def setUp(self):66 def setUp(self):
67 os.environ['XDG_DATA_HOME'] = TestLibertineService._tempdir.name
71 self.error = None68 self.error = None
72 self.result = None69 self.result = None
73 self.event = threading.Event()70 self.event = threading.Event()
71 self.signals = []
7472
75 for retries in range(1, 11):73 for retries in range(1, 11):
76 try:74 try:
77 self._bus = dbus.SessionBus()75 self._bus = dbus.SessionBus()
78 self._libertined = self._bus.get_object('com.canonical.libertine.Service', '/Manager')76 self.path = None
77
78 self._libertined = self._bus.get_object(constants.SERVICE_NAME, constants.OPERATIONS_OBJECT)
79
80 self.signals.append(self._bus.add_signal_receiver(path=constants.OPERATIONS_MONITOR_OBJECT, handler_function=self._finished_handler,
81 dbus_interface=constants.OPERATIONS_MONITOR_INTERFACE, signal_name='finished'))
82 self.signals.append(self._bus.add_signal_receiver(path=constants.OPERATIONS_MONITOR_OBJECT, handler_function=self._data_handler,
83 dbus_interface=constants.OPERATIONS_MONITOR_INTERFACE, signal_name='data'))
84 self.signals.append(self._bus.add_signal_receiver(path=constants.OPERATIONS_MONITOR_OBJECT, handler_function=self._error_handler,
85 dbus_interface=constants.OPERATIONS_MONITOR_INTERFACE, signal_name='error'))
79 break86 break
80 except dbus.DBusException as e:87 except dbus.DBusException as e:
81 print("Service not available (attempt %i/10). Exception: %s" % (retries, str(e)))88 print("Service not available (attempt %i/10). Exception: %s" % (retries, str(e)))
@@ -85,39 +92,35 @@
85 except Exception as e:92 except Exception as e:
86 self.fail('Exception occurred during connection: %s' % str(e))93 self.fail('Exception occurred during connection: %s' % str(e))
8794
95 def tearDown(self):
96 for signal in self.signals:
97 signal.remove()
98
88 def _finished_handler(self, path):99 def _finished_handler(self, path):
89 self.event.set()100 if self.path == path:
90101 self.event.set()
91 def _data_handler(self, message):102
92 self.result = message103 def _data_handler(self, path, message):
93104 if self.path == path:
94 def _error_handler(self, message):105 self.result = message
95 self.error = message106
96 self.event.set()107 def _error_handler(self, path, message):
108 if self.path == path:
109 self.error = message
110 self.event.set()
97111
98 def _send(self, func):112 def _send(self, func):
99 self.event.clear()113 self.event.clear()
100 self.result = None114 self.result = None
101115
102 obj_path = func()116 monitor = self._bus.get_object(constants.SERVICE_NAME, constants.OPERATIONS_MONITOR_OBJECT)
103 signals = []117 self.path = func()
104 signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._finished_handler,118
105 dbus_interface='com.canonical.applications.Download', signal_name='finished'))119 while monitor.running(self.path):
106 signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._data_handler,120 self.event.wait(.1)
107 dbus_interface='com.canonical.libertine.Service.Progress', signal_name='data'))121
108 signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._error_handler,122 self.assertEqual('', monitor.last_error(self.path))
109 dbus_interface='com.canonical.applications.Download', signal_name='error'))123 self.result = monitor.result(self.path)
110
111 task = self._bus.get_object('com.canonical.libertine.Service', obj_path)
112 if task.running():
113 self.event.wait(5)
114 self.assertIsNone(self.error)
115
116 self.assertEqual('', task.get_dbus_method('last_error', 'com.canonical.libertine.Service.Progress')())
117 self.result = task.get_dbus_method('result', 'com.canonical.libertine.Service.Progress')()
118
119 for signal in signals:
120 self._bus._clean_up_signal_match(signal)
121124
122 return self.result125 return self.result
123126
124127
=== added file 'tests/integration/test_liblibertine.cpp'
--- tests/integration/test_liblibertine.cpp 1970-01-01 00:00:00 +0000
+++ tests/integration/test_liblibertine.cpp 2017-03-31 20:05:48 +0000
@@ -0,0 +1,145 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, version 3, as published by the
6 * Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "liblibertine/libertine.h"
18#include <cstdlib>
19#include <gio/gio.h>
20#include <gtest/gtest.h>
21#include <libdbustest/dbus-test.h>
22#include <memory>
23#include <QtCore/QByteArray>
24#include <QtCore/QJsonDocument>
25#include <QtCore/QJsonParseError>
26
27class LiblibertineTest : public ::testing::Test
28{
29protected:
30 static void SetUpTestCase()
31 {
32 process = dbus_test_process_new("libertined");
33 dbus_test_process_append_param(process, "--debug");
34
35 dbus_test_task_set_bus(DBUS_TEST_TASK(process), DBUS_TEST_SERVICE_BUS_SESSION);
36 dbus_test_task_set_name(DBUS_TEST_TASK(process), "libertine");
37 dbus_test_task_set_return(DBUS_TEST_TASK(process), DBUS_TEST_TASK_RETURN_IGNORE);
38 dbus_test_task_set_wait_finished(DBUS_TEST_TASK(process), FALSE);
39
40 wait = dbus_test_task_new();
41 dbus_test_task_set_wait_for(wait, "com.canonical.libertine.Service");
42
43 service = dbus_test_service_new(nullptr);
44 dbus_test_service_add_task(service, DBUS_TEST_TASK(process));
45 dbus_test_service_add_task(service, wait);
46
47 dbus_test_service_start_tasks(service);
48
49 bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
50 g_dbus_connection_set_exit_on_close(bus, FALSE);
51 g_object_add_weak_pointer(G_OBJECT(bus), (gpointer*)&bus);
52 }
53
54 static void TearDownTestCase()
55 {
56 g_clear_object(&process);
57 g_clear_object(&wait);
58
59 g_clear_object(&service);
60 g_object_unref(bus);
61 }
62
63private:
64 static DbusTestProcess* process;
65 static DbusTestTask* wait;
66 static DbusTestService* service;
67 static GDBusConnection* bus;
68};
69
70
71DbusTestProcess* LiblibertineTest::process = nullptr;
72DbusTestTask* LiblibertineTest::wait = nullptr;
73DbusTestService* LiblibertineTest::service = nullptr;
74GDBusConnection* LiblibertineTest::bus = nullptr;
75
76
77TEST_F(LiblibertineTest, libertine_list_containers)
78{
79 auto scontainers = std::shared_ptr<gchar*>(libertine_list_containers(), g_strfreev);
80 auto containers = scontainers.get();
81
82 ASSERT_NE(containers[0], nullptr);
83 EXPECT_STREQ(containers[0], "jarjar");
84
85 ASSERT_NE(containers[1], nullptr);
86 EXPECT_STREQ(containers[1], "padme");
87
88 ASSERT_NE(containers[2], nullptr);
89 EXPECT_STREQ(containers[2], "anakin");
90
91 ASSERT_EQ(containers[3], nullptr);
92}
93
94
95TEST_F(LiblibertineTest, libertine_list_app_ids)
96{
97 auto sapps = std::shared_ptr<gchar*>(libertine_list_apps_for_container("padme"), g_strfreev);
98 auto apps = sapps.get();
99
100 ASSERT_NE(apps[0], nullptr);
101 EXPECT_STREQ(apps[0], "padme_dagobah_0.0");
102
103 ASSERT_NE(apps[1], nullptr);
104 EXPECT_STREQ(apps[1], "padme_tatooine_0.0");
105
106 ASSERT_EQ(apps[2], nullptr);
107}
108
109
110TEST_F(LiblibertineTest, libertine_container_name)
111{
112 auto actual = libertine_container_name("padme");
113 EXPECT_STREQ("Padme Amedala", actual);
114 g_free(actual);
115}
116
117
118TEST_F(LiblibertineTest, libertine_container_path)
119{
120 auto actual = libertine_container_path("padme");
121 auto expected = QString(getenv("XDG_CACHE_HOME")) + "/libertine-container/padme/rootfs";
122 EXPECT_STREQ(expected.toUtf8(), actual);
123 g_free(actual);
124}
125
126
127TEST_F(LiblibertineTest, libertine_container_path_returns_empty)
128{
129 EXPECT_EQ(nullptr, libertine_container_path("jarjar"));
130}
131
132
133TEST_F(LiblibertineTest, libertine_container_home_path)
134{
135 auto actual = libertine_container_home_path("padme");
136 auto expected = QString(getenv("XDG_DATA_HOME")) + "/libertine-container/user-data/padme";
137 EXPECT_STREQ(expected.toUtf8(), actual);
138 g_free(actual);
139}
140
141
142TEST_F(LiblibertineTest, libertine_container_home_path_returns_empty)
143{
144 EXPECT_EQ(nullptr, libertine_container_home_path("jarjar"));
145}
0146
=== modified file 'tests/unit/CMakeLists.txt'
--- tests/unit/CMakeLists.txt 2017-02-13 21:41:36 +0000
+++ tests/unit/CMakeLists.txt 2017-03-31 20:05:48 +0000
@@ -8,9 +8,10 @@
8 ContainerConfigListTests.cpp8 ContainerConfigListTests.cpp
9 ContainersConfigTests.cpp9 ContainersConfigTests.cpp
10)10)
11
11target_link_libraries(12target_link_libraries(
12 test_container_config13 test_container_config
13 libertine-common14 ${LIBERTINE_COMMON}
14 gtest gtest_main15 gtest gtest_main
15 Qt5::Core16 Qt5::Core
16)17)
1718
=== modified file 'tests/unit/ContainerConfigListTests.cpp'
--- tests/unit/ContainerConfigListTests.cpp 2017-02-08 14:57:34 +0000
+++ tests/unit/ContainerConfigListTests.cpp 2017-03-31 20:05:48 +0000
@@ -34,6 +34,7 @@
34 EXPECT_EQ(container_configs.size(), 0);34 EXPECT_EQ(container_configs.size(), 0);
35}35}
3636
37
37/** Verify constructing a ContainerConfig from JSON DTRT. */38/** Verify constructing a ContainerConfig from JSON DTRT. */
38TEST(LibertineContainerConfigList, constructFromJson)39TEST(LibertineContainerConfigList, constructFromJson)
39{40{
4041
=== modified file 'tests/unit/service/CMakeLists.txt'
--- tests/unit/service/CMakeLists.txt 2016-11-04 13:49:40 +0000
+++ tests/unit/service/CMakeLists.txt 2017-03-31 20:05:48 +0000
@@ -1,5 +1,5 @@
1function(create_service_unit_test test_name)1function(create_service_unit_test test_name)
2 add_test(${test_name} /usr/bin/python3 -m testtools.run ${test_name})2 add_test(${test_name} /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.py)
3 set_tests_properties(${test_name}3 set_tests_properties(${test_name}
4 PROPERTIES ENVIRONMENT4 PROPERTIES ENVIRONMENT
5 "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}")5 "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}")
@@ -8,5 +8,6 @@
8create_service_unit_test(test_container)8create_service_unit_test(test_container)
9create_service_unit_test(test_apt)9create_service_unit_test(test_apt)
10create_service_unit_test(test_task_dispatcher)10create_service_unit_test(test_task_dispatcher)
11create_service_unit_test(test_operations_monitor)
1112
12add_subdirectory(tasks)13add_subdirectory(tasks)
1314
=== modified file 'tests/unit/service/tasks/test_app_info_task.py'
--- tests/unit/service/tasks/test_app_info_task.py 2016-11-07 18:51:17 +0000
+++ tests/unit/service/tasks/test_app_info_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,7 +15,7 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks, apt18from libertine.service import tasks, apt, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
@@ -23,39 +23,37 @@
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.cache = unittest.mock.create_autospec(apt.AptCache)25 self.cache = unittest.mock.create_autospec(apt.AptCache)
26 self.connection = unittest.mock.Mock()26 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
27 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
2728
28 def test_app_not_found_causes_error(self):29 def test_app_not_found_causes_error(self):
29 self.called_with = None30 self.called_with = None
30 def callback(t):31 def callback(t):
31 self.called_with = t32 self.called_with = t
3233
33 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:34 self.cache.app_info.return_value = {}
34 progress = MockProgress.return_value35 task = tasks.AppInfoTask('palpatine', self.cache, 'lightside', [1, 2], self.config, self.monitor, callback)
35 self.cache.app_info.return_value = {}36 task._instant_callback = True
36 task = tasks.AppInfoTask('palpatine', self.cache, 'lightside', [1, 2], self.config, self.connection, callback)37 task.start().join()
37 task._instant_callback = True38
38 task.start().join()39 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Could not find app info for \'lightside\' in container \'palpatine\'')
3940
40 progress.error.assert_called_once_with('Could not find app info for \'lightside\' in container \'palpatine\'')41 self.assertEqual(task, self.called_with)
41
42 self.assertEqual(task, self.called_with)
4342
44 def test_success_sends_data(self):43 def test_success_sends_data(self):
45 self.called_with = None44 self.called_with = None
46 def callback(t):45 def callback(t):
47 self.called_with = t46 self.called_with = t
4847
49 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:48 self.monitor.done.return_value = False
50 progress = MockProgress.return_value49
51 progress.done = False50 self.cache.app_info.return_value = {'package': 'darkside-common'}
5251 self.config.get_package_install_status.return_value = 'installed'
53 self.cache.app_info.return_value = {'package': 'darkside-common'}52 task = tasks.AppInfoTask('palpatine', self.cache, 'darkside', [1, 2, 3], self.config, self.monitor, callback)
54 self.config.get_package_install_status.return_value = 'installed'53 task._instant_callback = True
55 task = tasks.AppInfoTask('palpatine', self.cache, 'darkside', [1, 2, 3], self.config, self.connection, callback)54 task.start().join()
56 task._instant_callback = True55
57 task.start().join()56 self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, str({'package': 'darkside-common', 'status': 'installed', 'task_ids': [1, 2, 3]}))
5857 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
59 progress.data.assert_called_once_with(str({'package': 'darkside-common', 'status': 'installed', 'task_ids': [1, 2, 3]}))58
6059 self.assertEqual(task, self.called_with)
61 self.assertEqual(task, self.called_with)
6260
=== modified file 'tests/unit/service/tasks/test_container_info_task.py'
--- tests/unit/service/tasks/test_container_info_task.py 2017-01-27 16:42:22 +0000
+++ tests/unit/service/tasks/test_container_info_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -17,40 +17,39 @@
17import unittest.mock17import unittest.mock
18from unittest import TestCase18from unittest import TestCase
19from libertine import utils19from libertine import utils
20from libertine.service import tasks20from libertine.service import tasks, operations_monitor
21from libertine.ContainersConfig import ContainersConfig21from libertine.ContainersConfig import ContainersConfig
2222
2323
24class TestContainerInfoTask(TestCase):24class TestContainerInfoTask(TestCase):
25 def setUp(self):25 def setUp(self):
26 self.config = unittest.mock.create_autospec(ContainersConfig)26 self.config = unittest.mock.create_autospec(ContainersConfig)
27 self.connection = unittest.mock.Mock()27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
2829
29 def test_success_sends_data(self):30 def test_success_sends_data(self):
30 self.called_with = None31 self.called_with = None
31 def callback(t):32 def callback(t):
32 self.called_with = t33 self.called_with = t
3334
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:35 self.monitor.done.return_value = False
35 progress = MockProgress.return_value36
36 progress.done = False37 self.config.get_container_install_status.return_value = 'ready'
3738 self.config.get_container_name.return_value = 'Palpatine'
38 self.config.get_container_install_status.return_value = 'ready'39 task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.monitor, callback)
39 self.config.get_container_name.return_value = 'Palpatine'40 task._instant_callback = True
40 task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.connection, callback)41 task.start().join()
41 task._instant_callback = True42
42 task.start().join()43 self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, unittest.mock.ANY)
4344 args, kwargs = self.monitor.data.call_args
44 progress.data.assert_called_once_with(unittest.mock.ANY)45 self.assertEqual({'id': 'palpatine',
45 args, kwargs = progress.data.call_args46 'status': 'ready',
46 self.assertEqual({'id': 'palpatine',47 'task_ids': [1, 2, 3],
47 'status': 'ready',48 'name': 'Palpatine',
48 'task_ids': [1, 2, 3],49 'root': utils.get_libertine_container_rootfs_path('palpatine'),
49 'name': 'Palpatine',50 'home': utils.get_libertine_container_home_dir('palpatine')},
50 'root': utils.get_libertine_container_rootfs_path('palpatine'),51 ast.literal_eval(args[1]))
51 'home': utils.get_libertine_container_home_dir('palpatine')},52
52 ast.literal_eval(args[0]))53 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
5354
54 progress.finished.assert_called_once_with('palpatine')55 self.assertEqual(task, self.called_with)
55
56 self.assertEqual(task, self.called_with)
5756
=== modified file 'tests/unit/service/tasks/test_create_task.py'
--- tests/unit/service/tasks/test_create_task.py 2016-11-07 18:51:17 +0000
+++ tests/unit/service/tasks/test_create_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,15 +15,18 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks18from libertine.service import tasks, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
22class TestCreateTask(TestCase):22class TestCreateTask(TestCase):
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.connection = unittest.mock.Mock()25 self.lock = unittest.mock.MagicMock()
26 self.lock = unittest.mock.MagicMock()26 self.client = unittest.mock.Mock()
27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28
29 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
27 self.called_with = None30 self.called_with = None
2831
29 def callback(self, task):32 def callback(self, task):
@@ -31,204 +34,195 @@
3134
32 def test_success_creates_lxc_container(self):35 def test_success_creates_lxc_container(self):
33 self.config.container_exists.return_value = False36 self.config.container_exists.return_value = False
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:37 self.monitor.done.return_value = False
35 progress = MockProgress.return_value38 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
36 progress.done = False39 self.config, self.lock, self.monitor, self.client, self.callback)
37 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)40 task._instant_callback = True
38 task._instant_callback = True41
3942 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
40 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:43 MockHostInfo.return_value.is_distro_valid.return_value = True
41 MockHostInfo.return_value.is_distro_valid.return_value = True44 MockHostInfo.return_value.has_lxc_support.return_value = True
42 MockHostInfo.return_value.has_lxc_support.return_value = True45
4346 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
44 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:47 MockContainer.return_value.create_libertine_container.return_value = True
45 MockContainer.return_value.create_libertine_container.return_value = True48 task.start().join()
46 task.start().join()49
4750 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
48 progress.finished.assert_called_once_with('palpatine')51 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
49 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')52 self.config.update_container_install_status.assert_has_calls([
50 self.config.update_container_install_status.assert_has_calls([53 unittest.mock.call('palpatine', 'installing'),
51 unittest.mock.call('palpatine', 'installing'),54 unittest.mock.call('palpatine', 'ready')
52 unittest.mock.call('palpatine', 'ready')55 ], any_order=True)
53 ], any_order=True)56 self.assertEqual(task, self.called_with)
54 self.assertEqual(task, self.called_with)
5557
56 def test_success_creates_chroot_container(self):58 def test_success_creates_chroot_container(self):
57 self.config.container_exists.return_value = False59 self.config.container_exists.return_value = False
58 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:60 self.monitor.done.return_value = False
59 progress = MockProgress.return_value61 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', False,
60 progress.done = False62 self.config, self.lock, self.monitor, self.client, self.callback)
61 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback)63 task._instant_callback = True
62 task._instant_callback = True64
6365 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
64 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:66 MockHostInfo.return_value.is_distro_valid.return_value = True
65 MockHostInfo.return_value.is_distro_valid.return_value = True67
6668 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
67 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:69 MockContainer.return_value.create_libertine_container.return_value = True
68 MockContainer.return_value.create_libertine_container.return_value = True70 task.start().join()
69 task.start().join()71
7072 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
71 progress.finished.assert_called_once_with('palpatine')73 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
72 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')74 self.config.update_container_install_status.assert_has_calls([
73 self.config.update_container_install_status.assert_has_calls([75 unittest.mock.call('palpatine', 'installing'),
74 unittest.mock.call('palpatine', 'installing'),76 unittest.mock.call('palpatine', 'ready')
75 unittest.mock.call('palpatine', 'ready')77 ], any_order=True)
76 ], any_order=True)78 self.assertEqual(task, self.called_with)
77 self.assertEqual(task, self.called_with)
7879
79 def test_container_runtime_error_sends_error(self):80 def test_container_runtime_error_sends_error(self):
80 self.config.container_exists.return_value = False81 self.config.container_exists.return_value = False
81 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:82 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
82 progress = MockProgress.return_value83 self.config, self.lock, self.monitor, self.client, self.callback)
83 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)84 task._instant_callback = True
84 task._instant_callback = True85
8586 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
86 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:87 MockHostInfo.return_value.is_distro_valid.return_value = True
87 MockHostInfo.return_value.is_distro_valid.return_value = True88 MockHostInfo.return_value.has_lxc_support.return_value = True
88 MockHostInfo.return_value.has_lxc_support.return_value = True89
8990 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
90 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:91 MockContainer.return_value.create_libertine_container.side_effect = RuntimeError('a great disturbance')
91 MockContainer.return_value.create_libertine_container.side_effect = RuntimeError('a great disturbance')92 task.start().join()
92 task.start().join()93
9394 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'a great disturbance')
94 progress.error.assert_called_once_with('a great disturbance')95
9596 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
96 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')97 self.config.update_container_install_status.assert_called_once_with('palpatine', 'installing')
97 self.config.update_container_install_status.assert_called_once_with('palpatine', 'installing')98 self.config.delete_container.assert_called_once_with('palpatine')
98 self.config.delete_container.assert_called_once_with('palpatine')99
99100 self.assertEqual(task, self.called_with)
100 self.assertEqual(task, self.called_with)
101101
102 def test_failed_container_exists_sends_error(self):102 def test_failed_container_exists_sends_error(self):
103 self.config.container_exists.return_value = True103 self.config.container_exists.return_value = True
104 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:104 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
105 progress = MockProgress.return_value105 self.config, self.lock, self.monitor, self.client, self.callback)
106 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)106 task._instant_callback = True
107 task._instant_callback = True107 task.start().join()
108 task.start().join()
109108
110 progress.error.assert_called_once_with('Container \'palpatine\' already exists')109 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' already exists')
111 self.assertEqual(task, self.called_with)110 self.assertEqual(task, self.called_with)
112111
113 def test_container_invalid_distro_error_sends_error(self):112 def test_container_invalid_distro_error_sends_error(self):
114 self.config.container_exists.return_value = False113 self.config.container_exists.return_value = False
115 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:114 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
116 progress = MockProgress.return_value115 MockHostInfo.return_value.is_distro_valid.return_value = False
117 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:116 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'vesty', 'lxc', False,
118 MockHostInfo.return_value.is_distro_valid.return_value = False117 self.config, self.lock, self.monitor, self.client, self.callback)
119 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'vesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)118 task._instant_callback = True
120 task._instant_callback = True119 task.start().join()
121 task.start().join()
122120
123 progress.error.assert_called_once_with('Invalid distro \'vesty\'.')121 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Invalid distro \'vesty\'.')
124 self.assertEqual(task, self.called_with)122 self.assertEqual(task, self.called_with)
125123
126 def test_container_improper_lxc_error_sends_error(self):124 def test_container_improper_lxc_error_sends_error(self):
127 self.config.container_exists.return_value = False125 self.config.container_exists.return_value = False
128 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:126 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
129 progress = MockProgress.return_value127 MockHostInfo.return_value.is_distro_valid.return_value = True
130 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:128 MockHostInfo.return_value.has_lxc_support.return_value = False
131 MockHostInfo.return_value.is_distro_valid.return_value = True129 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
132 MockHostInfo.return_value.has_lxc_support.return_value = False130 self.config, self.lock, self.monitor, self.client, self.callback)
133 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)131 task._instant_callback = True
134 task._instant_callback = True132 task.start().join()
135 task.start().join()
136133
137 progress.error.assert_called_once_with('System kernel does not support lxc type containers. Please either use chroot or leave empty.')134 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, \
138 self.assertEqual(task, self.called_with)135 'System kernel does not support lxc type containers. Please either use chroot or leave empty.')
136 self.assertEqual(task, self.called_with)
139137
140 def test_sets_generic_name_when_empty(self):138 def test_sets_generic_name_when_empty(self):
141 self.config.container_exists.return_value = False139 self.config.container_exists.return_value = False
142 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:140 self.monitor.done.return_value = False
143 progress = MockProgress.return_value141 task = tasks.CreateTask('palpatine', None, 'zesty', 'chroot', False, self.config,
144 progress.done = False142 self.lock, self.monitor, self.client, self.callback)
145 task = tasks.CreateTask('palpatine', None, 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback)143 task._instant_callback = True
146 task._instant_callback = True144
147145 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
148 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:146 MockHostInfo.return_value.is_distro_valid.return_value = True
149 MockHostInfo.return_value.is_distro_valid.return_value = True147 MockHostInfo.return_value.get_distro_codename.return_value = 'Zesty Zapus 17.04'
150 MockHostInfo.return_value.get_distro_codename.return_value = 'Zesty Zapus 17.04'148
151149 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
152 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:150 MockContainer.return_value.create_libertine_container.return_value = True
153 MockContainer.return_value.create_libertine_container.return_value = True151 task.start().join()
154 task.start().join()152
155153 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
156 progress.finished.assert_called_once_with('palpatine')154 self.config.add_new_container.assert_called_once_with('palpatine', 'Ubuntu \'Zesty Zapus 17.04\'', 'chroot', 'zesty')
157 self.config.add_new_container.assert_called_once_with('palpatine', 'Ubuntu \'Zesty Zapus 17.04\'', 'chroot', 'zesty')155 self.config.update_container_install_status.assert_has_calls([
158 self.config.update_container_install_status.assert_has_calls([156 unittest.mock.call('palpatine', 'installing'),
159 unittest.mock.call('palpatine', 'installing'),157 unittest.mock.call('palpatine', 'ready')
160 unittest.mock.call('palpatine', 'ready')158 ], any_order=True)
161 ], any_order=True)159 self.assertEqual(task, self.called_with)
162 self.assertEqual(task, self.called_with)
163160
164 def test_sets_multiarch(self):161 def test_sets_multiarch(self):
165 self.config.container_exists.return_value = False162 self.config.container_exists.return_value = False
166 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:163 self.monitor.done.return_value = False
167 progress = MockProgress.return_value164 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', True,
168 progress.done = False165 self.config, self.lock, self.monitor, self.client, self.callback)
169 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', True, self.config, self.lock, self.connection, self.callback)166 task._instant_callback = True
170 task._instant_callback = True167
171168 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
172 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:169 MockHostInfo.return_value.is_distro_valid.return_value = True
173 MockHostInfo.return_value.is_distro_valid.return_value = True170
174171 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
175 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:172 MockContainer.return_value.create_libertine_container.return_value = True
176 MockContainer.return_value.create_libertine_container.return_value = True173 task.start().join()
177 task.start().join()174
178175 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
179 progress.finished.assert_called_once_with('palpatine')176 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
180 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')177 self.config.update_container_multiarch_support.assert_called_once_with('palpatine', 'enabled')
181 self.config.update_container_multiarch_support.assert_called_once_with('palpatine', 'enabled')178 self.config.update_container_install_status.assert_has_calls([
182 self.config.update_container_install_status.assert_has_calls([179 unittest.mock.call('palpatine', 'installing'),
183 unittest.mock.call('palpatine', 'installing'),180 unittest.mock.call('palpatine', 'ready')
184 unittest.mock.call('palpatine', 'ready')181 ], any_order=True)
185 ], any_order=True)182 self.assertEqual(task, self.called_with)
186 self.assertEqual(task, self.called_with)
187183
188 def test_sets_default_type(self):184 def test_sets_default_type(self):
189 self.config.container_exists.return_value = False185 self.config.container_exists.return_value = False
190 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:186 self.monitor.done.return_value = False
191 progress = MockProgress.return_value187 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', None, False,
192 progress.done = False188 self.config, self.lock, self.monitor, self.client, self.callback)
193 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', None, False, self.config, self.lock, self.connection, self.callback)189 task._instant_callback = True
194 task._instant_callback = True190
195191 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
196 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:192 MockHostInfo.return_value.is_distro_valid.return_value = True
197 MockHostInfo.return_value.is_distro_valid.return_value = True193 MockHostInfo.return_value.select_container_type_by_kernel.return_value = 'lxc'
198 MockHostInfo.return_value.select_container_type_by_kernel.return_value = 'lxc'194
199195 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
200 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:196 MockContainer.return_value.create_libertine_container.return_value = True
201 MockContainer.return_value.create_libertine_container.return_value = True197 task.start().join()
202 task.start().join()198
203199 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
204 progress.finished.assert_called_once_with('palpatine')200 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
205 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')201 self.config.update_container_install_status.assert_has_calls([
206 self.config.update_container_install_status.assert_has_calls([202 unittest.mock.call('palpatine', 'installing'),
207 unittest.mock.call('palpatine', 'installing'),203 unittest.mock.call('palpatine', 'ready')
208 unittest.mock.call('palpatine', 'ready')204 ], any_order=True)
209 ], any_order=True)205 self.assertEqual(task, self.called_with)
210 self.assertEqual(task, self.called_with)
211206
212 def test_sets_default_distro(self):207 def test_sets_default_distro(self):
213 self.config.container_exists.return_value = False208 self.config.container_exists.return_value = False
214 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:209 self.monitor.done.return_value = False
215 progress = MockProgress.return_value210 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', None, 'lxc', False,
216 progress.done = False211 self.config, self.lock, self.monitor, self.client, self.callback)
217 task = tasks.CreateTask('palpatine', 'Emperor Palpatine', None, 'lxc', False, self.config, self.lock, self.connection, self.callback)212 task._instant_callback = True
218 task._instant_callback = True213
219214 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
220 with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:215 MockHostInfo.return_value.has_lxc_support.return_value = True
221 MockHostInfo.return_value.has_lxc_support.return_value = True216 MockHostInfo.return_value.get_host_distro_release.return_value = 'zesty'
222 MockHostInfo.return_value.get_host_distro_release.return_value = 'zesty'217
223218 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
224 with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:219 MockContainer.return_value.create_libertine_container.return_value = True
225 MockContainer.return_value.create_libertine_container.return_value = True220 task.start().join()
226 task.start().join()221
227222 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
228 progress.finished.assert_called_once_with('palpatine')223 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
229 self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')224 self.config.update_container_install_status.assert_has_calls([
230 self.config.update_container_install_status.assert_has_calls([225 unittest.mock.call('palpatine', 'installing'),
231 unittest.mock.call('palpatine', 'installing'),226 unittest.mock.call('palpatine', 'ready')
232 unittest.mock.call('palpatine', 'ready')227 ], any_order=True)
233 ], any_order=True)228 self.assertEqual(task, self.called_with)
234 self.assertEqual(task, self.called_with)
235229
=== modified file 'tests/unit/service/tasks/test_destroy_task.py'
--- tests/unit/service/tasks/test_destroy_task.py 2016-11-07 18:51:17 +0000
+++ tests/unit/service/tasks/test_destroy_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,15 +15,18 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks18from libertine.service import tasks, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
22class TestDestroyTask(TestCase):22class TestDestroyTask(TestCase):
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.connection = unittest.mock.Mock()25 self.lock = unittest.mock.MagicMock()
26 self.lock = unittest.mock.MagicMock()26 self.client = unittest.mock.Mock()
27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28
29 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
27 self.called_with = None30 self.called_with = None
2831
29 def callback(self, task):32 def callback(self, task):
@@ -31,50 +34,45 @@
3134
32 def test_sends_error_on_non_ready_container(self):35 def test_sends_error_on_non_ready_container(self):
33 self.config._get_value_by_key.return_value = ''36 self.config._get_value_by_key.return_value = ''
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:37 self.monitor.done.return_value = False
35 progress = MockProgress.return_value38 task = tasks.DestroyTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
36 progress.done = False39 task._instant_callback = True
37 task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)40
38 task._instant_callback = True41 with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3942 MockContainer.return_value.destroy_libertine_container.return_value = True
40 with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:43 task.start().join()
41 MockContainer.return_value.destroy_libertine_container.return_value = True44
42 task.start().join()45 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' does not exist')
4346 self.assertEqual(task, self.called_with)
44 progress.error.assert_called_once_with('Container \'palpatine\' does not exist')
45 self.assertEqual(task, self.called_with)
4647
47 def test_sends_error_on_failed_destroy(self):48 def test_sends_error_on_failed_destroy(self):
48 self.config._get_value_by_key.return_value = 'ready'49 self.config._get_value_by_key.return_value = 'ready'
49 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:50 task = tasks.DestroyTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
50 progress = MockProgress.return_value51 task._instant_callback = True
51 task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)52
52 task._instant_callback = True53 with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
5354 MockContainer.return_value.destroy_libertine_container.return_value = False
54 with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:55 task.start().join()
55 MockContainer.return_value.destroy_libertine_container.return_value = False56
56 task.start().join()57 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Destroying container \'palpatine\' failed')
5758 self.config.update_container_install_status.assert_has_calls([
58 progress.error.assert_called_once_with('Destroying container \'palpatine\' failed')59 unittest.mock.call('palpatine', 'removing'),
59 self.config.update_container_install_status.assert_has_calls([60 unittest.mock.call('palpatine', 'ready')
60 unittest.mock.call('palpatine', 'removing'),61 ], any_order=True)
61 unittest.mock.call('palpatine', 'ready')62
62 ], any_order=True)63 self.assertEqual(task, self.called_with)
63 self.assertEqual(task, self.called_with)
6464
65 def test_successfully_destroys(self):65 def test_successfully_destroys(self):
66 self.config._get_value_by_key.return_value = 'ready'66 self.config._get_value_by_key.return_value = 'ready'
67 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:67 self.monitor.done.return_value = False
68 progress = MockProgress.return_value68 task = tasks.DestroyTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
69 progress.done = False69 task._instant_callback = True
70 task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)70
71 task._instant_callback = True71 with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
7272 MockContainer.return_value.destroy_libertine_container.return_value = True
73 with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:73 task.start().join()
74 MockContainer.return_value.destroy_libertine_container.return_value = True74
75 task.start().join()75 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
7676 self.config.update_container_install_status.assert_called_once_with('palpatine', 'removing')
77 progress.finished.assert_called_once_with('palpatine')77 self.config.delete_container.assert_called_once_with('palpatine')
78 self.config.update_container_install_status.assert_called_once_with('palpatine', 'removing')78 self.assertEqual(task, self.called_with)
79 self.config.delete_container.assert_called_once_with('palpatine')
80 self.assertEqual(task, self.called_with)
8179
=== modified file 'tests/unit/service/tasks/test_install_task.py'
--- tests/unit/service/tasks/test_install_task.py 2016-11-08 15:20:20 +0000
+++ tests/unit/service/tasks/test_install_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,15 +15,18 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks18from libertine.service import tasks, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
22class TestInstallTask(TestCase):22class TestInstallTask(TestCase):
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.connection = unittest.mock.Mock()25 self.lock = unittest.mock.MagicMock()
26 self.lock = unittest.mock.MagicMock()26 self.client = unittest.mock.Mock()
27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28
29 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
27 self.called_with = None30 self.called_with = None
2831
29 def callback(self, task):32 def callback(self, task):
@@ -31,46 +34,40 @@
3134
32 def test_sends_error_on_existing_package(self):35 def test_sends_error_on_existing_package(self):
33 self.config.package_exists.return_value = True36 self.config.package_exists.return_value = True
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:37 task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
35 progress = MockProgress.return_value38 task._instant_callback = True
36 task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)39 task.start().join()
37 task._instant_callback = True
38 task.start().join()
3940
40 progress.error.assert_called_once_with('Package \'darkside-common\' already exists, skipping install')41 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Package \'darkside-common\' already exists, skipping install')
41 self.assertEqual(task, self.called_with)42 self.assertEqual(task, self.called_with)
4243
43 def test_sends_error_on_failed_install(self):44 def test_sends_error_on_failed_install(self):
44 self.config.package_exists.return_value = False45 self.config.package_exists.return_value = False
45 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:46 task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
46 progress = MockProgress.return_value47 task._instant_callback = True
47 task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)48
48 task._instant_callback = True49 with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
4950 MockContainer.return_value.install_package.return_value = False
50 with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:51 task.start().join()
51 MockContainer.return_value.install_package.return_value = False52
52 task.start().join()53 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, "Package installation failed for 'darkside-common'")
5354 self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'installing')
54 progress.error.assert_called_once_with("Package installation failed for 'darkside-common'")55 self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
55 self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'installing')56 self.assertEqual(task, self.called_with)
56 self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
57 self.assertEqual(task, self.called_with)
5857
59 def test_successfully_install(self):58 def test_successfully_install(self):
60 self.config.package_exists.return_value = False59 self.config.package_exists.return_value = False
61 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:60 self.monitor.done.return_value = False
62 progress = MockProgress.return_value61 task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
63 progress.done = False62 task._instant_callback = True
64 task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)63
65 task._instant_callback = True64 with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
6665 MockContainer.return_value.install_package.return_value = True
67 with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:66 task.start().join()
68 MockContainer.return_value.install_package.return_value = True67
69 task.start().join()68 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
7069 self.config.update_package_install_status.assert_has_calls([
71 progress.finished.assert_called_once_with('palpatine')70 unittest.mock.call('palpatine', 'darkside-common', 'installing'),
72 self.config.update_package_install_status.assert_has_calls([71 unittest.mock.call('palpatine', 'darkside-common', 'installed')
73 unittest.mock.call('palpatine', 'darkside-common', 'installing'),72 ], any_order=True)
74 unittest.mock.call('palpatine', 'darkside-common', 'installed')73 self.assertEqual(task, self.called_with)
75 ], any_order=True)
76 self.assertEqual(task, self.called_with)
7774
=== modified file 'tests/unit/service/tasks/test_list_app_ids_task.py'
--- tests/unit/service/tasks/test_list_app_ids_task.py 2017-02-07 12:35:48 +0000
+++ tests/unit/service/tasks/test_list_app_ids_task.py 2017-03-31 20:05:48 +0000
@@ -15,15 +15,17 @@
15import json15import json
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks18from libertine.service import tasks, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
22class TestListAppIdsTask(TestCase):22class TestListAppIdsTask(TestCase):
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.connection = unittest.mock.Mock()
26 self.lock = unittest.mock.MagicMock()25 self.lock = unittest.mock.MagicMock()
26 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
27
28 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
27 self.called_with = None29 self.called_with = None
2830
29 def callback(self, task):31 def callback(self, task):
@@ -31,29 +33,25 @@
3133
32 def test_sends_error_on_non_existent_container(self):34 def test_sends_error_on_non_existent_container(self):
33 self.config.container_exists.return_value = False35 self.config.container_exists.return_value = False
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:36 task = tasks.ListAppIdsTask('palpatine', self.config, self.monitor, self.callback)
35 progress = MockProgress.return_value37 task._instant_callback = True
36 task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback)38
37 task._instant_callback = True39 with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3840 task.start().join()
39 with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:41
40 task.start().join()42 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' does not exist, skipping list')
4143 self.assertEqual(task, self.called_with)
42 progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list')
43 self.assertEqual(task, self.called_with)
4444
45 def test_successfully_lists_apps(self):45 def test_successfully_lists_apps(self):
46 self.config.container_exists.return_value = True46 self.config.container_exists.return_value = True
47 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:47 self.monitor.done.return_value = False
48 progress = MockProgress.return_value48 task = tasks.ListAppIdsTask('palpatine', self.config, self.monitor, self.callback)
49 progress.done = False49 task._instant_callback = True
50 task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback)50
51 task._instant_callback = True51 with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
5252 MockContainer.return_value.list_app_ids.return_value = '["palpatine_gedit_0.0","palpatine_xterm_0.0"]'
53 with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:53 task.start().join()
54 MockContainer.return_value.list_app_ids.return_value = '["palpatine_gedit_0.0","palpatine_xterm_0.0"]'54
55 task.start().join()55 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
5656 self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, json.dumps('["palpatine_gedit_0.0","palpatine_xterm_0.0"]'))
57 progress.finished.assert_called_once_with('palpatine')57 self.assertEqual(task, self.called_with)
58 progress.data.assert_called_once_with(json.dumps('["palpatine_gedit_0.0","palpatine_xterm_0.0"]'))
59 self.assertEqual(task, self.called_with)
6058
=== modified file 'tests/unit/service/tasks/test_list_task.py'
--- tests/unit/service/tasks/test_list_task.py 2017-01-24 18:00:57 +0000
+++ tests/unit/service/tasks/test_list_task.py 2017-03-31 20:05:48 +0000
@@ -17,31 +17,30 @@
17import unittest.mock17import unittest.mock
1818
19from unittest import TestCase19from unittest import TestCase
20from libertine.service import tasks20from libertine.service import tasks, operations_monitor
21from libertine.ContainersConfig import ContainersConfig21from libertine.ContainersConfig import ContainersConfig
2222
2323
24class TestListTask(TestCase):24class TestListTask(TestCase):
25 def setUp(self):25 def setUp(self):
26 self.config = unittest.mock.create_autospec(ContainersConfig)26 self.config = unittest.mock.create_autospec(ContainersConfig)
27 self.connection = unittest.mock.Mock()27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
2829
29 def test_success_sends_data(self):30 def test_success_sends_data(self):
30 self.called_with = None31 self.called_with = None
31 def callback(t):32 def callback(t):
32 self.called_with = t33 self.called_with = t
3334
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:35 self.monitor.done.return_value = False
35 progress = MockProgress.return_value36
36 progress.done = False37 task = tasks.ListTask(self.config, self.monitor, callback)
3738 task._instant_callback = True
38 task = tasks.ListTask(self.config, self.connection, callback)39
39 task._instant_callback = True40 self.config.get_containers.return_value = ['palatine', 'vader', 'maul']
4041 task.start().join()
41 self.config.get_containers.return_value = ['palatine', 'vader', 'maul']42
42 task.start().join()43 self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, json.dumps(['palatine', 'vader', 'maul']))
4344 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
44 progress.data.assert_called_once_with(json.dumps(['palatine', 'vader', 'maul']))45
45 progress.finished.assert_called_once_with('')46 self.assertEqual(task, self.called_with)
46
47 self.assertEqual(task, self.called_with)
4847
=== modified file 'tests/unit/service/tasks/test_remove_task.py'
--- tests/unit/service/tasks/test_remove_task.py 2016-11-08 15:20:20 +0000
+++ tests/unit/service/tasks/test_remove_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,15 +15,18 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks18from libertine.service import tasks, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
22class TestRemoveTask(TestCase):22class TestRemoveTask(TestCase):
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.connection = unittest.mock.Mock()25 self.lock = unittest.mock.MagicMock()
26 self.lock = unittest.mock.MagicMock()26 self.client = unittest.mock.Mock()
27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28
29 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
27 self.called_with = None30 self.called_with = None
2831
29 def callback(self, task):32 def callback(self, task):
@@ -31,46 +34,40 @@
3134
32 def test_sends_error_on_non_installed_package(self):35 def test_sends_error_on_non_installed_package(self):
33 self.config.get_package_install_status.return_value = 'installing'36 self.config.get_package_install_status.return_value = 'installing'
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:37 task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
35 progress = MockProgress.return_value38 task._instant_callback = True
36 task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)39 task.start().join()
37 task._instant_callback = True
38 task.start().join()
3940
40 progress.error.assert_called_once_with('Package \'darkside-common\' not installed, skipping remove')41 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Package \'darkside-common\' not installed, skipping remove')
41 self.assertEqual(task, self.called_with)42 self.assertEqual(task, self.called_with)
4243
43 def test_sends_error_on_failed_install(self):44 def test_sends_error_on_failed_install(self):
44 self.config.get_package_install_status.return_value = 'installed'45 self.config.get_package_install_status.return_value = 'installed'
45 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:46 task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
46 progress = MockProgress.return_value47 task._instant_callback = True
47 task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)48
48 task._instant_callback = True49 with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
4950 MockContainer.return_value.remove_package.return_value = False
50 with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:51 task.start().join()
51 MockContainer.return_value.remove_package.return_value = False52
52 task.start().join()53 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, "Package removal failed for 'darkside-common'")
5354 self.config.update_package_install_status.assert_has_calls([
54 progress.error.assert_called_once_with("Package removal failed for 'darkside-common'")55 unittest.mock.call('palpatine', 'darkside-common', 'removing'),
55 self.config.update_package_install_status.assert_has_calls([56 unittest.mock.call('palpatine', 'darkside-common', 'installed')
56 unittest.mock.call('palpatine', 'darkside-common', 'removing'),57 ], any_order=True)
57 unittest.mock.call('palpatine', 'darkside-common', 'installed')58 self.assertEqual(task, self.called_with)
58 ], any_order=True)
59 self.assertEqual(task, self.called_with)
6059
61 def test_successfully_install(self):60 def test_successfully_install(self):
62 self.config.get_package_install_status.return_value = 'installed'61 self.config.get_package_install_status.return_value = 'installed'
63 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:62 self.monitor.done.return_value = False
64 progress = MockProgress.return_value63 task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
65 progress.done = False64 task._instant_callback = True
66 task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)65
67 task._instant_callback = True66 with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
6867 MockContainer.return_value.remove_package.return_value = True
69 with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:68 task.start().join()
70 MockContainer.return_value.remove_package.return_value = True69
71 task.start().join()70 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
7271 self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'removing')
73 progress.finished.assert_called_once_with('palpatine')72 self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
74 self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'removing')73 self.assertEqual(task, self.called_with)
75 self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
76 self.assertEqual(task, self.called_with)
7774
=== modified file 'tests/unit/service/tasks/test_search_task.py'
--- tests/unit/service/tasks/test_search_task.py 2016-11-07 18:51:17 +0000
+++ tests/unit/service/tasks/test_search_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,29 +15,29 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks, apt18from libertine.service import tasks, apt, operations_monitor
1919
2020
21class TestSearchTask(TestCase):21class TestSearchTask(TestCase):
22 def setUp(self):22 def setUp(self):
23 self.connection = unittest.mock.Mock()
24 self.lock = unittest.mock.MagicMock()23 self.lock = unittest.mock.MagicMock()
25 self.cache = unittest.mock.create_autospec(apt.AptCache)24 self.cache = unittest.mock.create_autospec(apt.AptCache)
25 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
26
27 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
26 self.called_with = None28 self.called_with = None
2729
28 def callback(self, task):30 def callback(self, task):
29 self.called_with = task31 self.called_with = task
3032
31 def test_successfully_lists_apps(self):33 def test_successfully_lists_apps(self):
32 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:34 self.monitor.done.return_value = False
33 progress = MockProgress.return_value35 task = tasks.SearchTask('palpatine', self.cache, 'jarjar', self.monitor, self.callback)
34 progress.done = False
35 task = tasks.SearchTask('palpatine', self.cache, 'jarjar', self.connection, self.callback)
36 task._instant_callback = True36 task._instant_callback = True
3737
38 self.cache.search.return_value = ['jarjar', 'sidius']38 self.cache.search.return_value = ['jarjar', 'sidius']
39 task.start().join()39 task.start().join()
4040
41 progress.finished.assert_called_once_with('palpatine')41 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
42 progress.data.assert_called_once_with(str(['jarjar', 'sidius']))42 self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, str(['jarjar', 'sidius']))
43 self.assertEqual(task, self.called_with)43 self.assertEqual(task, self.called_with)
4444
=== modified file 'tests/unit/service/tasks/test_update_task.py'
--- tests/unit/service/tasks/test_update_task.py 2016-11-08 15:20:20 +0000
+++ tests/unit/service/tasks/test_update_task.py 2017-03-31 20:05:48 +0000
@@ -1,4 +1,4 @@
1# Copyright 2016 Canonical Ltd.1# Copyright 2016-2017 Canonical Ltd.
2#2#
3# This program is free software: you can redistribute it and/or modify it3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published4# under the terms of the GNU General Public License version 3, as published
@@ -15,15 +15,18 @@
1515
16import unittest.mock16import unittest.mock
17from unittest import TestCase17from unittest import TestCase
18from libertine.service import tasks18from libertine.service import tasks, operations_monitor
19from libertine.ContainersConfig import ContainersConfig19from libertine.ContainersConfig import ContainersConfig
2020
2121
22class TestUpdateTask(TestCase):22class TestUpdateTask(TestCase):
23 def setUp(self):23 def setUp(self):
24 self.config = unittest.mock.create_autospec(ContainersConfig)24 self.config = unittest.mock.create_autospec(ContainersConfig)
25 self.connection = unittest.mock.Mock()25 self.lock = unittest.mock.MagicMock()
26 self.lock = unittest.mock.MagicMock()26 self.client = unittest.mock.Mock()
27 self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
28
29 self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
27 self.called_with = None30 self.called_with = None
2831
29 def callback(self, task):32 def callback(self, task):
@@ -31,50 +34,47 @@
3134
32 def test_sends_error_on_non_existent_container(self):35 def test_sends_error_on_non_existent_container(self):
33 self.config.container_exists.return_value = False36 self.config.container_exists.return_value = False
34 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:37 task = tasks.UpdateTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
35 progress = MockProgress.return_value38 task._instant_callback = True
36 task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)39
37 task._instant_callback = True40 with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3841 task.start().join()
39 with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:42
40 task.start().join()43 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' does not exist, skipping update')
4144
42 progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping update')45 self.assertEqual(task, self.called_with)
43 self.assertEqual(task, self.called_with)
4446
45 def test_sends_error_on_failed_update(self):47 def test_sends_error_on_failed_update(self):
46 self.config.container_exists.return_value = True48 self.config.container_exists.return_value = True
47 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:49 task = tasks.UpdateTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
48 progress = MockProgress.return_value50 task._instant_callback = True
49 task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)51
50 task._instant_callback = True52 with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
5153 MockContainer.return_value.update_libertine_container.return_value = False
52 with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:54 task.start().join()
53 MockContainer.return_value.update_libertine_container.return_value = False55
54 task.start().join()56 self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Failed to update container \'palpatine\'')
5557 self.config.update_container_install_status.assert_has_calls([
56 progress.error.assert_called_once_with('Failed to update container \'palpatine\'')58 unittest.mock.call('palpatine', 'updating'),
57 self.config.update_container_install_status.assert_has_calls([59 unittest.mock.call('palpatine', 'ready')
58 unittest.mock.call('palpatine', 'updating'),60 ], any_order=True)
59 unittest.mock.call('palpatine', 'ready')61
60 ], any_order=True)62 self.assertEqual(task, self.called_with)
61 self.assertEqual(task, self.called_with)
6263
63 def test_successfully_updates(self):64 def test_successfully_updates(self):
64 self.config.container_exists.return_value = True65 self.config.container_exists.return_value = True
65 with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:66 self.monitor.done.return_value = False
66 progress = MockProgress.return_value67 task = tasks.UpdateTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
67 progress.done = False68 task._instant_callback = True
68 task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)69
69 task._instant_callback = True70 with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
7071 MockContainer.return_value.update_libertine_container.return_value = True
71 with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:72 task.start().join()
72 MockContainer.return_value.update_libertine_container.return_value = True73
73 task.start().join()74 self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
7475 self.config.update_container_install_status.assert_has_calls([
75 progress.finished.assert_called_once_with('palpatine')76 unittest.mock.call('palpatine', 'updating'),
76 self.config.update_container_install_status.assert_has_calls([77 unittest.mock.call('palpatine', 'ready')
77 unittest.mock.call('palpatine', 'updating'),78 ], any_order=True)
78 unittest.mock.call('palpatine', 'ready')79
79 ], any_order=True)80 self.assertEqual(task, self.called_with)
80 self.assertEqual(task, self.called_with)
8181
=== modified file 'tests/unit/service/test_container.py'
--- tests/unit/service/test_container.py 2017-02-07 12:35:48 +0000
+++ tests/unit/service/test_container.py 2017-03-31 20:05:48 +0000
@@ -20,31 +20,32 @@
2020
21class TestContainer(TestCase):21class TestContainer(TestCase):
22 def setUp(self):22 def setUp(self):
23 self._connection = unittest.mock.Mock()23 self._monitor = unittest.mock.Mock()
24 self._config = unittest.mock.Mock()24 self._config = unittest.mock.Mock()
25 self._client = unittest.mock.Mock()
2526
26 def test_search_creates_search_task(self):27 def test_search_creates_search_task(self):
27 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:28 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
28 cache = MockCache.return_value29 cache = MockCache.return_value
29 c = container.Container('palpatine', self._config, self._connection, lambda task: task)30 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
30 with unittest.mock.patch('libertine.service.container.SearchTask') as MockSearchTask:31 with unittest.mock.patch('libertine.service.container.SearchTask') as MockSearchTask:
31 c.search('darkseid')32 c.search('darkseid')
32 MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._connection, unittest.mock.ANY)33 MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._monitor, unittest.mock.ANY)
33 MockSearchTask.return_value.start.assert_called_once_with()34 MockSearchTask.return_value.start.assert_called_once_with()
3435
35 def test_app_info_creates_app_info_task(self):36 def test_app_info_creates_app_info_task(self):
36 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:37 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
37 cache = MockCache.return_value38 cache = MockCache.return_value
38 c = container.Container('palpatine', self._config, self._connection, lambda task: task)39 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
39 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:40 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
40 c.app_info('force')41 c.app_info('force')
41 MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._connection, unittest.mock.ANY)42 MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._monitor, unittest.mock.ANY)
42 MockAppInfoTask.return_value.start.assert_called_once_with()43 MockAppInfoTask.return_value.start.assert_called_once_with()
4344
44 def test_app_info_gets_related_task_info(self):45 def test_app_info_gets_related_task_info(self):
45 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:46 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
46 cache = MockCache.return_value47 cache = MockCache.return_value
47 c = container.Container('palpatine', self._config, self._connection, lambda task: task)48 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
48 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:49 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
49 MockInstallTask.return_value.package = 'darkside'50 MockInstallTask.return_value.package = 'darkside'
50 MockInstallTask.return_value.matches.return_value = False51 MockInstallTask.return_value.matches.return_value = False
@@ -55,111 +56,111 @@
55 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:56 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
56 c.app_info('darkside')57 c.app_info('darkside')
57 MockAppInfoTask.assert_called_once_with('palpatine', cache, 'darkside', [install_task_id, remove_task_id],58 MockAppInfoTask.assert_called_once_with('palpatine', cache, 'darkside', [install_task_id, remove_task_id],
58 self._config, self._connection, unittest.mock.ANY)59 self._config, self._monitor, unittest.mock.ANY)
5960
60 def test_install_creates_install_task(self):61 def test_install_creates_install_task(self):
61 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:62 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
62 c = container.Container('palpatine', self._config, self._connection, lambda task: task)63 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
63 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:64 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
64 c.install('force')65 c.install('force')
65 MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)66 MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
66 MockInstallTask.return_value.start.assert_called_once_with()67 MockInstallTask.return_value.start.assert_called_once_with()
6768
68 def test_install_only_calls_once_when_unfinished(self):69 def test_install_only_calls_once_when_unfinished(self):
69 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:70 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
70 c = container.Container('palpatine', self._config, self._connection, lambda task: task)71 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
71 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:72 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
72 c.install('darkside')73 c.install('darkside')
73 c.install('darkside')74 c.install('darkside')
74 c.install('darkside')75 c.install('darkside')
75 MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)76 MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
76 MockInstallTask.return_value.start.assert_called_once_with()77 MockInstallTask.return_value.start.assert_called_once_with()
7778
78 def test_remove_creates_remove_task(self):79 def test_remove_creates_remove_task(self):
79 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:80 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
80 c = container.Container('palpatine', self._config, self._connection, lambda task: task)81 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
81 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:82 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
82 c.remove('force')83 c.remove('force')
83 MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)84 MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
84 MockRemoveTask.return_value.start.assert_called_once_with()85 MockRemoveTask.return_value.start.assert_called_once_with()
8586
86 def test_remove_only_calls_once_when_unfinished(self):87 def test_remove_only_calls_once_when_unfinished(self):
87 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:88 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
88 c = container.Container('palpatine', self._config, self._connection, lambda task: task)89 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
89 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:90 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
90 c.remove('darkside')91 c.remove('darkside')
91 c.remove('darkside')92 c.remove('darkside')
92 c.remove('darkside')93 c.remove('darkside')
93 MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)94 MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
94 MockRemoveTask.return_value.start.assert_called_once_with()95 MockRemoveTask.return_value.start.assert_called_once_with()
9596
96 def test_create_creates_create_task(self):97 def test_create_creates_create_task(self):
97 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:98 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
98 c = container.Container('palpatine', self._config, self._connection, lambda task: task)99 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
99 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:100 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
100 c.create('Emperor Palpatine', 'zesty', 'lxd', False)101 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
101 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,102 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
102 self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)103 self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
103 MockCreateTask.return_value.start.assert_called_once_with()104 MockCreateTask.return_value.start.assert_called_once_with()
104105
105 def test_create_only_calls_once_when_unfinished(self):106 def test_create_only_calls_once_when_unfinished(self):
106 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:107 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
107 c = container.Container('palpatine', self._config, self._connection, lambda task: task)108 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
108 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:109 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
109 c.create('Emperor Palpatine', 'zesty', 'lxd', False)110 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
110 c.create('Emperor Palpatine', 'zesty', 'lxd', False)111 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
111 c.create('Emperor Palpatine', 'zesty', 'lxd', False)112 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
112 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,113 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
113 self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)114 self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
114 MockCreateTask.return_value.start.assert_called_once_with()115 MockCreateTask.return_value.start.assert_called_once_with()
115116
116 def test_destroy_creates_destroy_task(self):117 def test_destroy_creates_destroy_task(self):
117 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:118 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
118 c = container.Container('palpatine', self._config, self._connection, lambda task: task)119 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
119 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:120 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
120 c.destroy()121 c.destroy()
121 MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)122 MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
122 MockDestroyTask.return_value.start.assert_called_once_with()123 MockDestroyTask.return_value.start.assert_called_once_with()
123124
124 def test_destroy_only_calls_once_when_unfinished(self):125 def test_destroy_only_calls_once_when_unfinished(self):
125 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:126 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
126 c = container.Container('palpatine', self._config, self._connection, lambda task: task)127 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
127 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:128 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
128 c.destroy()129 c.destroy()
129 c.destroy()130 c.destroy()
130 c.destroy()131 c.destroy()
131 MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)132 MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
132 MockDestroyTask.return_value.start.assert_called_once_with()133 MockDestroyTask.return_value.start.assert_called_once_with()
133134
134 def test_update_creates_update_task(self):135 def test_update_creates_update_task(self):
135 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:136 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
136 c = container.Container('palpatine', self._config, self._connection, lambda task: task)137 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
137 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:138 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
138 c.update()139 c.update()
139 MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)140 MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
140 MockUpdateTask.return_value.start.assert_called_once_with()141 MockUpdateTask.return_value.start.assert_called_once_with()
141142
142 def test_update_only_calls_once_when_unfinished(self):143 def test_update_only_calls_once_when_unfinished(self):
143 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:144 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
144 c = container.Container('palpatine', self._config, self._connection, lambda task: task)145 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
145 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:146 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
146 c.update()147 c.update()
147 c.update()148 c.update()
148 c.update()149 c.update()
149 MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)150 MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
150 MockUpdateTask.return_value.start.assert_called_once_with()151 MockUpdateTask.return_value.start.assert_called_once_with()
151152
152 def test_list_app_ids_creates_list_app_ids_task(self):153 def test_list_app_ids_creates_list_app_ids_task(self):
153 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:154 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
154 c = container.Container('palpatine', self._config, self._connection, lambda task: task)155 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
155 with unittest.mock.patch('libertine.service.container.ListAppIdsTask') as MockListAppIdsTask:156 with unittest.mock.patch('libertine.service.container.ListAppIdsTask') as MockListAppIdsTask:
156 c.list_app_ids()157 c.list_app_ids()
157 MockListAppIdsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY)158 MockListAppIdsTask.assert_called_once_with('palpatine', self._config, self._monitor, self._client, unittest.mock.ANY)
158 MockListAppIdsTask.return_value.start.assert_called_once_with()159 MockListAppIdsTask.return_value.start.assert_called_once_with()
159160
160 def test_removes_task_during_callback(self):161 def test_removes_task_during_callback(self):
161 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:162 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
162 c = container.Container('palpatine', self._config, self._connection, lambda task: task)163 c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
163 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:164 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
164 MockInstallTask.return_value.package = 'force'165 MockInstallTask.return_value.package = 'force'
165 c.install('force')166 c.install('force')
@@ -176,7 +177,7 @@
176 self._container_id = None177 self._container_id = None
177 def callback(container):178 def callback(container):
178 self._container_id = container.id179 self._container_id = container.id
179 c = container.Container('palpatine', self._config, self._connection, callback)180 c = container.Container('palpatine', self._config, self._monitor, self._client, callback)
180 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:181 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
181 c.install('force')182 c.install('force')
182 name, args, kwargs = MockInstallTask.mock_calls[0]183 name, args, kwargs = MockInstallTask.mock_calls[0]
183184
=== added file 'tests/unit/service/test_operations_monitor.py'
--- tests/unit/service/test_operations_monitor.py 1970-01-01 00:00:00 +0000
+++ tests/unit/service/test_operations_monitor.py 2017-03-31 20:05:48 +0000
@@ -0,0 +1,146 @@
1# Copyright 2017 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published
5# by the Free Software Foundation.
6#
7# This program is distributed in the hope that it will be useful, but
8# WITHOUT ANY WARRANTY; without even the implied warranties of
9# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
10# PURPOSE. See the GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License along
13# with this program. If not, see <http://www.gnu.org/licenses/>.
14
15import unittest.mock
16from unittest import TestCase
17from libertine.service import operations_monitor, download
18
19
20class TestOperationsMonitor(TestCase):
21 def setUp(self):
22 self._connection = unittest.mock.Mock()
23
24 def test_new_operation_returns_some_id(self):
25 with unittest.mock.patch('dbus.service.Object'):
26 monitor = operations_monitor.OperationsMonitor(self._connection)
27 monitor._connection = self._connection
28
29 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
30 self.assertIsNotNone(monitor.new_operation())
31
32 def test_remove_connection(self):
33 with unittest.mock.patch('dbus.service.Object'):
34 monitor = operations_monitor.OperationsMonitor(self._connection)
35 monitor._connection = self._connection
36
37 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
38 monitor.remove_from_connection(monitor.new_operation())
39 MockDownload.return_value.remove_from_connection.assert_called_once_with()
40
41 def test_returns_done_for_operation(self):
42 with unittest.mock.patch('dbus.service.Object'):
43 monitor = operations_monitor.OperationsMonitor(self._connection)
44 monitor._connection = self._connection
45
46 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
47 MockDownload.return_value.done = True
48 self.assertTrue(monitor.done(monitor.new_operation()))
49
50 MockDownload.return_value.done = False
51 self.assertFalse(monitor.done(monitor.new_operation()))
52
53 # non-existent operation
54 self.assertFalse(monitor.done("123456"))
55
56 def test_running_returns_download_state(self):
57 with unittest.mock.patch('dbus.service.Object'):
58 monitor = operations_monitor.OperationsMonitor(self._connection)
59 monitor._connection = self._connection
60
61 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
62 MockDownload.return_value.done = True
63 self.assertFalse(monitor.running(monitor.new_operation()))
64
65 MockDownload.return_value.done = False
66 self.assertTrue(monitor.running(monitor.new_operation()))
67
68 # non-existent operation
69 self.assertFalse(monitor.running("123456"))
70
71 def test_result_returns_download_results(self):
72 with unittest.mock.patch('dbus.service.Object'):
73 monitor = operations_monitor.OperationsMonitor(self._connection)
74 monitor._connection = self._connection
75
76 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
77 MockDownload.return_value.result = "pokemongus"
78 self.assertEqual("pokemongus", monitor.result(monitor.new_operation()))
79
80 # non-existent operation
81 self.assertEqual("", monitor.result("123456"))
82
83 def test_last_error_returns_download_last_errors(self):
84 with unittest.mock.patch('dbus.service.Object'):
85 monitor = operations_monitor.OperationsMonitor(self._connection)
86 monitor._connection = self._connection
87
88 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
89 MockDownload.return_value.last_error = "pokemongus"
90 self.assertEqual("pokemongus", monitor.last_error(monitor.new_operation()))
91
92 # non-existent operation
93 self.assertEqual("", monitor.last_error("123456"))
94
95 def test_forwards_finished(self):
96 with unittest.mock.patch('dbus.service.Object'):
97 monitor = operations_monitor.OperationsMonitor(self._connection)
98 monitor._connection = self._connection
99 monitor._locations = []
100
101 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
102 path = monitor.new_operation()
103 monitor.finished(path)
104 MockDownload.return_value.finished.assert_called_once_with(path)
105
106 # test does not crash on empty
107 MockDownload.reset_mock()
108 monitor.finished("some/junk")
109 MockDownload.return_value.finished.assert_not_called()
110
111 def test_forwards_error(self):
112 with unittest.mock.patch('dbus.service.Object'):
113 monitor = operations_monitor.OperationsMonitor(self._connection)
114 monitor._connection = self._connection
115 monitor._locations = []
116
117 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
118 path = monitor.new_operation()
119 monitor.error(path, "something messed up")
120 MockDownload.return_value.error.assert_called_once_with("something messed up")
121
122 # test does not crash on empty
123 MockDownload.reset_mock()
124 monitor.error("some/junk", "something messed up")
125 MockDownload.return_value.error.assert_not_called()
126
127 def test_forwards_data(self):
128 with unittest.mock.patch('dbus.service.Object'):
129 monitor = operations_monitor.OperationsMonitor(self._connection)
130 monitor._connection = self._connection
131 monitor._locations = []
132
133 with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
134 path = monitor.new_operation()
135 monitor.data(path, "some of that gud data")
136 MockDownload.return_value.data.assert_called_once_with("some of that gud data")
137
138 # test does not crash on empty
139 MockDownload.reset_mock()
140 monitor.data("some/junk", "some of that gud data")
141 MockDownload.return_value.data.assert_not_called()
142
143
144
145if __name__ == '__main__':
146 unittest.main()
0147
=== modified file 'tests/unit/service/test_task_dispatcher.py'
--- tests/unit/service/test_task_dispatcher.py 2017-02-07 12:35:48 +0000
+++ tests/unit/service/test_task_dispatcher.py 2017-03-31 20:05:48 +0000
@@ -20,9 +20,10 @@
20class TestTaskDispatcher(TestCase):20class TestTaskDispatcher(TestCase):
21 def setUp(self):21 def setUp(self):
22 self._connection = unittest.mock.Mock()22 self._connection = unittest.mock.Mock()
23 self._client = unittest.mock.Mock()
23 self._config_patcher = unittest.mock.patch('libertine.service.task_dispatcher.libertine.ContainersConfig.ContainersConfig')24 self._config_patcher = unittest.mock.patch('libertine.service.task_dispatcher.libertine.ContainersConfig.ContainersConfig')
24 self._config_patcher.start()25 self._config_patcher.start()
25 self._dispatcher = task_dispatcher.TaskDispatcher(self._connection)26 self._dispatcher = task_dispatcher.TaskDispatcher(self._connection, self._client)
2627
27 def tearDown(self):28 def tearDown(self):
28 self._config_patcher.stop()29 self._config_patcher.stop()
@@ -90,20 +91,20 @@
90 self._dispatcher.list_app_ids('palpatine')91 self._dispatcher.list_app_ids('palpatine')
91 self._dispatcher.list_app_ids('palpatine')92 self._dispatcher.list_app_ids('palpatine')
92 self._dispatcher.list_app_ids('palpatine')93 self._dispatcher.list_app_ids('palpatine')
93 MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)94 MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY)
9495
95 def test_container_callback_removes_container(self):96 def test_container_callback_removes_container(self):
96 with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:97 with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
97 c = MockContainer.return_value98 c = MockContainer.return_value
98 c.id = 'palpatine'99 c.id = 'palpatine'
99 self._dispatcher.list_app_ids('palpatine')100 self._dispatcher.list_app_ids('palpatine')
100 MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)101 MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY)
101 name, args, kwargs = MockContainer.mock_calls[0]102 name, args, kwargs = MockContainer.mock_calls[0]
102 args[len(args)-1](MockContainer.return_value)103 args[len(args)-1](MockContainer.return_value)
103 self._dispatcher.list_app_ids('palpatine')104 self._dispatcher.list_app_ids('palpatine')
104 MockContainer.assert_has_calls([ # verify container constructed twice105 MockContainer.assert_has_calls([ # verify container constructed twice
105 unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY),106 unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY),
106 unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)107 unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY)
107 ], any_order=True)108 ], any_order=True)
108109
109 def test_container_info_creates_container_info_task(self):110 def test_container_info_creates_container_info_task(self):
110111
=== modified file 'tools/libertine-container-manager'
--- tools/libertine-container-manager 2017-03-03 18:34:34 +0000
+++ tools/libertine-container-manager 2017-03-31 20:05:48 +0000
@@ -24,6 +24,10 @@
24import sys24import sys
25import re25import re
2626
27import gettext
28gettext.textdomain('libertine')
29_ = gettext.gettext
30
27from libertine import ContainerRunning, LibertineContainer31from libertine import ContainerRunning, LibertineContainer
28from libertine.ContainersConfig import ContainersConfig32from libertine.ContainersConfig import ContainersConfig
29from libertine.HostInfo import HostInfo33from libertine.HostInfo import HostInfo
@@ -35,6 +39,16 @@
35 self.containers_config = ContainersConfig()39 self.containers_config = ContainersConfig()
36 self.host_info = HostInfo()40 self.host_info = HostInfo()
3741
42
43 def _container(self, container_id):
44 try:
45 return LibertineContainer(container_id, self.containers_config)
46 except ImportError as e:
47 container_type = self.containers_config.get_container_type(container_id)
48 libertine.utils.get_logger().error(_("Backend for container '{id}' not installed. Install "
49 "'python3-libertine-{type}' and try again.").format(id=container_id, type=container_type))
50 sys.exit(1)
51
38 def _get_updated_locale(self, container_id):52 def _get_updated_locale(self, container_id):
39 host_locale = self.host_info.get_host_locale()53 host_locale = self.host_info.get_host_locale()
4054
@@ -99,16 +113,18 @@
99113
100 try:114 try:
101 self.containers_config.update_container_locale(args.id, self.host_info.get_host_locale())115 self.containers_config.update_container_locale(args.id, self.host_info.get_host_locale())
102 container = LibertineContainer(args.id)116 container = LibertineContainer(args.id, self.containers_config)
103 self.containers_config.update_container_install_status(args.id, "installing")117 try:
104 if not container.create_libertine_container(password, args.multiarch):118 self.containers_config.update_container_install_status(args.id, "installing")
105 libertine.utils.get_logger().error("Failed to create container")119 if not container.create_libertine_container(password, args.multiarch):
106 self.containers_config.delete_container(args.id)120 libertine.utils.get_logger().error("Failed to create container")
107 sys.exit(1)121 self.containers_config.delete_container(args.id)
122 sys.exit(1)
123 except Exception as e:
124 container.destroy_libertine_container(force=True)
125 raise
108 except Exception as e:126 except Exception as e:
109 libertine.utils.get_logger().error("Failed to create container: '{}'".format(str(e)))127 libertine.utils.get_logger().error("Failed to create container: '{}'".format(str(e)))
110 if container:
111 container.destroy_libertine_container(force=True)
112128
113 self.containers_config.delete_container(args.id)129 self.containers_config.delete_container(args.id)
114 sys.exit(1)130 sys.exit(1)
@@ -130,7 +146,7 @@
130146
131 def destroy(self, args):147 def destroy(self, args):
132 container_id = self.containers_config.check_container_id(args.id)148 container_id = self.containers_config.check_container_id(args.id)
133 container = LibertineContainer(container_id, self.containers_config)149 container = self._container(container_id)
134150
135 self.destroy_container(container, args.force)151 self.destroy_container(container, args.force)
136152
@@ -138,11 +154,14 @@
138154
139 def install_package(self, args):155 def install_package(self, args):
140 container_id = self.containers_config.check_container_id(args.id)156 container_id = self.containers_config.check_container_id(args.id)
141 container = LibertineContainer(container_id, self.containers_config)157 container = self._container(container_id)
142 failure = False158 failure = False
143159
144 with ContainerRunning(container.container):160 with ContainerRunning(container.container):
145 for i, pkg in enumerate(args.package):161 for i, pkg in enumerate(args.package):
162 if not pkg:
163 continue
164
146 is_debian_package = pkg.endswith('.deb')165 is_debian_package = pkg.endswith('.deb')
147166
148 if is_debian_package:167 if is_debian_package:
@@ -193,11 +212,14 @@
193212
194 def remove_package(self, args):213 def remove_package(self, args):
195 container_id = self.containers_config.check_container_id(args.id)214 container_id = self.containers_config.check_container_id(args.id)
196 container = LibertineContainer(container_id)215 container = self._container(container_id)
197 failure = False216 failure = False
198217
199 with ContainerRunning(container.container):218 with ContainerRunning(container.container):
200 for pkg in args.package:219 for pkg in args.package:
220 if not pkg:
221 continue
222
201 if self.containers_config.get_package_install_status(container_id, pkg) != 'installed':223 if self.containers_config.get_package_install_status(container_id, pkg) != 'installed':
202 libertine.utils.get_logger().error("Package \'%s\' is not installed." % pkg)224 libertine.utils.get_logger().error("Package \'%s\' is not installed." % pkg)
203 failure = True225 failure = True
@@ -216,8 +238,8 @@
216238
217 def search_cache(self, args):239 def search_cache(self, args):
218 container_id = self.containers_config.check_container_id(args.id)240 container_id = self.containers_config.check_container_id(args.id)
241 container = self._container(container_id)
219242
220 container = LibertineContainer(container_id)
221 if container.search_package_cache(args.search_string) is not 0:243 if container.search_package_cache(args.search_string) is not 0:
222 libertine.utils.get_logger().error("Search for '{}' in container '{}' exited with non-zero status"244 libertine.utils.get_logger().error("Search for '{}' in container '{}' exited with non-zero status"
223 .format(args.id, args.search_string))245 .format(args.id, args.search_string))
@@ -225,10 +247,10 @@
225247
226 def update(self, args):248 def update(self, args):
227 container_id = self.containers_config.check_container_id(args.id)249 container_id = self.containers_config.check_container_id(args.id)
250 container = self._container(container_id)
251
228 new_locale = self._get_updated_locale(container_id)252 new_locale = self._get_updated_locale(container_id)
229253
230 container = LibertineContainer(container_id)
231
232 if not container.update_libertine_container(new_locale):254 if not container.update_libertine_container(new_locale):
233 sys.exit(1)255 sys.exit(1)
234256
@@ -242,7 +264,7 @@
242 def list_apps(self, args):264 def list_apps(self, args):
243 container_id = self.containers_config.check_container_id(args.id)265 container_id = self.containers_config.check_container_id(args.id)
244266
245 app_ids = LibertineContainer(container_id).list_app_ids()267 app_ids = self._container(container_id).list_app_ids()
246 if args.json:268 if args.json:
247 print(json.dumps(app_ids))269 print(json.dumps(app_ids))
248 else:270 else:
@@ -252,7 +274,7 @@
252 def exec(self, args):274 def exec(self, args):
253 container_id = self.containers_config.check_container_id(args.id)275 container_id = self.containers_config.check_container_id(args.id)
254276
255 container = LibertineContainer(container_id)277 container = self._container(container_id)
256278
257 if not container.exec_command(args.command):279 if not container.exec_command(args.command):
258 sys.exit(1)280 sys.exit(1)
@@ -269,7 +291,7 @@
269291
270 def configure(self, args):292 def configure(self, args):
271 container_id = self.containers_config.check_container_id(args.id)293 container_id = self.containers_config.check_container_id(args.id)
272 container = LibertineContainer(container_id)294 container = self._container(container_id)
273295
274 if args.multiarch and self.host_info.get_host_architecture() == 'amd64':296 if args.multiarch and self.host_info.get_host_architecture() == 'amd64':
275 multiarch = 'disabled'297 multiarch = 'disabled'
@@ -377,7 +399,7 @@
377 def fix_integrity(self, args):399 def fix_integrity(self, args):
378 if 'containerList' in self.containers_config.container_list:400 if 'containerList' in self.containers_config.container_list:
379 for container in self.containers_config.container_list['containerList']:401 for container in self.containers_config.container_list['containerList']:
380 libertine_container = LibertineContainer(container['id'])402 libertine_container = self._container(container['id'])
381403
382 if 'installStatus' not in container or container['installStatus'] == 'removing':404 if 'installStatus' not in container or container['installStatus'] == 'removing':
383 self.destroy_container(libertine_container)405 self.destroy_container(libertine_container)
@@ -411,7 +433,7 @@
411 libertine.utils.get_logger().error("The restart subcommand is only valid for LXC and LXD type containers.")433 libertine.utils.get_logger().error("The restart subcommand is only valid for LXC and LXD type containers.")
412 sys.exit(1)434 sys.exit(1)
413435
414 container = LibertineContainer(container_id)436 container = self._container(container_id)
415437
416 container.restart_libertine_container()438 container.restart_libertine_container()
417439
418440
=== modified file 'tools/libertine-launch'
--- tools/libertine-launch 2017-02-23 18:18:29 +0000
+++ tools/libertine-launch 2017-03-31 20:05:48 +0000
@@ -19,6 +19,10 @@
19import os19import os
20import sys20import sys
2121
22import gettext
23gettext.textdomain('libertine')
24_ = gettext.gettext
25
22from libertine import launcher26from libertine import launcher
2327
24def main():28def main():
@@ -30,8 +34,14 @@
30 utils.get_logger().error("No container with id '%s'" % config.container_id)34 utils.get_logger().error("No container with id '%s'" % config.container_id)
31 sys.exit(1)35 sys.exit(1)
3236
33 from libertine import LibertineContainer37 try:
34 container = LibertineContainer(container_id=config.container_id)38 from libertine import LibertineContainer
39 container = LibertineContainer(container_id=config.container_id)
40 except ImportError as e:
41 container_type = self.containers_config.get_container_type(container_id)
42 libertine.utils.get_logger().error(_("Backend for container '{id}' not installed. Install "
43 "'python3-libertine-{type}' and try again.").format(id=config.container_id, type=container_type))
44 sys.exit(1)
35 else:45 else:
36 from libertine import NoContainer46 from libertine import NoContainer
3747
3848
=== modified file 'tools/libertine-lxd-setup'
--- tools/libertine-lxd-setup 2017-01-06 15:32:00 +0000
+++ tools/libertine-lxd-setup 2017-03-31 20:05:48 +0000
@@ -20,20 +20,43 @@
20 echo ${idmap} | tee -a /etc/subuid /etc/subgid20 echo ${idmap} | tee -a /etc/subuid /etc/subgid
21fi21fi
2222
23# find the right lxc command23
24lxc=`which lxc`24lxc profile device list default | grep "nic\|disk" > /dev/null
25if [ -z "${lxc}" ]; then25requires_init=$?
26 if [ -n `which lxd.lxc` ]; then26version=`lxd --version`
27 lxc=`which lxd.lxc`27if [ ${version%%.*} -lt 2 -o ${version#*.} -lt 3 ]; then
28 else28 if [ "$requires_init" -ne "0" ]; then
29 echo "No lxc command found on this system."29 # Running lxd init without args to force the caller to pick the options
30 exit 130 lxd init
31 fi31 fi
32fi32else
3333 if [ "$requires_init" -ne "0" ]; then
34# Run lxd init if there are no containers already on the system34 # Running lxd init with auto flag to choose most of the right options
35if [ 2 -ge `${lxc} list | grep -e "^+" | wc -l` ]; then35 lxd init --auto
36 lxd init36 fi
37
38 # networking
39 ifconfig lxdbr0 > /dev/null
40 bridge_exists=$?
41
42 lxc network show lxdbr0 > /dev/null
43 network_exists=$?
44
45 # no managed network, no bridge means we need to create a managed network
46 if [ "$bridge_exists" -ne "0" ]; then
47 if [ "$network_exists" -ne "0" ]; then
48 lxc network create lxdbr0
49
50 network_name=`lxc profile device get default eth0 parent`
51 if [ "$network_name" != "lxdbr0" ]; then
52 lxc network attach-profile lxdbr0 default eth0
53 fi
54 else
55 # a managed network and no bridge means the bridge should be recreated
56 # when we restart the lxd service
57 service lxd restart
58 fi
59 fi
37fi60fi
3861
39mkdir -p /home/$USERNAME/.config/lxc62mkdir -p /home/$USERNAME/.config/lxc
4063
=== modified file 'tools/libertined'
--- tools/libertined 2017-02-15 21:12:37 +0000
+++ tools/libertined 2017-03-31 20:05:48 +0000
@@ -1,7 +1,7 @@
1#!/usr/bin/python31#!/usr/bin/python3
2# -*- coding: utf-8 -*-2# -*- coding: utf-8 -*-
33
4# Copyright 2016 Canonical Ltd.4# Copyright 2016-2017 Canonical Ltd.
5#5#
6# This program is free software: you can redistribute it and/or modify6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by7# it under the terms of the GNU General Public License as published by
@@ -15,13 +15,17 @@
15# You should have received a copy of the GNU General Public License15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.16# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
18
18import argparse19import argparse
20import dbus
21import os
19import signal22import signal
20import sys23import sys
21import os24
22from gi.repository import GLib, GObject25from dbus.mainloop.glib import DBusGMainLoop
26from gi.repository import GLib
23from libertine import utils27from libertine import utils
24from libertine.service import manager28from libertine.service import constants, operations, container_control, container_control_client
2529
2630
27class OutputRedirector(object):31class OutputRedirector(object):
@@ -116,6 +120,7 @@
116 signal.SIGTERM,120 signal.SIGTERM,
117 self.sigterm,121 self.sigterm,
118 None)122 None)
123 DBusGMainLoop(set_as_default=True)
119 self.loop = GLib.MainLoop()124 self.loop = GLib.MainLoop()
120125
121 def sigterm(self, code):126 def sigterm(self, code):
@@ -135,13 +140,29 @@
135 config = Config()140 config = Config()
136141
137 with OutputRedirector(config):142 with OutputRedirector(config):
138 utils.get_logger().info("Starting libertine service")143 utils.get_logger().info("Initializing libertined...")
139 service = manager.Manager()
140 loop = Loop()144 loop = Loop()
141 try:145
146 try:
147 bus_name = dbus.service.BusName(constants.SERVICE_NAME,
148 bus=dbus.SessionBus(),
149 do_not_queue=True)
150 except dbus.exceptions.NameExistsException:
151 utils.get_logger().warning("service is already running")
152 raise
153
154 client = container_control_client.ContainerControlClient()
155 manager = operations.Operations(bus_name, client)
156 container_control.ContainerControl(manager.connection, client)
157
158 try:
159 utils.get_logger().info("libertined ready")
142 loop.run()160 loop.run()
143 except KeyboardInterrupt:161 except KeyboardInterrupt:
144 utils.get_logger().debug("keyboard interrupt received")162 utils.get_logger().debug("keyboard interrupt received")
163 except Exception as e:
164 utils.get_logger().error("Unexpected exception occurred: '{}'".format(str(e)))
165 finally:
145 loop.shutdown()166 loop.shutdown()
146167
147168

Subscribers

People subscribed via source and target branches