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
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2017-03-06 17:36:14 +0000
3+++ CMakeLists.txt 2017-03-31 20:05:48 +0000
4@@ -30,6 +30,12 @@
5 pkg_check_modules(PYTHON3 REQUIRED python3)
6 include_directories(${PYTHON3_INCLUDE_DIRS})
7
8+pkg_check_modules(DBUSTEST REQUIRED dbustest-1>=14.04.0)
9+include_directories(${DBUSTEST_INCLUDE_DIRS})
10+
11+pkg_check_modules(GIO2 REQUIRED gio-2.0 gio-unix-2.0)
12+include_directories(${GIO2_INCLUDE_DIRS})
13+
14 set(CMAKE_AUTOMOC ON)
15
16 set(LIBERTINE_COMMON libertine-common)
17
18=== modified file 'debian/changelog'
19--- debian/changelog 2017-03-20 15:34:36 +0000
20+++ debian/changelog 2017-03-31 20:05:48 +0000
21@@ -1,3 +1,40 @@
22+libertine (1.7.1-0ubuntu1) UNRELEASED; urgency=medium
23+
24+ [ Chris Townsend ]
25+ * When starting pasted, ensure DISPLAY is set and valid before continuing to
26+ run. (LP: #1666472)
27+ * Fix pasted to work with rootless Xmir. (LP: #1671257)
28+ * Add a '-x' option to pkill so it only kills the libertined process(es)
29+ and nothing else. (LP: #1676005)
30+ * Remove hard-coded dependency on libraries.
31+
32+ [ Larry Price ]
33+ * Bump version to 1.7.1
34+ * Gracefully handle creating a LibertineContainer object when the container
35+ backend is unavailable.
36+ * Update libertine xmir components to not depend on container backends.
37+ (LP: #1671938)
38+ * Catch all errors and gracefully shutdown libertined. (LP: #1671009)
39+ * Fix method call from ContainerControl d-bus to interfaces.
40+ * Update signal handlers in test_libertine_service to reflect new API.
41+ * Inject client for accessing ContainerControl within containers.
42+ * Rearchitect libertine service python backend for simpler access to running
43+ tasks. (LP: #1669091)
44+ * Ignore completions from dependencies during snapcraft build.
45+ * LXD needs to forward host environment to container when running arbitrary
46+ commands.
47+ * Add client object to list_app_ids_task to avoid making reentrant service
48+ calls.
49+ * Take advantage of new network subcommand during lxd init on newer
50+ installs.
51+ * Prevent installing empty package names and appropriately update status for
52+ unstopped containers.
53+ * Modifications to make test_libertine_service more stable.
54+ * Reopen the database file after every failure to grab the lock.
55+ (LP: #1662655)
56+
57+ -- Larry Price <larry.price@canonical.com> Tue, 14 Mar 2017 11:35:33 -0400
58+
59 libertine (1.7+17.04.20170320.1-0ubuntu1) zesty; urgency=medium
60
61 * No change rebuild
62
63=== modified file 'debian/control'
64--- debian/control 2017-03-13 14:01:08 +0000
65+++ debian/control 2017-03-31 20:05:48 +0000
66@@ -9,6 +9,7 @@
67 gobject-introspection,
68 intltool,
69 libcontent-hub-dev (>= 0.2),
70+ libdbustest1-dev (>= 14.04.0),
71 libgirepository1.0-dev,
72 libglib2.0-dev,
73 libgtest-dev,
74@@ -61,7 +62,6 @@
75 Depends: libertine-qt-common,
76 libertine-tools,
77 python3-libertine-lxd,
78- libsystemsettings1,
79 ${misc:Depends},
80 ${shlibs:Depends}
81 Enhances: ubuntu-system-settings
82@@ -74,7 +74,7 @@
83 Architecture: any
84 Depends: qml-module-qtquick2,
85 qml-module-qtquick-dialogs,
86- qtdeclarative5-ubuntu-ui-toolkit-plugin,
87+ qtdeclarative5-ubuntu-ui-toolkit-plugin | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles,
88 ${misc:Depends},
89 ${shlibs:Depends}
90 Description: common files for qt-based libertine applications
91@@ -84,13 +84,14 @@
92
93 Package: libertine-tools
94 Architecture: any
95-Depends: libglib2.0-bin,
96+Depends: libertine-xmir-tools (>= 1.7.1) [amd64 armhf arm64 i386],
97+ libglib2.0-bin,
98 python3-dbus,
99 python3-libertine,
100 ${misc:Depends},
101 ${python3:Depends},
102 ${shlibs:Depends}
103-Recommends: libertine-xmir-tools (>= 1.4.3)
104+Recommends: python3-libertine-lxd
105 Breaks: libertine-demo
106 Replaces: libertine-demo
107 Description: CLI tools for running deb-packaged X11 apps on Ubuntu Personal
108@@ -98,17 +99,14 @@
109 the Ubuntu Personal sandbox for legacy Deb-packaged X11 applicatons.
110
111 Package: libertine-xmir-tools
112-Architecture: amd64 armhf arm64 i386
113-Depends: libcontent-hub0,
114- libqt5core5a,
115- libqt5gui5,
116- libqt5widgets5,
117+Architecture: any
118+Depends: python3-libertine,
119 xmir [amd64 armhf arm64 i386],
120 ${misc:Depends},
121 ${python3:Depends},
122 ${shlibs:Depends}
123-Breaks: libertine-tools (<< 1.4.3)
124-Replaces: libertine-tools (<< 1.4.3)
125+Breaks: libertine-tools (<< 1.7.1)
126+Replaces: libertine-tools (<< 1.7.1)
127 Description: helper apps for using and interacting with Xmir
128 Helper applications for using and interacting with Xmir such as launching
129 Xmir and allowing copy and paste.
130
131=== modified file 'debian/libertine-tools.install'
132--- debian/libertine-tools.install 2017-01-20 18:43:08 +0000
133+++ debian/libertine-tools.install 2017-03-31 20:05:48 +0000
134@@ -1,4 +1,3 @@
135 usr/bin/libertine-container-manager
136-usr/bin/libertine-launch
137 usr/share/bash-completion/completions/libertine-container-manager
138-usr/share/man
139+usr/share/man/*/libertine-container-manager.1
140
141=== modified file 'debian/libertine-xmir-tools.install'
142--- debian/libertine-xmir-tools.install 2016-10-26 20:48:08 +0000
143+++ debian/libertine-xmir-tools.install 2017-03-31 20:05:48 +0000
144@@ -1,3 +1,7 @@
145+usr/bin/libertine-launch
146 usr/bin/libertine-xmir
147 usr/bin/pasted
148+usr/lib/python*/*/libertine/launcher
149+usr/share/man/*/libertine-launch.1
150+usr/share/man/*/libertine-xmir.1
151 usr/share/upstart/sessions/libertine-xmir.conf
152
153=== modified file 'debian/libertined.preinst'
154--- debian/libertined.preinst 2017-03-06 16:15:27 +0000
155+++ debian/libertined.preinst 2017-03-31 20:05:48 +0000
156@@ -16,7 +16,7 @@
157
158 if [ "$1" = "upgrade" ]; then
159 echo "Shutting down the libertined service..."
160- pkill libertined
161+ pkill -x libertined
162 fi
163
164 #DEBHELPER#
165
166=== modified file 'debian/libertined.prerm'
167--- debian/libertined.prerm 2017-03-06 16:15:27 +0000
168+++ debian/libertined.prerm 2017-03-31 20:05:48 +0000
169@@ -16,7 +16,7 @@
170
171 if [ "$1" = "remove" ]; then
172 echo "Shutting down the libertined service..."
173- pkill libertined
174+ pkill -x libertined
175 fi
176
177 #DEBHELPER#
178
179=== modified file 'debian/python3-libertine.install'
180--- debian/python3-libertine.install 2017-02-15 21:12:37 +0000
181+++ debian/python3-libertine.install 2017-03-31 20:05:48 +0000
182@@ -1,7 +1,6 @@
183-usr/lib/python*/*/libertine/Client.py
184+usr/lib/python*/*/libertine/ContainerControlClient.py
185 usr/lib/python*/*/libertine/ContainersConfig.py
186 usr/lib/python*/*/libertine/HostInfo.py
187 usr/lib/python*/*/libertine/Libertine.py
188 usr/lib/python*/*/libertine/__init__.py
189-usr/lib/python*/*/libertine/launcher
190 usr/lib/python*/*/libertine/utils.py
191
192=== modified file 'liblibertine/libertined.cpp'
193--- liblibertine/libertined.cpp 2017-02-23 21:08:28 +0000
194+++ liblibertine/libertined.cpp 2017-03-31 20:05:48 +0000
195@@ -25,12 +25,16 @@
196 #include <QJsonArray>
197 #include <QJsonObject>
198
199+
200 namespace
201 {
202-constexpr auto SERVICE_INTERFACE = "com.canonical.libertine.Service";
203-constexpr auto PROGRESS_INTERFACE = "com.canonical.libertine.Service.Progress";
204-constexpr auto SESSION_DBUS_ENV_VAR = "DBUS_SESSION_BUS_ADDRESS";
205-constexpr auto SERVICE_NAME = "libertined";
206+constexpr auto SERVICE_INTERFACE = "com.canonical.libertine.Service";
207+constexpr auto OPERATIONS_INTERFACE = "com.canonical.libertine.Service.Operations";
208+constexpr auto OPERATIONS_OBJECT = "/com/canonical/libertine/Service/Operations";
209+constexpr auto OPERATIONS_MONITOR_INTERFACE = "com.canonical.libertine.Service.OperationsMonitor";
210+constexpr auto OPERATIONS_MONITOR_OBJECT = "/com/canonical/libertine/Service/OperationsMonitor";
211+constexpr auto SESSION_DBUS_ENV_VAR = "DBUS_SESSION_BUS_ADDRESS";
212+constexpr auto SERVICE_NAME = "libertined";
213
214
215 class SessionBus
216@@ -69,7 +73,7 @@
217 static bool
218 isRunning(QDBusConnection const& bus, QString const& path)
219 {
220- auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "running", QVariantList());
221+ auto args = dbusCall(bus, OPERATIONS_MONITOR_INTERFACE, OPERATIONS_MONITOR_OBJECT, "running", QVariantList{path});
222
223 if (args.isEmpty())
224 {
225@@ -83,12 +87,12 @@
226 static QString
227 result(QDBusConnection const& bus, QString const& path)
228 {
229- auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "result");
230+ auto args = dbusCall(bus, OPERATIONS_MONITOR_INTERFACE, OPERATIONS_MONITOR_OBJECT, "result", QVariantList{path});
231
232 if (args.isEmpty())
233 {
234 qWarning() << "lastError - no arguments?";
235- return "";
236+ return QString();
237 }
238
239 return args.first().toString();
240@@ -97,12 +101,12 @@
241 static QString
242 lastError(QDBusConnection const& bus, QString const& path)
243 {
244- auto args = dbusCall(bus, PROGRESS_INTERFACE, path, "last_error");
245+ auto args = dbusCall(bus, OPERATIONS_MONITOR_INTERFACE, OPERATIONS_MONITOR_OBJECT, "last_error", QVariantList{path});
246
247 if (args.isEmpty())
248 {
249 qWarning() << "lastError - no arguments?";
250- return "";
251+ return QString();
252 }
253
254 return args.first().toString();
255@@ -111,7 +115,7 @@
256 static QString
257 call(QDBusConnection const& bus, QString const& method, QVariantList const& args)
258 {
259- auto results = dbusCall(bus, SERVICE_INTERFACE, "/Manager", method, args);
260+ auto results = dbusCall(bus, OPERATIONS_INTERFACE, OPERATIONS_OBJECT, method, args);
261
262 if (results.isEmpty())
263 {
264@@ -158,6 +162,7 @@
265 }
266 }
267
268+
269 QJsonArray
270 libertined_list()
271 {
272
273=== modified file 'pasted/pasted.cpp'
274--- pasted/pasted.cpp 2016-09-12 17:59:56 +0000
275+++ pasted/pasted.cpp 2017-03-31 20:05:48 +0000
276@@ -36,9 +36,8 @@
277 constexpr auto UNITY_FOCUSINFO_METHOD = "isSurfaceFocused";
278
279
280-static QString getPersistentSurfaceId()
281+static QString getPersistentSurfaceId(Display *dpy, const Window& id)
282 {
283- Display *dpy = XOpenDisplay(NULL);
284 Atom prop = XInternAtom(dpy, MIR_WM_PERSISTENT_ID, 0),
285 type; // unused
286 int form, // unused
287@@ -48,7 +47,7 @@
288 unsigned char *data = nullptr;
289 QString persistentSurfaceId;
290
291- status = XGetWindowProperty(dpy, XDefaultRootWindow(dpy), prop, 0, 1024, 0,
292+ status = XGetWindowProperty(dpy, id, prop, 0, 1024, 0,
293 XA_STRING, &type, &form, &len, &remain, &data);
294
295 if (status)
296@@ -60,56 +59,109 @@
297 persistentSurfaceId = (const char *)data;
298 }
299
300- XCloseDisplay(dpy);
301 XFree(data);
302
303 return persistentSurfaceId;
304 }
305
306+
307+Display *checkXServer()
308+{
309+ char *display = getenv("DISPLAY");
310+
311+ if (display == nullptr)
312+ {
313+ qCritical() << "DISPLAY environment variable not set!";
314+ exit(-1);
315+ }
316+
317+ Display *dpy = XOpenDisplay(display);
318+ if (dpy == nullptr)
319+ {
320+ qCritical() << "Xmir is not running on DISPLAY" << display << "!";
321+ exit(-1);
322+ }
323+
324+ return dpy;
325+}
326+
327 } //anonymous namespace
328
329
330+XEventWorker::
331+XEventWorker(Display *dpy)
332+: dpy_(dpy)
333+{
334+ unityFocus_ = new QDBusInterface(UNITY_FOCUSINFO_SERVICE,
335+ UNITY_FOCUSINFO_PATH,
336+ UNITY_FOCUSINFO_INTERFACE,
337+ QDBusConnection::sessionBus(),
338+ this);
339+}
340+
341+
342+XEventWorker::
343+~XEventWorker()
344+{
345+ XCloseDisplay(dpy_);
346+}
347+
348+
349+bool XEventWorker::
350+isSurfaceFocused(const Window& focus_window)
351+{
352+ surfaceId_ = getPersistentSurfaceId(dpy_, focus_window);
353+
354+ QDBusReply<bool> isFocused = unityFocus_->call(UNITY_FOCUSINFO_METHOD, surfaceId_);
355+
356+ return isFocused;
357+}
358+
359+
360 void XEventWorker::
361 checkForAppFocus()
362 {
363 bool hasFocus = false;
364+ int focus_state;
365+ Window focus_window;
366+
367+ XGetInputFocus(dpy_, &focus_window, &focus_state);
368+
369+ if (focus_window > PointerRoot)
370+ {
371+ if (isSurfaceFocused(focus_window))
372+ {
373+ focusChanged(surfaceId_);
374+ hasFocus = true;
375+ }
376+ }
377+
378+ XSelectInput(dpy_, XDefaultRootWindow(dpy_), FocusChangeMask);
379+
380+ bool focused = false;
381 XEvent event;
382
383- QDBusInterface *unityFocus = new QDBusInterface(UNITY_FOCUSINFO_SERVICE,
384- UNITY_FOCUSINFO_PATH,
385- UNITY_FOCUSINFO_INTERFACE,
386- QDBusConnection::sessionBus(),
387- this);
388-
389- QString surfaceId = getPersistentSurfaceId();
390-
391- QDBusReply<bool> isFocused = unityFocus->call(UNITY_FOCUSINFO_METHOD, surfaceId);
392-
393- if (isFocused == true)
394- {
395- focusChanged();
396- hasFocus = true;
397- }
398-
399- Display *dpy = XOpenDisplay(NULL);
400- XSelectInput(dpy, XDefaultRootWindow(dpy), FocusChangeMask);
401-
402 while (1)
403 {
404- XNextEvent(dpy, &event);
405-
406- isFocused = unityFocus->call(UNITY_FOCUSINFO_METHOD, surfaceId);
407-
408- if (hasFocus == false && isFocused == true)
409- {
410- qDebug() << "Surface is focused";
411- focusChanged();
412- hasFocus = true;
413- }
414- else if (hasFocus == true && isFocused == false)
415- {
416- qDebug() << "Surface lost focus";
417- hasFocus = false;
418+ XNextEvent(dpy_, &event);
419+
420+ XGetInputFocus(dpy_, &focus_window, &focus_state);
421+
422+ if (focus_window > PointerRoot)
423+ {
424+ focused = isSurfaceFocused(focus_window);
425+
426+ if (hasFocus == false && focused == true)
427+ {
428+ qDebug() << "Surface is focused";
429+ focusChanged(surfaceId_);
430+ hasFocus = true;
431+ }
432+ else if (hasFocus == true && focused == false)
433+ {
434+ qDebug() << "Surface lost focus";
435+ hasFocus = false;
436+ }
437 }
438 }
439 }
440@@ -230,19 +282,19 @@
441
442
443 void Pasted::
444-setPersistentSurfaceId()
445+setPersistentSurfaceId(const QString& surfaceId)
446 {
447- if (persistentSurfaceId_.isEmpty())
448+ if (persistentSurfaceId_ != surfaceId)
449 {
450- persistentSurfaceId_ = getPersistentSurfaceId();
451+ persistentSurfaceId_ = surfaceId;
452 }
453 }
454
455
456 void Pasted::
457-appFocused()
458+appFocused(const QString& surfaceId)
459 {
460- setPersistentSurfaceId();
461+ setPersistentSurfaceId(surfaceId);
462 handleContentHubPasteboard();
463 }
464
465@@ -252,10 +304,12 @@
466 {
467 qSetMessagePattern(QString("%{appname}: %{message}"));
468
469+ Display *dpy = checkXServer();
470+
471 Pasted pasted(argc, argv);
472
473 QThread t;
474- XEventWorker worker;
475+ XEventWorker worker(dpy);
476
477 worker.moveToThread(&t);
478
479
480=== modified file 'pasted/pasted.h'
481--- pasted/pasted.h 2016-09-12 17:59:56 +0000
482+++ pasted/pasted.h 2017-03-31 20:05:48 +0000
483@@ -39,14 +39,22 @@
484 Q_OBJECT
485
486 public:
487- XEventWorker() = default;
488- virtual ~XEventWorker() = default;
489+ XEventWorker(Display *dpy);
490+ virtual ~XEventWorker();
491+
492+ private:
493+ bool isSurfaceFocused(const Window& focus_window);
494
495 signals:
496- void focusChanged();
497+ void focusChanged(const QString& surfaceId);
498
499 public slots:
500 void checkForAppFocus();
501+
502+ private:
503+ Display *dpy_;
504+ QDBusInterface *unityFocus_;
505+ QString surfaceId_;
506 };
507
508
509@@ -60,13 +68,13 @@
510 virtual ~Pasted() = default;
511
512 public slots:
513- void appFocused();
514+ void appFocused(const QString& surfaceId);
515
516 private:
517 void updateLastMimeData(const QMimeData *source);
518 void updateXMimeData(const QMimeData *source);
519 void handleContentHubPasteboard();
520- void setPersistentSurfaceId();
521+ void setPersistentSurfaceId(const QString& surfaceId);
522
523 static bool compareMimeData(const QMimeData *a, const QMimeData *b);
524 static void copyMimeData(QMimeData& target, const QMimeData *source);
525
526=== modified file 'python/libertine/ChrootContainer.py'
527--- python/libertine/ChrootContainer.py 2017-02-27 20:47:21 +0000
528+++ python/libertine/ChrootContainer.py 2017-03-31 20:05:48 +0000
529@@ -47,8 +47,8 @@
530 A concrete container type implemented using a plain old chroot.
531 """
532
533- def __init__(self, container_id, config):
534- super().__init__(container_id, 'chroot', config)
535+ def __init__(self, container_id, config, service):
536+ super().__init__(container_id, 'chroot', config, service)
537 # FIXME: Disabling seccomp is a temporary measure until we fully understand why we need
538 # it or figure out when we need it.
539 os.environ['PROOT_NO_SECCOMP'] = '1'
540
541=== renamed file 'python/libertine/Client.py' => 'python/libertine/ContainerControlClient.py'
542--- python/libertine/Client.py 2017-02-24 14:56:36 +0000
543+++ python/libertine/ContainerControlClient.py 2017-03-31 20:05:48 +0000
544@@ -19,19 +19,24 @@
545 from . import utils
546
547
548-class Client(object):
549+class ContainerControlClient(object):
550+ """
551+ A client for ContainerControl using D-BUS, to be used in clients
552+ external to the libertine service.
553+ """
554 def __init__(self):
555 self._get_manager()
556
557 def _get_manager(self):
558- self._manager = None
559+ self._control = None
560
561 try:
562- from .service.manager import LIBERTINE_MANAGER_NAME, LIBERTINE_MANAGER_OBJECT
563+ from .service import constants
564+
565 if utils.set_session_dbus_env_var():
566 bus = dbus.SessionBus()
567- self._manager = bus.get_object(LIBERTINE_MANAGER_NAME, LIBERTINE_MANAGER_OBJECT)
568- self._interface = LIBERTINE_MANAGER_NAME
569+ self._control = bus.get_object(constants.SERVICE_NAME, constants.CONTAINER_CONTROL_OBJECT)
570+ self._interface = constants.CONTAINER_CONTROL_INTERFACE
571 except ImportError as e:
572 utils.get_logger().warning("Libertine service libraries not installed.")
573 except dbus.exceptions.DBusException as e:
574@@ -51,11 +56,11 @@
575
576 @property
577 def valid(self):
578- return self._manager is not None
579+ return self._control is not None
580
581 def container_operation_start(self, id):
582 retries = 0
583- while not self._do_operation(lambda: self._manager.get_dbus_method('container_operation_start', self._interface)(id)):
584+ while not self._do_operation(lambda: self._control.get_dbus_method('start', self._interface)(id)):
585 retries += 1
586 if retries > 5:
587 return False
588@@ -64,7 +69,7 @@
589 return True
590
591 def container_operation_finished(self, id, app_name, pid):
592- return self._do_operation(lambda: self._manager.get_dbus_method("container_operation_finished", self._interface)(id, app_name, pid))
593+ return self._do_operation(lambda: self._control.get_dbus_method('finished', self._interface)(id, app_name, pid))
594
595 def container_stopped(self, id):
596- return self._do_operation(lambda: self._manager.get_dbus_method('container_stopped', self._interface)(id))
597+ return self._do_operation(lambda: self._control.get_dbus_method('stopped', self._interface)(id))
598
599=== modified file 'python/libertine/ContainersConfig.py'
600--- python/libertine/ContainersConfig.py 2017-02-23 21:08:28 +0000
601+++ python/libertine/ContainersConfig.py 2017-03-31 20:05:48 +0000
602@@ -14,42 +14,77 @@
603
604 import fcntl
605 import json
606-import libertine.utils
607 import os
608 import sys
609+import errno
610+import time
611+
612 from hashlib import md5
613-from libertine.HostInfo import HostInfo
614+from . import utils, HostInfo
615+
616+class _ContainersConfigFile(object):
617+ def __init__(self, readonly=True):
618+ self._readonly = readonly
619+
620+ if readonly:
621+ self._flags = 'r'
622+ else:
623+ self._flags = 'w'
624+
625+ self._fd = None
626+
627+ def __enter__(self):
628+ container_config_file = utils.get_libertine_database_file_path()
629+ if self._readonly and (not os.path.exists(container_config_file) or
630+ os.path.getsize(container_config_file) == 0):
631+ return None
632+
633+ retries = 0
634+ while retries < 100:
635+ try:
636+ fd = open(container_config_file, self._flags)
637+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
638+
639+ self._fd = fd
640+ return self._fd
641+ except IOError as e:
642+ if e.errno != errno.EAGAIN:
643+ raise
644+ else:
645+ fd.close()
646+ retries += 1
647+ time.sleep(0.05)
648+
649+ def __exit__(self, *args):
650+ if self._fd:
651+ self._fd.close()
652
653
654 def read_container_config_file():
655 container_list = {}
656- container_config_file = libertine.utils.get_libertine_database_file_path()
657-
658- if (os.path.exists(container_config_file) and
659- os.path.getsize(container_config_file) != 0):
660- with open(container_config_file, 'r') as fd:
661- fcntl.flock(fd, fcntl.LOCK_EX)
662+ with _ContainersConfigFile() as fd:
663+ if fd is not None:
664 container_list = json.load(fd)
665
666 return container_list
667
668
669 def write_container_config_file(container_list):
670- container_config_file = libertine.utils.get_libertine_database_file_path()
671+ container_config_file = utils.get_libertine_database_file_path()
672
673 # Add a warning to adventurous users advising against mucking with this file
674 if container_list is not None:
675 container_list["_warning"] = "This file is automatically generated by Libertine and should not be manually edited."
676
677- with open(container_config_file, 'w') as fd:
678- fcntl.flock(fd, fcntl.LOCK_EX)
679- json.dump(container_list, fd, sort_keys=True, indent=4)
680- fd.write('\n')
681+ with _ContainersConfigFile(False) as fd:
682+ if fd:
683+ json.dump(container_list, fd, sort_keys=True, indent=4)
684+ fd.write('\n')
685
686
687 def container_config_hash():
688 checksum = md5()
689- container_config_file = libertine.utils.get_libertine_database_file_path()
690+ container_config_file = utils.get_libertine_database_file_path()
691 if (os.path.exists(container_config_file) and os.path.getsize(container_config_file) != 0):
692 with open(container_config_file, "rb") as fd:
693 fcntl.flock(fd, fcntl.LOCK_EX)
694@@ -214,12 +249,12 @@
695
696 def check_container_id(self, container_id):
697 if container_id and not self.container_exists(container_id):
698- libertine.utils.get_logger().error("Container id \'%s\' does not exist." % container_id)
699+ utils.get_logger().error("Container id '{container_id}' does not exist.".format(container_id=container_id))
700 sys.exit(1)
701 elif not container_id:
702 container_id = self.get_default_container_id()
703 if container_id is None:
704- libertine.utils.get_logger().error("No default container available.")
705+ utils.get_logger().error("No default container available.")
706 sys.exit(1)
707
708 return container_id
709@@ -263,7 +298,7 @@
710
711 def delete_container(self, container_id):
712 if not self.container_list:
713- libertine.utils.get_logger().error("Unable to delete container. No containers defined.")
714+ utils.get_logger().error("Unable to delete container. No containers defined.")
715 sys.exit(1)
716
717 container = self._get_container_entry(container_id)
718
719=== modified file 'python/libertine/Libertine.py'
720--- python/libertine/Libertine.py 2017-03-03 15:34:16 +0000
721+++ python/libertine/Libertine.py 2017-03-31 20:05:48 +0000
722@@ -17,7 +17,7 @@
723 import os
724 import shutil
725
726-from . import utils
727+from . import utils, ContainerControlClient
728 from libertine.ContainersConfig import ContainersConfig
729 from libertine.HostInfo import HostInfo
730
731@@ -79,10 +79,11 @@
732
733 :param container_id: The machine-readable container name.
734 """
735- def __init__(self, container_id, container_type, config):
736+ def __init__(self, container_id, container_type, config, service):
737 self.container_type = container_type
738 self.container_id = container_id
739 self._config = config
740+ self._service = service
741 self._app_name = ''
742 self._pid = 0
743 self.root_path = utils.get_libertine_container_rootfs_path(self.container_id)
744@@ -332,8 +333,8 @@
745 """
746 A concrete mock container type. Used for unit testing.
747 """
748- def __init__(self, container_id, config):
749- super().__init__(container_id, 'mock', config)
750+ def __init__(self, container_id, config, service):
751+ super().__init__(container_id, 'mock', config, service)
752
753 def create_libertine_container(self, password=None, multiarch=False):
754 return True
755@@ -386,7 +387,7 @@
756 A sandbox for DEB-packaged X11-based applications.
757 """
758
759- def __init__(self, container_id, containers_config=None):
760+ def __init__(self, container_id, containers_config=None, service=None):
761 """
762 Initializes the container object.
763
764@@ -394,23 +395,22 @@
765 """
766 super().__init__()
767
768- if containers_config is None:
769- containers_config = ContainersConfig()
770- self.containers_config = containers_config
771+ self.containers_config = containers_config or ContainersConfig()
772+ service = service or ContainerControlClient.ContainerControlClient()
773
774 container_type = self.containers_config.get_container_type(container_id)
775
776 if container_type == "lxc":
777 from libertine.LxcContainer import LibertineLXC
778- self.container = LibertineLXC(container_id, self.containers_config)
779+ self.container = LibertineLXC(container_id, self.containers_config, service)
780 elif container_type == "lxd":
781 from libertine.LxdContainer import LibertineLXD
782- self.container = LibertineLXD(container_id, self.containers_config)
783+ self.container = LibertineLXD(container_id, self.containers_config, service)
784 elif container_type == "chroot":
785 from libertine.ChrootContainer import LibertineChroot
786- self.container = LibertineChroot(container_id, self.containers_config)
787+ self.container = LibertineChroot(container_id, self.containers_config, service)
788 elif container_type == "mock":
789- self.container = LibertineMock(container_id, self.containers_config)
790+ self.container = LibertineMock(container_id, self.containers_config, service)
791 else:
792 raise RuntimeError("Unsupported container type %s" % container_type)
793
794@@ -463,7 +463,10 @@
795 try:
796 with ContainerRunning(self.container):
797 self.containers_config.update_container_install_status(self.container_id, "installing packages")
798- return self.container.install_package(package_name, no_dialog, update_cache)
799+ retval = self.container.install_package(package_name, no_dialog, update_cache)
800+
801+ self.containers_config.update_container_install_status(self.container_id, "running")
802+ return retval
803 except RuntimeError as e:
804 return handle_runtime_error(e)
805
806@@ -479,7 +482,10 @@
807 os.environ['DEBIAN_FRONTEND'] = 'teletype'
808
809 self.containers_config.update_container_install_status(self.container_id, "removing packages")
810- return self.container.remove_package(package_name)
811+ retval = self.container.remove_package(package_name)
812+
813+ self.containers_config.update_container_install_status(self.container_id, "running")
814+ return retval
815 except RuntimeError as e:
816 return handle_runtime_error(e)
817
818@@ -547,7 +553,7 @@
819 for root, dirs, files in os.walk(apps_dir):
820 app_ids.extend(["{}_{}_0.0".format(self.container_id, f[:-8]) for f in files if f.endswith(".desktop")])
821
822- return app_ids
823+ return sorted(app_ids)
824
825 def exec_command(self, exec_line):
826 """
827
828=== modified file 'python/libertine/LxcContainer.py'
829--- python/libertine/LxcContainer.py 2017-03-03 15:34:16 +0000
830+++ python/libertine/LxcContainer.py 2017-03-31 20:05:48 +0000
831@@ -23,7 +23,7 @@
832 import tempfile
833
834 from .Libertine import BaseContainer
835-from . import utils, HostInfo, Client
836+from . import utils, HostInfo
837
838
839 home_path = os.environ['HOME']
840@@ -157,14 +157,12 @@
841 A concrete container type implemented using an LXC container.
842 """
843
844- def __init__(self, container_id, config):
845- super().__init__(container_id, 'lxc', config)
846+ def __init__(self, container_id, config, service):
847+ super().__init__(container_id, 'lxc', config, service)
848 self.container = lxc_container(container_id)
849 self.host_info = HostInfo.HostInfo()
850 self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)
851
852- self._manager = Client.Client()
853-
854 def _setup_pulse(self):
855 pulse_socket_path = os.path.join(utils.get_libertine_runtime_dir(), 'pulse_socket')
856
857@@ -213,7 +211,7 @@
858 return fd.read().strip('\n') != self.host_info.get_host_timezone()
859
860 def start_container(self):
861- if not self._manager.container_operation_start(self.container_id):
862+ if not self._service.container_operation_start(self.container_id):
863 return False
864
865 if self.container.state == 'RUNNING':
866@@ -236,11 +234,11 @@
867 stopped = False
868 self._config.refresh_database()
869
870- if self._manager.container_operation_finished(self.container_id, self._app_name, self._pid):
871+ if self._service.container_operation_finished(self.container_id, self._app_name, self._pid):
872 self._config.update_container_install_status(self.container_id, self._get_stop_type_string(self._freeze_on_stop))
873
874 if lxc_stop(self.container, self._freeze_on_stop):
875- stopped = self._manager.container_stopped(self.container_id)
876+ stopped = self._service.container_stopped(self.container_id)
877
878 self._config.update_container_install_status(self.container_id, self.container.state.lower())
879
880@@ -397,7 +395,7 @@
881 os.environ.update(environ)
882
883 if not self.start_container():
884- self._manager.container_stopped(self.container_id)
885+ self._service.container_stopped(self.container_id)
886 return
887
888 self._app_name = app_exec_line[0]
889
890=== modified file 'python/libertine/LxdContainer.py'
891--- python/libertine/LxdContainer.py 2017-03-03 15:34:16 +0000
892+++ python/libertine/LxdContainer.py 2017-03-31 20:05:48 +0000
893@@ -22,7 +22,7 @@
894 import subprocess
895 import time
896
897-from . import Libertine, utils, HostInfo, Client
898+from . import Libertine, utils, HostInfo
899
900
901 def _get_devices_map():
902@@ -62,10 +62,11 @@
903
904 def _setup_lxd():
905 if utils.is_snap_environment():
906- utils.get_logger().warning("Running in snap environment, skipping automatic lxd setup.")
907+ utils.get_logger().warning("Snapped libertine detected, you may need to run `sudo lxd init` manually.")
908 return True
909
910- utils.get_logger().info("Running LXD setup.")
911+ utils.get_logger().debug("Running LXD setup.")
912+
913 import pexpect
914 child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'})
915
916@@ -74,6 +75,7 @@
917 # The following are required for lxd=2.0.x
918 '.+\[yes/no\].*',
919 '.+\(e.g. (?P<example>[a-z0-9\.:]+)\).+'])
920+
921 if index == 0:
922 child.sendline()
923 elif index == 1:
924@@ -383,8 +385,8 @@
925
926
927 class LibertineLXD(Libertine.BaseContainer):
928- def __init__(self, name, config):
929- super().__init__(name, 'lxd', config)
930+ def __init__(self, name, config, service):
931+ super().__init__(name, 'lxd', config, service)
932 self._host_info = HostInfo.HostInfo()
933 self._container = None
934 self._freeze_on_stop = config.get_freeze_on_stop(self.container_id)
935@@ -392,15 +394,14 @@
936 if not _setup_lxd():
937 raise Exception("Failed to setup lxd.")
938
939- self._client = pylxd.Client()
940- self._manager = Client.Client()
941+ self._lxd_client = pylxd.Client()
942
943 def create_libertine_container(self, password=None, multiarch=False):
944 if self._try_get_container():
945 utils.get_logger().error("Container already exists")
946 return False
947
948- update_libertine_profile(self._client)
949+ update_libertine_profile(self._lxd_client)
950
951 utils.get_logger().info("Creating container '%s' with distro '%s'" % (self.container_id, self.installed_release))
952 create = subprocess.Popen(shlex.split('lxc launch ubuntu-daily:{distro} {id} --profile '
953@@ -498,13 +499,13 @@
954 return _lxc_args(self.container_id, command, environ)
955
956 def run_in_container(self, command):
957- return subprocess.Popen(self._lxc_args(command)).wait()
958+ return subprocess.Popen(self._lxc_args(command, os.environ.copy())).wait()
959
960 def start_container(self, home=env_home_path()):
961 if not self._try_get_container():
962 return False
963
964- if not self._manager.container_operation_start(self.container_id):
965+ if not self._service.container_operation_start(self.container_id):
966 return False
967
968 if self._container.status == 'Running':
969@@ -513,12 +514,12 @@
970 requires_remount = self._container.status == 'Stopped'
971
972 if requires_remount:
973- update_libertine_profile(self._client)
974+ update_libertine_profile(self._lxd_client)
975 update_bind_mounts(self._container, self._config, home)
976
977 self._config.update_container_install_status(self.container_id, "starting")
978 if not lxd_start(self._container):
979- self._manager.container_stopped()
980+ self._service.container_stopped()
981 self._config.update_container_install_status(self.container_id, self._container.status.lower())
982 return False
983
984@@ -539,11 +540,11 @@
985 stopped = False
986 self._config.refresh_database()
987
988- if self._manager.container_operation_finished(self.container_id, self._app_name, self._pid):
989+ if self._service.container_operation_finished(self.container_id, self._app_name, self._pid):
990 self._config.update_container_install_status(self.container_id, self._get_stop_type_string(self._freeze_on_stop))
991-
992+
993 if lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop):
994- stopped = self._manager.container_stopped(self.container_id)
995+ stopped = self._service.container_stopped(self.container_id)
996
997 self._config.update_container_install_status(self.container_id, self._container.status.lower())
998
999@@ -598,6 +599,6 @@
1000
1001 def _try_get_container(self):
1002 if self._container is None:
1003- self._container = lxd_container(self._client, self.container_id)
1004+ self._container = lxd_container(self._lxd_client, self.container_id)
1005
1006 return self._container is not None
1007
1008=== added file 'python/libertine/service/constants.py'
1009--- python/libertine/service/constants.py 1970-01-01 00:00:00 +0000
1010+++ python/libertine/service/constants.py 2017-03-31 20:05:48 +0000
1011@@ -0,0 +1,27 @@
1012+# Copyright 2017 Canonical Ltd.
1013+#
1014+# This program is free software: you can redistribute it and/or modify
1015+# it under the terms of the GNU General Public License as published by
1016+# the Free Software Foundation; version 3 of the License.
1017+#
1018+# This program is distributed in the hope that it will be useful,
1019+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1020+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1021+# GNU General Public License for more details.
1022+#
1023+# You should have received a copy of the GNU General Public License
1024+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1025+
1026+SERVICE_NAME = "com.canonical.libertine.Service"
1027+
1028+OPERATIONS_INTERFACE = "com.canonical.libertine.Service.Operations"
1029+OPERATIONS_OBJECT = "/com/canonical/libertine/Service/Operations"
1030+
1031+DOWNLOAD_INTERFACE = "com.canonical.applications.Service.Download"
1032+DOWNLOAD_OBJECT = "/com/canonical/libertine/Service/Download/%s"
1033+
1034+OPERATIONS_MONITOR_INTERFACE = "com.canonical.libertine.Service.OperationsMonitor"
1035+OPERATIONS_MONITOR_OBJECT = "/com/canonical/libertine/Service/OperationsMonitor"
1036+
1037+CONTAINER_CONTROL_INTERFACE = "com.canonical.libertine.Service.ContainerControl"
1038+CONTAINER_CONTROL_OBJECT = "/com/canonical/libertine/Service/ContainerControl"
1039
1040=== modified file 'python/libertine/service/container.py'
1041--- python/libertine/service/container.py 2017-02-07 12:35:48 +0000
1042+++ python/libertine/service/container.py 2017-03-31 20:05:48 +0000
1043@@ -22,11 +22,13 @@
1044
1045
1046 class Container(object):
1047- def __init__(self, container_id, config, connection, callback):
1048+ def __init__(self, container_id, config, monitor, client, callback):
1049 self._id = container_id
1050- self._connection = connection
1051+ self._config = config
1052+ self._monitor = monitor
1053+ self._client = client
1054 self._callback = callback
1055- self._config = config
1056+
1057 self._lock = Lock()
1058 self._tasks = []
1059
1060@@ -53,31 +55,6 @@
1061 def tasks(self):
1062 return [task.id for task in self._tasks if task.running]
1063
1064- def search(self, query):
1065- utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query))
1066-
1067- if utils.is_snap_environment():
1068- raise Exception("This operation is not currently supported within the snap")
1069-
1070- task = SearchTask(self.id, self._cache, query, self._connection, self._cleanup_task)
1071- self._tasks.append(task)
1072- task.start()
1073-
1074- return task.id
1075-
1076- def app_info(self, package_name):
1077- utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id))
1078-
1079- if utils.is_snap_environment():
1080- raise Exception("This operation is not currently supported within the snap")
1081-
1082- related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running]
1083- task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._connection, self._cleanup_task)
1084-
1085- self._tasks.append(task)
1086- task.start()
1087- return task.id
1088-
1089 def install(self, package_name):
1090 utils.get_logger().debug("Install package '%s' from container '%s'" % (package_name, self.id))
1091
1092@@ -86,7 +63,7 @@
1093 utils.get_logger().debug("Install already in progress for '%s':'%s'" % (package_name, self.id))
1094 return tasks[0].id
1095
1096- task = InstallTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task)
1097+ task = InstallTask(package_name, self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
1098 self._tasks.append(task)
1099 task.start()
1100 return task.id
1101@@ -99,7 +76,7 @@
1102 utils.get_logger().debug("Remove already in progress for '%s':'%s'" % (package_name, self.id))
1103 return tasks[0].id
1104
1105- task = RemoveTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task)
1106+ task = RemoveTask(package_name, self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
1107 self._tasks.append(task)
1108 task.start()
1109 return task.id
1110@@ -113,7 +90,7 @@
1111 return tasks[0].id
1112
1113 task = CreateTask(self.id, container_name, distro, container_type, enable_multiarch,
1114- self._config, self._lock, self._connection, self._cleanup_task)
1115+ self._config, self._lock, self._monitor, self._client, self._cleanup_task)
1116 self._tasks.append(task)
1117 task.start()
1118 return task.id
1119@@ -126,7 +103,7 @@
1120 utils.get_logger().debug("Destroy already in progress for '%s'" % self.id)
1121 return tasks[0].id
1122
1123- task = DestroyTask(self.id, self._config, self._lock, self._connection, self._cleanup_task)
1124+ task = DestroyTask(self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
1125 self._tasks.append(task)
1126 task.start()
1127 return task.id
1128@@ -139,15 +116,42 @@
1129 utils.get_logger().debug("Update already in progress for '%s'" % self.id)
1130 return tasks[0].id
1131
1132- task = UpdateTask(self.id, self._config, self._lock, self._connection, self._cleanup_task)
1133+ task = UpdateTask(self.id, self._config, self._lock, self._monitor, self._client, self._cleanup_task)
1134 self._tasks.append(task)
1135 task.start()
1136 return task.id
1137
1138+ # Tasks which don't require starting/stopping the container
1139+
1140 def list_app_ids(self):
1141 utils.get_logger().debug("List all app ids in container '%s'" % self.id)
1142
1143- task = ListAppIdsTask(self.id, self._config, self._connection, self._cleanup_task)
1144+ task = ListAppIdsTask(self.id, self._config, self._monitor, self._client, self._cleanup_task)
1145+
1146+ self._tasks.append(task)
1147+ task.start()
1148+ return task.id
1149+
1150+ def search(self, query):
1151+ utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query))
1152+
1153+ if utils.is_snap_environment():
1154+ raise Exception("This operation is not currently supported within the snap")
1155+
1156+ task = SearchTask(self.id, self._cache, query, self._monitor, self._cleanup_task)
1157+ self._tasks.append(task)
1158+ task.start()
1159+
1160+ return task.id
1161+
1162+ def app_info(self, package_name):
1163+ utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id))
1164+
1165+ if utils.is_snap_environment():
1166+ raise Exception("This operation is not currently supported within the snap")
1167+
1168+ related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running]
1169+ task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._monitor, self._cleanup_task)
1170
1171 self._tasks.append(task)
1172 task.start()
1173
1174=== renamed file 'python/libertine/service/operations_state.py' => 'python/libertine/service/container_control.py'
1175--- python/libertine/service/operations_state.py 2017-02-23 15:25:17 +0000
1176+++ python/libertine/service/container_control.py 2017-03-31 20:05:48 +0000
1177@@ -12,64 +12,35 @@
1178 # You should have received a copy of the GNU General Public License
1179 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1180
1181-import libertine.ContainersConfig
1182-import psutil
1183-
1184-from collections import Counter
1185+
1186+import dbus
1187+
1188+from . import constants
1189 from libertine import utils
1190
1191
1192-class OperationsState(object):
1193- def __init__(self):
1194- self._get_running_apps_per_container()
1195-
1196- def _get_running_apps_per_container(self):
1197- self._invalid_apps = dict()
1198- self._operations = Counter()
1199- config = libertine.ContainersConfig.ContainersConfig()
1200-
1201- for container in config.get_containers():
1202- running_apps = config.get_running_apps(container).copy()
1203-
1204- for app in running_apps:
1205- try:
1206- proc = psutil.Process(app['pid'])
1207- if app['appExecName'] in proc.cmdline():
1208- self._operations[container] += 1
1209- else:
1210- raise
1211- except:
1212- utils.get_logger().error("Container app {} is not valid.".format(app['appExecName']))
1213- if container not in self._invalid_apps:
1214- self._invalid_apps[container] = [{app['appExecName'], app['pid']}]
1215- else:
1216- self._invalid_apps[container].append({app['appExecName'], app['pid']})
1217- config.delete_running_app(container, app)
1218- continue
1219-
1220- def operation_start(self, container):
1221- if self._operations[container] == -1:
1222- return False
1223-
1224- self._operations[container] += 1
1225-
1226- return True
1227-
1228- def operation_finished(self, container, app_name, pid):
1229- if container in self._invalid_apps and {app_name, pid} in self._invalid_apps[container]:
1230- self._invalid_apps[container].remove({app_name, pid})
1231- if not self._invalid_apps[container]:
1232- del self._invalid_apps[container]
1233- else:
1234- self._operations[container] -= 1
1235-
1236- if self._operations[container] == 0:
1237- self._operations[container] = -1
1238- return True
1239-
1240- return False
1241-
1242- def operation_stopped(self, container):
1243- del self._operations[container]
1244-
1245- return True
1246+class ContainerControl(dbus.service.Object):
1247+ def __init__(self, connection, client):
1248+ dbus.service.Object.__init__(self, conn=connection, object_path=constants.CONTAINER_CONTROL_OBJECT)
1249+ self._client = client
1250+
1251+ @dbus.service.method(constants.CONTAINER_CONTROL_INTERFACE,
1252+ in_signature='s',
1253+ out_signature='b')
1254+ def start(self, container):
1255+ utils.get_logger().debug("start({})".format(container))
1256+ return self._client.container_operation_start(container)
1257+
1258+ @dbus.service.method(constants.CONTAINER_CONTROL_INTERFACE,
1259+ in_signature='ssi',
1260+ out_signature='b')
1261+ def finished(self, container, app_name, pid):
1262+ utils.get_logger().debug("finished({})".format(container))
1263+ return self._client.container_operation_finished(container, app_name, pid)
1264+
1265+ @dbus.service.method(constants.CONTAINER_CONTROL_INTERFACE,
1266+ in_signature='s',
1267+ out_signature='b')
1268+ def stopped(self, container):
1269+ utils.get_logger().debug("stopped({})".format(container))
1270+ return self._client.container_stopped(container)
1271
1272=== added file 'python/libertine/service/container_control_client.py'
1273--- python/libertine/service/container_control_client.py 1970-01-01 00:00:00 +0000
1274+++ python/libertine/service/container_control_client.py 2017-03-31 20:05:48 +0000
1275@@ -0,0 +1,75 @@
1276+# Copyright 2017 Canonical Ltd.
1277+#
1278+# This program is free software: you can redistribute it and/or modify
1279+# it under the terms of the GNU General Public License as published by
1280+# the Free Software Foundation; version 3 of the License.
1281+#
1282+# This program is distributed in the hope that it will be useful,
1283+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1284+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1285+# GNU General Public License for more details.
1286+#
1287+# You should have received a copy of the GNU General Public License
1288+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1289+
1290+
1291+import libertine.ContainersConfig
1292+import psutil
1293+
1294+from collections import Counter
1295+from libertine import utils
1296+
1297+
1298+class ContainerControlClient(object):
1299+ def __init__(self):
1300+ self._get_running_apps_per_container()
1301+
1302+ def _get_running_apps_per_container(self):
1303+ self._invalid_apps = dict()
1304+ self._operations = Counter()
1305+ config = libertine.ContainersConfig.ContainersConfig()
1306+
1307+ for container in config.get_containers():
1308+ running_apps = config.get_running_apps(container).copy()
1309+
1310+ for app in running_apps:
1311+ try:
1312+ proc = psutil.Process(app['pid'])
1313+ if app['appExecName'] in proc.cmdline():
1314+ self._operations[container] += 1
1315+ else:
1316+ raise
1317+ except:
1318+ utils.get_logger().error("Container app {} is not valid.".format(app['appExecName']))
1319+ if container not in self._invalid_apps:
1320+ self._invalid_apps[container] = [{app['appExecName'], app['pid']}]
1321+ else:
1322+ self._invalid_apps[container].append({app['appExecName'], app['pid']})
1323+ config.delete_running_app(container, app)
1324+ continue
1325+
1326+ def container_operation_start(self, container):
1327+ if self._operations[container] == -1:
1328+ return False
1329+
1330+ self._operations[container] += 1
1331+
1332+ return True
1333+
1334+ def container_operation_finished(self, container, app_name, pid):
1335+ if container in self._invalid_apps and {app_name, pid} in self._invalid_apps[container]:
1336+ self._invalid_apps[container].remove({app_name, pid})
1337+ if not self._invalid_apps[container]:
1338+ del self._invalid_apps[container]
1339+ else:
1340+ self._operations[container] -= 1
1341+
1342+ if self._operations[container] == 0:
1343+ self._operations[container] = -1
1344+ return True
1345+
1346+ return False
1347+
1348+ def container_stopped(self, container):
1349+ del self._operations[container]
1350+ return True
1351
1352=== renamed file 'python/libertine/service/progress.py' => 'python/libertine/service/download.py'
1353--- python/libertine/service/progress.py 2017-01-24 18:00:57 +0000
1354+++ python/libertine/service/download.py 2017-03-31 20:05:48 +0000
1355@@ -1,4 +1,4 @@
1356-# Copyright 2016-2017 Canonical Ltd.
1357+# Copyright 2017 Canonical Ltd.
1358 #
1359 # This program is free software: you can redistribute it and/or modify
1360 # it under the terms of the GNU General Public License as published by
1361@@ -12,73 +12,68 @@
1362 # You should have received a copy of the GNU General Public License
1363 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1364
1365+
1366 import dbus.service
1367 import threading
1368+
1369+from . import constants
1370 from libertine import utils
1371 from time import time
1372
1373-DOWNLOAD_INTERFACE = "com.canonical.applications.Download"
1374-PROGRESS_INTERFACE = "com.canonical.libertine.Service.Progress"
1375
1376-class Progress(dbus.service.Object):
1377- def __init__(self, connection):
1378- utils.get_logger().debug("creating a Progress object")
1379+class Download(dbus.service.Object):
1380+ def __init__(self, connection, id):
1381 self._finished = False
1382- self._result = []
1383+ self._result = ''
1384 self._error = ''
1385- dbus.service.Object.__init__(self, conn=connection, object_path=("/Progress/%s" % hex(int(time()*10000000))[2:]))
1386-
1387- # self.emit_processing() # Disabled until something requires the Download interface
1388-
1389- @property
1390- def id(self):
1391- return self._object_path
1392-
1393+ dbus.service.Object.__init__(self, conn=connection, object_path=(constants.DOWNLOAD_OBJECT % id))
1394+
1395+ # Disabled until something requires the Download interface
1396+ # self.emit_processing()
1397+
1398+ # This is currently how services using the Ubuntu SDK to show progress
1399+ # determine whether or not an operation is running.
1400 def emit_processing(self):
1401 if not self.done:
1402 self.processing(self.id)
1403 threading.Timer(0.5, self.emit_processing).start()
1404
1405 @property
1406+ def id(self):
1407+ return self._object_path
1408+
1409+ @property
1410 def done(self):
1411 return self._finished
1412
1413- @dbus.service.signal(DOWNLOAD_INTERFACE)
1414+ @property
1415+ def result(self):
1416+ return self._result.strip()
1417+
1418+ @property
1419+ def last_error(self):
1420+ return self._error
1421+
1422+ def data(self, message):
1423+ self._result += message + '\n'
1424+
1425+ # Signals to satisfy the download interface
1426+
1427+ @dbus.service.signal(constants.DOWNLOAD_INTERFACE, signature='o')
1428 def processing(self, path):
1429 utils.get_logger().debug("emit processing('%s')" % path)
1430
1431- @dbus.service.signal(DOWNLOAD_INTERFACE)
1432+ @dbus.service.signal(constants.DOWNLOAD_INTERFACE, signature='o')
1433 def finished(self, path):
1434 utils.get_logger().debug("emit finished('%s')" % path)
1435 self._finished = True
1436
1437- @dbus.service.signal(DOWNLOAD_INTERFACE)
1438+ @dbus.service.signal(constants.DOWNLOAD_INTERFACE)
1439 def progress(self, received, total):
1440 utils.get_logger().debug("emit progress(%d, %d)" % (received, total))
1441
1442- @dbus.service.signal(DOWNLOAD_INTERFACE)
1443+ @dbus.service.signal(constants.DOWNLOAD_INTERFACE, signature='s')
1444 def error(self, message):
1445 utils.get_logger().error("emit error(%s)" % message)
1446 self._error = message
1447 self._finished = True
1448-
1449- @dbus.service.signal(PROGRESS_INTERFACE)
1450- def data(self, message):
1451- utils.get_logger().debug("emit data(%s)" % message)
1452- self._result.append(message)
1453-
1454- @dbus.service.method(PROGRESS_INTERFACE, out_signature='b')
1455- def running(self):
1456- utils.get_logger().debug(not self.done)
1457- return not self.done
1458-
1459- @dbus.service.method(PROGRESS_INTERFACE, out_signature='s')
1460- def result(self):
1461- full_result = "\n".join(self._result)
1462- utils.get_logger().debug(full_result)
1463- return full_result
1464-
1465- @dbus.service.method(PROGRESS_INTERFACE, out_signature='s')
1466- def last_error(self):
1467- utils.get_logger().debug(self._error)
1468- return self._error
1469
1470=== renamed file 'python/libertine/service/manager.py' => 'python/libertine/service/operations.py'
1471--- python/libertine/service/manager.py 2017-02-21 18:20:21 +0000
1472+++ python/libertine/service/operations.py 2017-03-31 20:05:48 +0000
1473@@ -12,69 +12,51 @@
1474 # You should have received a copy of the GNU General Public License
1475 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1476
1477+
1478 import dbus
1479 import dbus.service
1480-import libertine.service.task_dispatcher
1481-import libertine.service.operations_state
1482-from dbus.mainloop.glib import DBusGMainLoop
1483-from libertine.service import container
1484+
1485+from . import constants, operations_monitor, task_dispatcher
1486 from libertine import utils
1487
1488
1489-LIBERTINE_MANAGER_NAME = "com.canonical.libertine.Service"
1490-LIBERTINE_MANAGER_INTERFACE = LIBERTINE_MANAGER_NAME
1491-LIBERTINE_MANAGER_OBJECT = "/Manager"
1492-
1493-
1494-class Manager(dbus.service.Object):
1495- def __init__(self):
1496- utils.get_logger().debug("creating service")
1497- DBusGMainLoop(set_as_default=True)
1498- self._operations_state = libertine.service.operations_state.OperationsState()
1499-
1500- try:
1501- bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME,
1502- bus=dbus.SessionBus(),
1503- do_not_queue=True)
1504- except dbus.exceptions.NameExistsException:
1505- utils.get_logger().warning("service is already running")
1506- raise
1507-
1508- super().__init__(bus_name, LIBERTINE_MANAGER_OBJECT)
1509-
1510- self._dispatcher = libertine.service.task_dispatcher.TaskDispatcher(self.connection)
1511+class Operations(dbus.service.Object):
1512+ def __init__(self, bus_name, client):
1513+ super().__init__(bus_name, constants.OPERATIONS_OBJECT)
1514+
1515+ self._dispatcher = task_dispatcher.TaskDispatcher(operations_monitor.OperationsMonitor(self.connection), client)
1516
1517 # Information
1518
1519- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1520+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1521 in_signature='ss',
1522 out_signature='o')
1523 def search(self, container_id, search_string):
1524 utils.get_logger().debug("search('{}', '{}') called".format(container_id, search_string))
1525 return self._dispatcher.search(container_id, search_string)
1526
1527- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1528+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1529 in_signature='ss',
1530 out_signature='o')
1531 def app_info(self, container_id, app_id):
1532 utils.get_logger().debug("app_info('{}', '{}') called".format(container_id, app_id))
1533 return self._dispatcher.app_info(container_id, app_id)
1534
1535- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1536+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1537 in_signature='s',
1538 out_signature='o')
1539 def container_info(self, container_id):
1540 utils.get_logger().debug("container_info('{}')".format(container_id))
1541 return self._dispatcher.container_info(container_id)
1542
1543- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1544+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1545 in_signature='s',
1546 out_signature='o')
1547 def list_app_ids(self, container_id):
1548 utils.get_logger().debug("list_app_ids('{}')".format(container_id))
1549 return self._dispatcher.list_app_ids(container_id)
1550
1551- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1552+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1553 out_signature='o')
1554 def list(self):
1555 utils.get_logger().debug("list()")
1556@@ -82,63 +64,37 @@
1557
1558 # Operations
1559
1560- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1561+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1562 in_signature='ssssb',
1563 out_signature='o')
1564 def create(self, container_id, container_name='', distro='', container_type='', enable_multiarch=False):
1565 utils.get_logger().debug("create('{}', '{}', '{}', '{}', '{}')".format(container_id, container_name, distro, container_type, enable_multiarch))
1566 return self._dispatcher.create(container_id, container_name, distro, container_type, enable_multiarch)
1567
1568- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1569+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1570 in_signature='s',
1571 out_signature='o')
1572 def destroy(self, container_id):
1573 utils.get_logger().debug("destroy('{}')".format(container_id))
1574 return self._dispatcher.destroy(container_id)
1575
1576- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1577+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1578 in_signature='s',
1579 out_signature='o')
1580 def update(self, container_id):
1581 utils.get_logger().debug("update('{}')".format(container_id))
1582 return self._dispatcher.update(container_id)
1583
1584- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1585+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1586 in_signature='ss',
1587 out_signature='o')
1588 def install(self, container_id, package_name):
1589 utils.get_logger().debug("install('%s', '%s')" % (container_id, package_name))
1590 return self._dispatcher.install(container_id, package_name)
1591
1592- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1593+ @dbus.service.method(constants.OPERATIONS_INTERFACE,
1594 in_signature='ss',
1595 out_signature='o')
1596 def remove(self, container_id, package_name):
1597 utils.get_logger().debug("remove('%s', '%s')" % (container_id, package_name))
1598 return self._dispatcher.remove(container_id, package_name)
1599-
1600- # Container Lifecycle
1601-
1602- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1603- in_signature='s',
1604- out_signature='b')
1605- def container_operation_start(self, container):
1606- utils.get_logger().debug("container_operation_start({})".format(container))
1607-
1608- return self._operations_state.operation_start(container)
1609-
1610- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1611- in_signature='ssi',
1612- out_signature='b')
1613- def container_operation_finished(self, container, app_name='', pid=0):
1614- utils.get_logger().debug("container_operation_finished({})".format(container))
1615-
1616- return self._operations_state.operation_finished(container, app_name, pid)
1617-
1618- @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
1619- in_signature='s',
1620- out_signature='b')
1621- def container_stopped(self, container):
1622- utils.get_logger().debug("container_stopped({})".format(container))
1623-
1624- return self._operations_state.operation_stopped(container)
1625
1626=== added file 'python/libertine/service/operations_monitor.py'
1627--- python/libertine/service/operations_monitor.py 1970-01-01 00:00:00 +0000
1628+++ python/libertine/service/operations_monitor.py 2017-03-31 20:05:48 +0000
1629@@ -0,0 +1,93 @@
1630+# Copyright 2017 Canonical Ltd.
1631+#
1632+# This program is free software: you can redistribute it and/or modify
1633+# it under the terms of the GNU General Public License as published by
1634+# the Free Software Foundation; version 3 of the License.
1635+#
1636+# This program is distributed in the hope that it will be useful,
1637+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1638+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1639+# GNU General Public License for more details.
1640+#
1641+# You should have received a copy of the GNU General Public License
1642+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1643+
1644+
1645+import dbus.service
1646+import threading
1647+import uuid
1648+
1649+from . import constants, download
1650+from libertine import utils
1651+from time import time
1652+
1653+
1654+class OperationsMonitor(dbus.service.Object):
1655+ def __init__(self, connection):
1656+ self._operations = []
1657+ dbus.service.Object.__init__(self, conn=connection, object_path=constants.OPERATIONS_MONITOR_OBJECT)
1658+
1659+ def new_operation(self):
1660+ self._operations.append(download.Download(self.connection, str(uuid.uuid4().fields[-1])))
1661+ return self._operations[-1].id
1662+
1663+ def remove_from_connection(self, path):
1664+ operation = self._operation(path)
1665+ self._operations.remove(operation)
1666+ operation.remove_from_connection()
1667+
1668+ def done(self, path):
1669+ op = self._operation(path)
1670+ if op:
1671+ return op.done
1672+ else:
1673+ return False
1674+
1675+ def _operation(self, path):
1676+ operations = [op for op in self._operations if op.id == path]
1677+ if not operations:
1678+ return None
1679+
1680+ return operations[0]
1681+
1682+ @dbus.service.signal(constants.OPERATIONS_MONITOR_INTERFACE, signature='o')
1683+ def finished(self, path):
1684+ op = self._operation(path)
1685+ if op:
1686+ op.finished(path)
1687+
1688+ @dbus.service.signal(constants.OPERATIONS_MONITOR_INTERFACE, signature='os')
1689+ def error(self, path, message):
1690+ op = self._operation(path)
1691+ if op:
1692+ op.error(message)
1693+
1694+ @dbus.service.signal(constants.OPERATIONS_MONITOR_INTERFACE, signature='os')
1695+ def data(self, path, message):
1696+ op = self._operation(path)
1697+ if op:
1698+ op.data(message)
1699+
1700+ @dbus.service.method(constants.OPERATIONS_MONITOR_INTERFACE, in_signature='o', out_signature='b')
1701+ def running(self, path):
1702+ op = self._operation(path)
1703+ if op:
1704+ return not op.done
1705+ else:
1706+ return False
1707+
1708+ @dbus.service.method(constants.OPERATIONS_MONITOR_INTERFACE, in_signature='o', out_signature='s')
1709+ def result(self, path):
1710+ op = self._operation(path)
1711+ if op:
1712+ return op.result
1713+ else:
1714+ return ''
1715+
1716+ @dbus.service.method(constants.OPERATIONS_MONITOR_INTERFACE, in_signature='o', out_signature='s')
1717+ def last_error(self, path):
1718+ op = self._operation(path)
1719+ if op:
1720+ return op.last_error
1721+ else:
1722+ return ''
1723
1724=== modified file 'python/libertine/service/task_dispatcher.py'
1725--- python/libertine/service/task_dispatcher.py 2017-02-23 20:47:39 +0000
1726+++ python/libertine/service/task_dispatcher.py 2017-03-31 20:05:48 +0000
1727@@ -16,22 +16,13 @@
1728 import libertine.ContainersConfig
1729 from libertine.service.container import Container
1730 from libertine.service.tasks import *
1731-from threading import Lock
1732 from libertine import utils
1733
1734
1735-# Decorator to refresh database before making a call
1736-def _refresh_config(func):
1737- def wrapper(*args, **kwargs):
1738- args[0]._config.refresh_database()
1739- return func(*args, **kwargs)
1740-
1741- return wrapper
1742-
1743-
1744 class TaskDispatcher(object):
1745- def __init__(self, connection):
1746- self._connection = connection
1747+ def __init__(self, monitor, client):
1748+ self._monitor = monitor
1749+ self._client = client
1750 self._config = libertine.ContainersConfig.ContainersConfig()
1751 self._containerless_tasks = []
1752 self._tasks = []
1753@@ -49,11 +40,13 @@
1754
1755 def _find_or_create_container(self, container_id):
1756 utils.get_logger().debug("finding or creating container '%s'" % container_id)
1757+
1758 container = self._find_container(container_id)
1759 if container is not None:
1760 utils.get_logger().debug("using existing container '%s'" % container_id)
1761 return container
1762- container = Container(container_id, self._config, self._connection, self._cleanup_container)
1763+
1764+ container = Container(container_id, self._config, self._monitor, self._client, self._cleanup_container)
1765 self._containers.append(container)
1766
1767 return container
1768@@ -65,49 +58,40 @@
1769
1770 # Tasks (usually) run within a container
1771
1772- @_refresh_config
1773 def search(self, container_id, query):
1774 utils.get_logger().debug("dispatching search in container '%s' for package '%s'" % (container_id, query))
1775 return self._find_or_create_container(container_id).search(query)
1776
1777- @_refresh_config
1778 def app_info(self, container_id, app_id):
1779 utils.get_logger().debug("dispatching app_info in container '%s' for package '%s'" % (container_id, app_id))
1780 return self._find_or_create_container(container_id).app_info(app_id)
1781
1782- @_refresh_config
1783 def install(self, container_id, package_name):
1784 utils.get_logger().debug("dispatching install of package '%s' from container '%s'" % (package_name, container_id))
1785 return self._find_or_create_container(container_id).install(package_name)
1786
1787- @_refresh_config
1788 def remove(self, container_id, package_name):
1789 utils.get_logger().debug("dispatching remove of package '%s' from container '%s'" % (package_name, container_id))
1790 return self._find_or_create_container(container_id).remove(package_name)
1791
1792- @_refresh_config
1793 def create(self, container_id, container_name, distro, container_type, enable_multiarch):
1794 utils.get_logger().debug("dispatching create of container '%s'" % container_id)
1795 return self._find_or_create_container(container_id).create(container_name, distro, container_type, enable_multiarch)
1796
1797- @_refresh_config
1798 def destroy(self, container_id):
1799 utils.get_logger().debug("dispatching destroy container '%s'" % container_id)
1800 return self._find_or_create_container(container_id).destroy()
1801
1802- @_refresh_config
1803 def update(self, container_id):
1804 utils.get_logger().debug("dispatching update container '%s'" % container_id)
1805 return self._find_or_create_container(container_id).update()
1806
1807- @_refresh_config
1808 def list_app_ids(self, container_id):
1809 utils.get_logger().debug("dispatching list apps ids in container '%s'" % container_id)
1810 return self._find_or_create_container(container_id).list_app_ids()
1811
1812 # Containerless Tasks
1813
1814- @_refresh_config
1815 def container_info(self, container_id):
1816 utils.get_logger().debug("dispatching get info for container '%s'" % container_id)
1817
1818@@ -115,17 +99,16 @@
1819 container = self._find_container(container_id)
1820 if container is not None:
1821 related_task_ids = container.tasks
1822- task = ContainerInfoTask(container_id, related_task_ids, self._config, self._connection, self._cleanup_task)
1823+ task = ContainerInfoTask(container_id, related_task_ids, self._config, self._monitor, self._cleanup_task)
1824 self._tasks.append(task)
1825 task.start()
1826
1827 return task.id
1828
1829- @_refresh_config
1830 def list(self):
1831 utils.get_logger().debug("dispatching list all containers")
1832
1833- task = ListTask(self._config, self._connection, self._cleanup_task)
1834+ task = ListTask(self._config, self._monitor, self._cleanup_task)
1835 self._tasks.append(task)
1836 task.start()
1837
1838
1839=== modified file 'python/libertine/service/tasks/__init__.py'
1840--- python/libertine/service/tasks/__init__.py 2017-02-07 12:35:48 +0000
1841+++ python/libertine/service/tasks/__init__.py 2017-03-31 20:05:48 +0000
1842@@ -12,7 +12,7 @@
1843 # You should have received a copy of the GNU General Public License along
1844 # with this program. If not, see <http://www.gnu.org/licenses/>.
1845
1846-from .base_task import BaseTask
1847+from .base_task import BaseTask, ContainerBaseTask
1848 from .app_info_task import AppInfoTask
1849 from .container_info_task import ContainerInfoTask
1850 from .create_task import CreateTask
1851@@ -27,6 +27,7 @@
1852 __all__ = [
1853 'AppInfoTask',
1854 'BaseTask',
1855+ 'ContainerBaseTask',
1856 'ContainerInfoTask',
1857 'CreateTask',
1858 'DestroyTask',
1859
1860=== modified file 'python/libertine/service/tasks/app_info_task.py'
1861--- python/libertine/service/tasks/app_info_task.py 2016-11-01 17:38:38 +0000
1862+++ python/libertine/service/tasks/app_info_task.py 2017-03-31 20:05:48 +0000
1863@@ -1,4 +1,4 @@
1864-# Copyright 2016 Canonical Ltd.
1865+# Copyright 2016-2017 Canonical Ltd.
1866 #
1867 # This program is free software: you can redistribute it and/or modify
1868 # it under the terms of the GNU General Public License as published by
1869@@ -18,8 +18,8 @@
1870
1871
1872 class AppInfoTask(BaseTask):
1873- def __init__(self, container_id, cache, app_id, tasks, config, connection, callback):
1874- super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
1875+ def __init__(self, container_id, cache, app_id, tasks, config, monitor, callback):
1876+ super().__init__(lock=None, container_id=container_id, config=config, monitor=monitor, callback=callback)
1877 self._cache = cache
1878 self._app_id = app_id
1879 self._tasks = tasks
1880@@ -27,9 +27,9 @@
1881 def _run(self):
1882 app = self._cache.app_info(self._app_id)
1883 if app == {}:
1884- self._progress.error("Could not find app info for '%s' in container '%s'" % (self._app_id, self._container))
1885+ self._error("Could not find app info for '%s' in container '%s'" % (self._app_id, self._container))
1886 return
1887
1888 app['status'] = self._config.get_package_install_status(self._container, app['package']) or ''
1889 app['task_ids'] = self._tasks
1890- self._progress.data(str(app))
1891+ self._data(str(app))
1892
1893=== modified file 'python/libertine/service/tasks/base_task.py'
1894--- python/libertine/service/tasks/base_task.py 2017-02-17 17:39:13 +0000
1895+++ python/libertine/service/tasks/base_task.py 2017-03-31 20:05:48 +0000
1896@@ -12,8 +12,9 @@
1897 # You should have received a copy of the GNU General Public License
1898 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1899
1900-import libertine.service.progress
1901+
1902 import threading
1903+
1904 from abc import ABCMeta, abstractmethod
1905
1906
1907@@ -24,24 +25,21 @@
1908 in a separate thread. Override _before to implement pre-execution actions
1909 without locking; if _before returns False, _run will not be executed.
1910 """
1911- def __init__(self, lock, container_id, config, connection, callback):
1912+ def __init__(self, lock, container_id, config, monitor, callback):
1913 self._lock = lock
1914 self._container = container_id
1915 self._config = config
1916 self._callback = callback
1917- self._connection = connection
1918- self._progress = None
1919- self._instant_callback = False
1920+ self._monitor = monitor
1921+ self._operation_id = None
1922+ self._instant_callback = False # for testing
1923
1924 def matches(self, container, klass):
1925 return self._container == container and self.__class__ == klass
1926
1927 @property
1928 def id(self):
1929- if self._progress is not None:
1930- return self._progress.id
1931- else:
1932- return None
1933+ return self._operation_id
1934
1935 @property
1936 def container(self):
1937@@ -53,40 +51,67 @@
1938
1939 @property
1940 def running(self):
1941- return not self._progress.done
1942+ return not self._monitor.done(self._operation_id)
1943
1944 def _delayed_callback(self):
1945 if self._instant_callback:
1946 self._callback(self)
1947 else:
1948- threading.Timer(10, lambda: (self._progress.remove_from_connection(), self._callback(self))).start()
1949+ threading.Timer(10, lambda: (self._monitor.remove_from_connection(self._operation_id), self._callback(self))).start()
1950
1951 def start(self):
1952- self._progress = libertine.service.progress.Progress(self._connection)
1953+ self._operation_id = self._monitor.new_operation()
1954 thread = threading.Thread(target=self.run)
1955 thread.start()
1956 return thread
1957
1958 def run(self):
1959+ self._refresh_database()
1960+
1961 if not self._before():
1962- self._progress.finished(self.container)
1963+ self._monitor.finished(self._operation_id)
1964 self._delayed_callback()
1965 return
1966
1967 if self._lock is not None:
1968 with self._lock:
1969+ self._refresh_database(False)
1970 self._run()
1971 else:
1972+ self._refresh_database()
1973 self._run()
1974
1975 if self.running:
1976- self._progress.finished(self.container)
1977+ self._finished()
1978
1979 self._delayed_callback()
1980
1981+ def _refresh_database(self, require_lock=True):
1982+ if self._config:
1983+ if require_lock and self._lock is not None:
1984+ with self._lock:
1985+ self._config.refresh_database()
1986+ else:
1987+ self._config.refresh_database()
1988+
1989 @abstractmethod
1990 def _run(self):
1991 pass
1992
1993 def _before(self):
1994 return True
1995+
1996+ def _data(self, message):
1997+ self._monitor.data(self._operation_id, message)
1998+
1999+ def _finished(self):
2000+ self._monitor.finished(self._operation_id)
2001+
2002+ def _error(self, message):
2003+ self._monitor.error(self._operation_id, message)
2004+
2005+
2006+class ContainerBaseTask(BaseTask):
2007+ def __init__(self, lock, container_id, config, monitor, client, callback):
2008+ super().__init__(lock=lock, container_id=container_id, config=config, monitor=monitor, callback=callback)
2009+ self._client = client
2010
2011=== modified file 'python/libertine/service/tasks/container_info_task.py'
2012--- python/libertine/service/tasks/container_info_task.py 2017-01-18 20:08:47 +0000
2013+++ python/libertine/service/tasks/container_info_task.py 2017-03-31 20:05:48 +0000
2014@@ -20,8 +20,8 @@
2015
2016
2017 class ContainerInfoTask(BaseTask):
2018- def __init__(self, container_id, tasks, config, connection, callback):
2019- super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
2020+ def __init__(self, container_id, tasks, config, monitor, callback):
2021+ super().__init__(lock=None, container_id=container_id, config=config, monitor=monitor, callback=callback)
2022 self._tasks = tasks
2023
2024 def _run(self):
2025@@ -35,11 +35,11 @@
2026 container['root'] = utils.get_libertine_container_rootfs_path(self._container)
2027 container['home'] = utils.get_libertine_container_home_dir(self._container)
2028
2029- self._progress.data(json.dumps(container))
2030+ self._data(json.dumps(container))
2031
2032 def _before(self):
2033 if not self._config.container_exists(self._container):
2034- self._progress.error("Container '%s' does not exist, ignoring info request" % self._container)
2035+ self._error("Container '%s' does not exist, ignoring info request" % self._container)
2036 return False
2037
2038 return True
2039
2040=== modified file 'python/libertine/service/tasks/create_task.py'
2041--- python/libertine/service/tasks/create_task.py 2017-02-24 15:05:33 +0000
2042+++ python/libertine/service/tasks/create_task.py 2017-03-31 20:05:48 +0000
2043@@ -1,4 +1,4 @@
2044-# Copyright 2016 Canonical Ltd.
2045+# Copyright 2016-2017 Canonical Ltd.
2046 #
2047 # This program is free software: you can redistribute it and/or modify
2048 # it under the terms of the GNU General Public License as published by
2049@@ -13,14 +13,16 @@
2050 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2051
2052
2053-from .base_task import BaseTask
2054+from .base_task import ContainerBaseTask
2055 from libertine import LibertineContainer, utils
2056 from libertine.HostInfo import HostInfo
2057
2058
2059-class CreateTask(BaseTask):
2060- def __init__(self, container_id, container_name, distro, container_type, enable_multiarch, config, lock, connection, callback):
2061- super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
2062+class CreateTask(ContainerBaseTask):
2063+ def __init__(self, container_id, container_name, distro, container_type, enable_multiarch,
2064+ config, lock, monitor, client, callback):
2065+ super().__init__(lock=lock, container_id=container_id, config=config,
2066+ monitor=monitor, client=client, callback=callback)
2067 self._name = container_name
2068 self._distro = distro
2069 self._type = container_type
2070@@ -30,35 +32,36 @@
2071 utils.get_logger().debug("Creating container '%s'" % self._container)
2072
2073 try:
2074- container = LibertineContainer(self._container, self._config)
2075+ container = LibertineContainer(self._container, self._config, self._client)
2076
2077 if not container.create_libertine_container(password='', multiarch=self._multiarch):
2078 self._config.delete_container(self._container)
2079- self._progress.error("Creating container '%s' failed" % self._container)
2080+ self._error("Creating container '%s' failed" % self._container)
2081 else:
2082 self._config.update_container_install_status(self._container, "ready")
2083+ self._finished()
2084 except RuntimeError as e:
2085- self._progress.error(str(e))
2086 self._config.delete_container(self._container)
2087+ self._error(str(e))
2088
2089 def _before(self):
2090 utils.get_logger().debug("CreateTask::_before")
2091 if self._config.container_exists(self._container):
2092- self._progress.error("Container '%s' already exists" % self._container)
2093+ self._error("Container '%s' already exists" % self._container)
2094 return False
2095
2096 info = HostInfo()
2097 if not self._distro:
2098 self._distro = info.get_host_distro_release()
2099 elif not info.is_distro_valid(self._distro):
2100- self._progress.error("Invalid distro '%s'." % self._distro)
2101+ self._error("Invalid distro '%s'." % self._distro)
2102 return False
2103
2104 if not self._type:
2105 self._type = info.select_container_type_by_kernel()
2106 elif (self._type == 'lxd' and not info.has_lxd_support()) or \
2107 (self._type == 'lxc' and not info.has_lxc_support()):
2108- self._progress.error("System kernel does not support %s type containers. "
2109+ self._error("System kernel does not support %s type containers. "
2110 "Please either use chroot or leave empty." % self._type)
2111 return False
2112
2113
2114=== modified file 'python/libertine/service/tasks/destroy_task.py'
2115--- python/libertine/service/tasks/destroy_task.py 2016-11-01 17:38:38 +0000
2116+++ python/libertine/service/tasks/destroy_task.py 2017-03-31 20:05:48 +0000
2117@@ -1,4 +1,4 @@
2118-# Copyright 2016 Canonical Ltd.
2119+# Copyright 2016-2017 Canonical Ltd.
2120 #
2121 # This program is free software: you can redistribute it and/or modify
2122 # it under the terms of the GNU General Public License as published by
2123@@ -13,29 +13,31 @@
2124 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2125
2126
2127-from .base_task import BaseTask
2128+from .base_task import ContainerBaseTask
2129 from libertine import LibertineContainer, utils
2130
2131
2132-class DestroyTask(BaseTask):
2133- def __init__(self, container_id, config, lock, connection, callback):
2134- super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
2135+class DestroyTask(ContainerBaseTask):
2136+ def __init__(self, container_id, config, lock, monitor, client, callback):
2137+ super().__init__(lock=lock, container_id=container_id, config=config,
2138+ monitor=monitor, client=client, callback=callback)
2139
2140 def _run(self):
2141 utils.get_logger().debug("Destroying container '%s'" % self._container)
2142
2143- container = LibertineContainer(self._container, self._config)
2144+ container = LibertineContainer(self._container, self._config, self._client)
2145 if not container.destroy_libertine_container():
2146- self._progress.error("Destroying container '%s' failed" % self._container)
2147+ self._error("Destroying container '%s' failed" % self._container)
2148 self._config.update_container_install_status(self._container, "ready")
2149 return
2150
2151 self._config.delete_container(self._container)
2152+ self._finished()
2153
2154 def _before(self):
2155 utils.get_logger().debug("CreateTask::_before")
2156 if self._config._get_value_by_key(self._container, 'installStatus') != 'ready':
2157- self._progress.error("Container '%s' does not exist" % self._container)
2158+ self._error("Container '%s' does not exist" % self._container)
2159 return False
2160
2161 self._config.update_container_install_status(self._container, 'removing')
2162
2163=== modified file 'python/libertine/service/tasks/install_task.py'
2164--- python/libertine/service/tasks/install_task.py 2016-11-08 15:45:54 +0000
2165+++ python/libertine/service/tasks/install_task.py 2017-03-31 20:05:48 +0000
2166@@ -1,4 +1,4 @@
2167-# Copyright 2016 Canonical Ltd.
2168+# Copyright 2016-2017 Canonical Ltd.
2169 #
2170 # This program is free software: you can redistribute it and/or modify
2171 # it under the terms of the GNU General Public License as published by
2172@@ -13,13 +13,14 @@
2173 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2174
2175
2176-from .base_task import BaseTask
2177+from .base_task import ContainerBaseTask
2178 from libertine import LibertineContainer, utils
2179
2180
2181-class InstallTask(BaseTask):
2182- def __init__(self, package_name, container_id, config, lock, connection, callback):
2183- super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
2184+class InstallTask(ContainerBaseTask):
2185+ def __init__(self, package_name, container_id, config, lock, monitor, client, callback):
2186+ super().__init__(lock=lock, container_id=container_id, config=config,
2187+ monitor=monitor, client=client, callback=callback)
2188 self._package = package_name
2189
2190 def matches(self, package, klass):
2191@@ -31,17 +32,18 @@
2192
2193 def _run(self):
2194 utils.get_logger().debug("Installing package '%s'" % self._package)
2195- container = LibertineContainer(self._container, self._config)
2196+ container = LibertineContainer(self._container, self._config, self._client)
2197 if container.install_package(self._package):
2198 self._config.update_package_install_status(self._container, self._package, "installed")
2199+ self._finished()
2200 else:
2201 self._config.delete_package(self._container, self._package)
2202- self._progress.error("Package installation failed for '%s'" % self._package)
2203+ self._error("Package installation failed for '%s'" % self._package)
2204
2205 def _before(self):
2206 utils.get_logger().debug("InstallTask::_before")
2207 if self._config.package_exists(self._container, self._package):
2208- self._progress.error("Package '%s' already exists, skipping install" % self._package)
2209+ self._error("Package '%s' already exists, skipping install" % self._package)
2210 return False
2211 else:
2212 self._config.add_new_package(self._container, self._package)
2213
2214=== modified file 'python/libertine/service/tasks/list_app_ids_task.py'
2215--- python/libertine/service/tasks/list_app_ids_task.py 2017-01-20 15:20:13 +0000
2216+++ python/libertine/service/tasks/list_app_ids_task.py 2017-03-31 20:05:48 +0000
2217@@ -14,22 +14,22 @@
2218
2219
2220 import json
2221-from .base_task import BaseTask
2222+from .base_task import ContainerBaseTask
2223 from libertine import LibertineContainer, utils
2224 import time
2225
2226
2227-class ListAppIdsTask(BaseTask):
2228- def __init__(self, container_id, config, connection, callback):
2229- super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
2230+class ListAppIdsTask(ContainerBaseTask):
2231+ def __init__(self, container_id, config, monitor, client, callback):
2232+ super().__init__(lock=None, container_id=container_id, config=config, monitor=monitor, client=client, callback=callback)
2233
2234 def _run(self):
2235 utils.get_logger().debug("Listing app ids from container '%s'" % self._container)
2236- self._progress.data(json.dumps(LibertineContainer(self._container, self._config).list_app_ids()))
2237+ self._data(json.dumps(LibertineContainer(self._container, self._config, self._client).list_app_ids()))
2238
2239 def _before(self):
2240 if not self._config.container_exists(self._container):
2241- self._progress.error("Container '%s' does not exist, skipping list" % self._container)
2242+ self._error("Container '%s' does not exist, skipping list" % self._container)
2243 return False
2244
2245 return True
2246
2247=== modified file 'python/libertine/service/tasks/list_task.py'
2248--- python/libertine/service/tasks/list_task.py 2017-01-18 20:08:47 +0000
2249+++ python/libertine/service/tasks/list_task.py 2017-03-31 20:05:48 +0000
2250@@ -20,8 +20,9 @@
2251
2252
2253 class ListTask(BaseTask):
2254- def __init__(self, config, connection, callback):
2255- super().__init__(lock=None, container_id=None, config=config, connection=connection, callback=callback)
2256+ def __init__(self, config, monitor, callback):
2257+ super().__init__(lock=None, container_id=None, config=config, monitor=monitor, callback=callback)
2258
2259 def _run(self):
2260- self._progress.data(json.dumps(self._config.get_containers()))
2261+ self._data(json.dumps(self._config.get_containers()))
2262+ self._finished()
2263
2264=== modified file 'python/libertine/service/tasks/remove_task.py'
2265--- python/libertine/service/tasks/remove_task.py 2016-11-08 15:45:54 +0000
2266+++ python/libertine/service/tasks/remove_task.py 2017-03-31 20:05:48 +0000
2267@@ -1,4 +1,4 @@
2268-# Copyright 2016 Canonical Ltd.
2269+# Copyright 2016-2017 Canonical Ltd.
2270 #
2271 # This program is free software: you can redistribute it and/or modify
2272 # it under the terms of the GNU General Public License as published by
2273@@ -13,13 +13,13 @@
2274 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2275
2276
2277-from .base_task import BaseTask
2278+from .base_task import ContainerBaseTask
2279 from libertine import LibertineContainer, utils
2280
2281
2282-class RemoveTask(BaseTask):
2283- def __init__(self, package_name, container_id, config, lock, connection, callback):
2284- super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
2285+class RemoveTask(ContainerBaseTask):
2286+ def __init__(self, package_name, container_id, config, lock, monitor, client, callback):
2287+ super().__init__(lock=lock, container_id=container_id, config=config, monitor=monitor, client=client, callback=callback)
2288 self._package = package_name
2289
2290 def matches(self, package, klass):
2291@@ -31,12 +31,13 @@
2292
2293 def _run(self):
2294 utils.get_logger().debug("Removing package '%s'" % self._package)
2295- container = LibertineContainer(self._container, self._config)
2296+ container = LibertineContainer(self._container, self._config, self._client)
2297 if container.remove_package(self._package):
2298 self._config.delete_package(self._container, self._package)
2299+ self._finished()
2300 else:
2301 self._config.update_package_install_status(self._container, self._package, 'installed')
2302- self._progress.error("Package removal failed for '%s'" % self._package)
2303+ self._error("Package removal failed for '%s'" % self._package)
2304
2305 def _before(self):
2306 utils.get_logger().debug("RemoveTask::_before")
2307@@ -44,5 +45,5 @@
2308 self._config.update_package_install_status(self._container, self._package, "removing")
2309 return True
2310 else:
2311- self._progress.error("Package '%s' not installed, skipping remove" % self._package)
2312+ self._error("Package '%s' not installed, skipping remove" % self._package)
2313 return False
2314
2315=== modified file 'python/libertine/service/tasks/search_task.py'
2316--- python/libertine/service/tasks/search_task.py 2016-11-01 19:49:32 +0000
2317+++ python/libertine/service/tasks/search_task.py 2017-03-31 20:05:48 +0000
2318@@ -1,4 +1,4 @@
2319-# Copyright 2016 Canonical Ltd.
2320+# Copyright 2016-2017 Canonical Ltd.
2321 #
2322 # This program is free software: you can redistribute it and/or modify
2323 # it under the terms of the GNU General Public License as published by
2324@@ -18,10 +18,10 @@
2325
2326
2327 class SearchTask(BaseTask):
2328- def __init__(self, container_id, cache, query, connection, callback):
2329- super().__init__(lock=None, container_id=container_id, config=None, connection=connection, callback=callback)
2330+ def __init__(self, container_id, cache, query, monitor, callback):
2331+ super().__init__(lock=None, container_id=container_id, config=None, monitor=monitor, callback=callback)
2332 self._cache = cache
2333 self._query = query
2334
2335 def _run(self):
2336- self._progress.data(str(self._cache.search(self._query)))
2337+ self._data(str(self._cache.search(self._query)))
2338
2339=== modified file 'python/libertine/service/tasks/update_task.py'
2340--- python/libertine/service/tasks/update_task.py 2016-11-08 15:37:58 +0000
2341+++ python/libertine/service/tasks/update_task.py 2017-03-31 20:05:48 +0000
2342@@ -1,4 +1,4 @@
2343-# Copyright 2016 Canonical Ltd.
2344+# Copyright 2016-2017 Canonical Ltd.
2345 #
2346 # This program is free software: you can redistribute it and/or modify
2347 # it under the terms of the GNU General Public License as published by
2348@@ -13,27 +13,28 @@
2349 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2350
2351
2352-from .base_task import BaseTask
2353+from .base_task import ContainerBaseTask
2354 from libertine import LibertineContainer, utils
2355
2356
2357-class UpdateTask(BaseTask):
2358- def __init__(self, container_id, config, lock, connection, callback):
2359- super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
2360+class UpdateTask(ContainerBaseTask):
2361+ def __init__(self, container_id, config, lock, monitor, client, callback):
2362+ super().__init__(lock=lock, container_id=container_id, config=config,
2363+ monitor=monitor, client=client, callback=callback)
2364
2365 def _run(self):
2366 utils.get_logger().debug("Updating container '%s'" % self._container)
2367- container = LibertineContainer(self._container, self._config)
2368+ container = LibertineContainer(self._container, self._config, self._client)
2369 self._config.update_container_install_status(self._container, "updating")
2370 if not container.update_libertine_container():
2371- self._progress.error("Failed to update container '%s'" % self._container)
2372+ self._error("Failed to update container '%s'" % self._container)
2373
2374 self._config.update_container_install_status(self._container, "ready")
2375
2376 def _before(self):
2377 utils.get_logger().debug("UpdateTask::_before")
2378 if not self._config.container_exists(self._container):
2379- self._progress.error("Container '%s' does not exist, skipping update" % self._container)
2380+ self._error("Container '%s' does not exist, skipping update" % self._container)
2381 return False
2382- else:
2383- return True
2384+
2385+ return True
2386
2387=== modified file 'snap/plugins/x-libertine-deps.py'
2388--- snap/plugins/x-libertine-deps.py 2017-02-14 19:17:30 +0000
2389+++ snap/plugins/x-libertine-deps.py 2017-03-31 20:05:48 +0000
2390@@ -145,7 +145,8 @@
2391 '-usr/lib/{}/liblibertine.so*'.format(self._arch),
2392 '-usr/bin/libertine*',
2393 '-etc/sudoers.d/libertine*',
2394- '-usr/lib/python3/dist-packages/libertine'
2395+ '-usr/lib/python3/dist-packages/libertine',
2396+ '-usr/share/bash-completion/completions/*'
2397 ])
2398
2399 def build(self):
2400
2401=== modified file 'snap/snap-runner.wrapper' (properties changed: -x to +x)
2402=== modified file 'tests/integration/CMakeLists.txt'
2403--- tests/integration/CMakeLists.txt 2017-03-06 15:33:12 +0000
2404+++ tests/integration/CMakeLists.txt 2017-03-31 20:05:48 +0000
2405@@ -1,4 +1,24 @@
2406 add_test(test_libertine_service dbus-run-session -- /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/test_libertine_service.py)
2407 set_tests_properties(test_libertine_service
2408 PROPERTIES ENVIRONMENT
2409- "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;LIBERTINE_DATA_DIR=${CMAKE_CURRENT_SOURCE_DIR};PATH=${CMAKE_SOURCE_DIR}/tools:$ENV{PATH};")
2410+ "LIBERTINE_DEBUG=2;PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;;PATH=${CMAKE_SOURCE_DIR}/tools:$ENV{PATH};XDG_DATA_HOME=/tmp")
2411+
2412+add_executable(
2413+ test_liblibertine
2414+ test_liblibertine.cpp
2415+)
2416+
2417+target_link_libraries(
2418+ test_liblibertine
2419+ ${LIBERTINE_CORE}
2420+ gtest gtest_main
2421+ ${DBUSTEST_LIBRARIES}
2422+ ${GIO2_LIBRARIES}
2423+ Qt5::Core
2424+)
2425+
2426+add_test(test_liblibertine test_liblibertine)
2427+
2428+set_tests_properties(test_liblibertine
2429+ PROPERTIES ENVIRONMENT
2430+ "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")
2431
2432=== added directory 'tests/integration/data'
2433=== added directory 'tests/integration/data/libertine'
2434=== added directory 'tests/integration/data/libertine-container'
2435=== added directory 'tests/integration/data/libertine-container/padme'
2436=== added directory 'tests/integration/data/libertine-container/padme/rootfs'
2437=== added directory 'tests/integration/data/libertine-container/padme/rootfs/usr'
2438=== added directory 'tests/integration/data/libertine-container/padme/rootfs/usr/share'
2439=== added directory 'tests/integration/data/libertine-container/padme/rootfs/usr/share/applications'
2440=== added file 'tests/integration/data/libertine-container/padme/rootfs/usr/share/applications/dagobah.desktop'
2441=== added file 'tests/integration/data/libertine-container/padme/rootfs/usr/share/applications/tatooine.desktop'
2442=== added directory 'tests/integration/data/libertine-container/user-data'
2443=== added directory 'tests/integration/data/libertine-container/user-data/padme'
2444=== added file 'tests/integration/data/libertine/ContainersConfig.json'
2445--- tests/integration/data/libertine/ContainersConfig.json 1970-01-01 00:00:00 +0000
2446+++ tests/integration/data/libertine/ContainersConfig.json 2017-03-31 20:05:48 +0000
2447@@ -0,0 +1,19 @@
2448+{
2449+ "containerList": [
2450+ {
2451+ "id": "jarjar",
2452+ "name": "JarJar Binks",
2453+ "type": "mock"
2454+ },
2455+ {
2456+ "id": "padme",
2457+ "name": "Padme Amedala",
2458+ "type": "mock"
2459+ },
2460+ {
2461+ "id": "anakin",
2462+ "name": "Anakin Skywalker",
2463+ "type": "mock"
2464+ }
2465+ ]
2466+}
2467
2468=== modified file 'tests/integration/test_libertine_service.py'
2469--- tests/integration/test_libertine_service.py 2017-03-06 15:33:12 +0000
2470+++ tests/integration/test_libertine_service.py 2017-03-31 20:05:48 +0000
2471@@ -26,7 +26,7 @@
2472
2473 from gi.repository import GLib
2474 from libertine import utils
2475-from libertine.service import tasks, apt
2476+from libertine.service import tasks, apt, constants
2477 from libertine.ContainersConfig import ContainersConfig
2478 from subprocess import Popen, PIPE
2479 from unittest import TestCase
2480@@ -42,18 +42,14 @@
2481 def setUpClass(cls):
2482 cls._tempdir = tempfile.TemporaryDirectory()
2483
2484- environ = os.environ.copy()
2485- environ['XDG_DATA_HOME'] = cls._tempdir.name
2486-
2487- cls._process = pexpect.spawnu('libertined --debug', env=environ)
2488+ os.environ['XDG_DATA_HOME'] = cls._tempdir.name
2489+ cls._process = pexpect.spawnu('libertined --debug', env=os.environ.copy())
2490 cls._process.logfile = sys.stdout
2491
2492 # give libertined enough time to start the whole process
2493- verbosity = environ.get('LIBERTINE_DEBUG', '1')
2494- if verbosity == '1':
2495- cls._process.expect(['.+\n', pexpect.TIMEOUT], timeout=1)
2496- elif environ['LIBERTINE_DEBUG'] == '2':
2497- cls._process.expect(['.+\n.+\n.+\n', pexpect.TIMEOUT], timeout=1)
2498+ verbosity = os.environ.get('LIBERTINE_DEBUG', '1')
2499+ if verbosity != '0':
2500+ cls._process.expect(['libertined ready', pexpect.TIMEOUT], timeout=5)
2501
2502 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
2503 cls._loop = GLib.MainLoop()
2504@@ -68,14 +64,25 @@
2505 cls._tempdir.cleanup()
2506
2507 def setUp(self):
2508+ os.environ['XDG_DATA_HOME'] = TestLibertineService._tempdir.name
2509 self.error = None
2510 self.result = None
2511 self.event = threading.Event()
2512+ self.signals = []
2513
2514 for retries in range(1, 11):
2515 try:
2516 self._bus = dbus.SessionBus()
2517- self._libertined = self._bus.get_object('com.canonical.libertine.Service', '/Manager')
2518+ self.path = None
2519+
2520+ self._libertined = self._bus.get_object(constants.SERVICE_NAME, constants.OPERATIONS_OBJECT)
2521+
2522+ self.signals.append(self._bus.add_signal_receiver(path=constants.OPERATIONS_MONITOR_OBJECT, handler_function=self._finished_handler,
2523+ dbus_interface=constants.OPERATIONS_MONITOR_INTERFACE, signal_name='finished'))
2524+ self.signals.append(self._bus.add_signal_receiver(path=constants.OPERATIONS_MONITOR_OBJECT, handler_function=self._data_handler,
2525+ dbus_interface=constants.OPERATIONS_MONITOR_INTERFACE, signal_name='data'))
2526+ self.signals.append(self._bus.add_signal_receiver(path=constants.OPERATIONS_MONITOR_OBJECT, handler_function=self._error_handler,
2527+ dbus_interface=constants.OPERATIONS_MONITOR_INTERFACE, signal_name='error'))
2528 break
2529 except dbus.DBusException as e:
2530 print("Service not available (attempt %i/10). Exception: %s" % (retries, str(e)))
2531@@ -85,39 +92,35 @@
2532 except Exception as e:
2533 self.fail('Exception occurred during connection: %s' % str(e))
2534
2535+ def tearDown(self):
2536+ for signal in self.signals:
2537+ signal.remove()
2538+
2539 def _finished_handler(self, path):
2540- self.event.set()
2541-
2542- def _data_handler(self, message):
2543- self.result = message
2544-
2545- def _error_handler(self, message):
2546- self.error = message
2547- self.event.set()
2548+ if self.path == path:
2549+ self.event.set()
2550+
2551+ def _data_handler(self, path, message):
2552+ if self.path == path:
2553+ self.result = message
2554+
2555+ def _error_handler(self, path, message):
2556+ if self.path == path:
2557+ self.error = message
2558+ self.event.set()
2559
2560 def _send(self, func):
2561 self.event.clear()
2562 self.result = None
2563
2564- obj_path = func()
2565- signals = []
2566- signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._finished_handler,
2567- dbus_interface='com.canonical.applications.Download', signal_name='finished'))
2568- signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._data_handler,
2569- dbus_interface='com.canonical.libertine.Service.Progress', signal_name='data'))
2570- signals.append(self._bus.add_signal_receiver(path=obj_path, handler_function=self._error_handler,
2571- dbus_interface='com.canonical.applications.Download', signal_name='error'))
2572-
2573- task = self._bus.get_object('com.canonical.libertine.Service', obj_path)
2574- if task.running():
2575- self.event.wait(5)
2576- self.assertIsNone(self.error)
2577-
2578- self.assertEqual('', task.get_dbus_method('last_error', 'com.canonical.libertine.Service.Progress')())
2579- self.result = task.get_dbus_method('result', 'com.canonical.libertine.Service.Progress')()
2580-
2581- for signal in signals:
2582- self._bus._clean_up_signal_match(signal)
2583+ monitor = self._bus.get_object(constants.SERVICE_NAME, constants.OPERATIONS_MONITOR_OBJECT)
2584+ self.path = func()
2585+
2586+ while monitor.running(self.path):
2587+ self.event.wait(.1)
2588+
2589+ self.assertEqual('', monitor.last_error(self.path))
2590+ self.result = monitor.result(self.path)
2591
2592 return self.result
2593
2594
2595=== added file 'tests/integration/test_liblibertine.cpp'
2596--- tests/integration/test_liblibertine.cpp 1970-01-01 00:00:00 +0000
2597+++ tests/integration/test_liblibertine.cpp 2017-03-31 20:05:48 +0000
2598@@ -0,0 +1,145 @@
2599+/*
2600+ * Copyright 2017 Canonical Ltd.
2601+ *
2602+ * This program is free software: you can redistribute it and/or modify it under
2603+ * the terms of the GNU General Public License, version 3, as published by the
2604+ * Free Software Foundation.
2605+ *
2606+ * This program is distributed in the hope that it will be useful,
2607+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2608+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2609+ * GNU General Public License for more details.
2610+ *
2611+ * You should have received a copy of the GNU General Public License
2612+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2613+ */
2614+
2615+#include "liblibertine/libertine.h"
2616+#include <cstdlib>
2617+#include <gio/gio.h>
2618+#include <gtest/gtest.h>
2619+#include <libdbustest/dbus-test.h>
2620+#include <memory>
2621+#include <QtCore/QByteArray>
2622+#include <QtCore/QJsonDocument>
2623+#include <QtCore/QJsonParseError>
2624+
2625+class LiblibertineTest : public ::testing::Test
2626+{
2627+protected:
2628+ static void SetUpTestCase()
2629+ {
2630+ process = dbus_test_process_new("libertined");
2631+ dbus_test_process_append_param(process, "--debug");
2632+
2633+ dbus_test_task_set_bus(DBUS_TEST_TASK(process), DBUS_TEST_SERVICE_BUS_SESSION);
2634+ dbus_test_task_set_name(DBUS_TEST_TASK(process), "libertine");
2635+ dbus_test_task_set_return(DBUS_TEST_TASK(process), DBUS_TEST_TASK_RETURN_IGNORE);
2636+ dbus_test_task_set_wait_finished(DBUS_TEST_TASK(process), FALSE);
2637+
2638+ wait = dbus_test_task_new();
2639+ dbus_test_task_set_wait_for(wait, "com.canonical.libertine.Service");
2640+
2641+ service = dbus_test_service_new(nullptr);
2642+ dbus_test_service_add_task(service, DBUS_TEST_TASK(process));
2643+ dbus_test_service_add_task(service, wait);
2644+
2645+ dbus_test_service_start_tasks(service);
2646+
2647+ bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
2648+ g_dbus_connection_set_exit_on_close(bus, FALSE);
2649+ g_object_add_weak_pointer(G_OBJECT(bus), (gpointer*)&bus);
2650+ }
2651+
2652+ static void TearDownTestCase()
2653+ {
2654+ g_clear_object(&process);
2655+ g_clear_object(&wait);
2656+
2657+ g_clear_object(&service);
2658+ g_object_unref(bus);
2659+ }
2660+
2661+private:
2662+ static DbusTestProcess* process;
2663+ static DbusTestTask* wait;
2664+ static DbusTestService* service;
2665+ static GDBusConnection* bus;
2666+};
2667+
2668+
2669+DbusTestProcess* LiblibertineTest::process = nullptr;
2670+DbusTestTask* LiblibertineTest::wait = nullptr;
2671+DbusTestService* LiblibertineTest::service = nullptr;
2672+GDBusConnection* LiblibertineTest::bus = nullptr;
2673+
2674+
2675+TEST_F(LiblibertineTest, libertine_list_containers)
2676+{
2677+ auto scontainers = std::shared_ptr<gchar*>(libertine_list_containers(), g_strfreev);
2678+ auto containers = scontainers.get();
2679+
2680+ ASSERT_NE(containers[0], nullptr);
2681+ EXPECT_STREQ(containers[0], "jarjar");
2682+
2683+ ASSERT_NE(containers[1], nullptr);
2684+ EXPECT_STREQ(containers[1], "padme");
2685+
2686+ ASSERT_NE(containers[2], nullptr);
2687+ EXPECT_STREQ(containers[2], "anakin");
2688+
2689+ ASSERT_EQ(containers[3], nullptr);
2690+}
2691+
2692+
2693+TEST_F(LiblibertineTest, libertine_list_app_ids)
2694+{
2695+ auto sapps = std::shared_ptr<gchar*>(libertine_list_apps_for_container("padme"), g_strfreev);
2696+ auto apps = sapps.get();
2697+
2698+ ASSERT_NE(apps[0], nullptr);
2699+ EXPECT_STREQ(apps[0], "padme_dagobah_0.0");
2700+
2701+ ASSERT_NE(apps[1], nullptr);
2702+ EXPECT_STREQ(apps[1], "padme_tatooine_0.0");
2703+
2704+ ASSERT_EQ(apps[2], nullptr);
2705+}
2706+
2707+
2708+TEST_F(LiblibertineTest, libertine_container_name)
2709+{
2710+ auto actual = libertine_container_name("padme");
2711+ EXPECT_STREQ("Padme Amedala", actual);
2712+ g_free(actual);
2713+}
2714+
2715+
2716+TEST_F(LiblibertineTest, libertine_container_path)
2717+{
2718+ auto actual = libertine_container_path("padme");
2719+ auto expected = QString(getenv("XDG_CACHE_HOME")) + "/libertine-container/padme/rootfs";
2720+ EXPECT_STREQ(expected.toUtf8(), actual);
2721+ g_free(actual);
2722+}
2723+
2724+
2725+TEST_F(LiblibertineTest, libertine_container_path_returns_empty)
2726+{
2727+ EXPECT_EQ(nullptr, libertine_container_path("jarjar"));
2728+}
2729+
2730+
2731+TEST_F(LiblibertineTest, libertine_container_home_path)
2732+{
2733+ auto actual = libertine_container_home_path("padme");
2734+ auto expected = QString(getenv("XDG_DATA_HOME")) + "/libertine-container/user-data/padme";
2735+ EXPECT_STREQ(expected.toUtf8(), actual);
2736+ g_free(actual);
2737+}
2738+
2739+
2740+TEST_F(LiblibertineTest, libertine_container_home_path_returns_empty)
2741+{
2742+ EXPECT_EQ(nullptr, libertine_container_home_path("jarjar"));
2743+}
2744
2745=== modified file 'tests/unit/CMakeLists.txt'
2746--- tests/unit/CMakeLists.txt 2017-02-13 21:41:36 +0000
2747+++ tests/unit/CMakeLists.txt 2017-03-31 20:05:48 +0000
2748@@ -8,9 +8,10 @@
2749 ContainerConfigListTests.cpp
2750 ContainersConfigTests.cpp
2751 )
2752+
2753 target_link_libraries(
2754 test_container_config
2755- libertine-common
2756+ ${LIBERTINE_COMMON}
2757 gtest gtest_main
2758 Qt5::Core
2759 )
2760
2761=== modified file 'tests/unit/ContainerConfigListTests.cpp'
2762--- tests/unit/ContainerConfigListTests.cpp 2017-02-08 14:57:34 +0000
2763+++ tests/unit/ContainerConfigListTests.cpp 2017-03-31 20:05:48 +0000
2764@@ -34,6 +34,7 @@
2765 EXPECT_EQ(container_configs.size(), 0);
2766 }
2767
2768+
2769 /** Verify constructing a ContainerConfig from JSON DTRT. */
2770 TEST(LibertineContainerConfigList, constructFromJson)
2771 {
2772
2773=== modified file 'tests/unit/service/CMakeLists.txt'
2774--- tests/unit/service/CMakeLists.txt 2016-11-04 13:49:40 +0000
2775+++ tests/unit/service/CMakeLists.txt 2017-03-31 20:05:48 +0000
2776@@ -1,5 +1,5 @@
2777 function(create_service_unit_test test_name)
2778- add_test(${test_name} /usr/bin/python3 -m testtools.run ${test_name})
2779+ add_test(${test_name} /usr/bin/python3 ${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.py)
2780 set_tests_properties(${test_name}
2781 PROPERTIES ENVIRONMENT
2782 "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}")
2783@@ -8,5 +8,6 @@
2784 create_service_unit_test(test_container)
2785 create_service_unit_test(test_apt)
2786 create_service_unit_test(test_task_dispatcher)
2787+create_service_unit_test(test_operations_monitor)
2788
2789 add_subdirectory(tasks)
2790
2791=== modified file 'tests/unit/service/tasks/test_app_info_task.py'
2792--- tests/unit/service/tasks/test_app_info_task.py 2016-11-07 18:51:17 +0000
2793+++ tests/unit/service/tasks/test_app_info_task.py 2017-03-31 20:05:48 +0000
2794@@ -1,4 +1,4 @@
2795-# Copyright 2016 Canonical Ltd.
2796+# Copyright 2016-2017 Canonical Ltd.
2797 #
2798 # This program is free software: you can redistribute it and/or modify it
2799 # under the terms of the GNU General Public License version 3, as published
2800@@ -15,7 +15,7 @@
2801
2802 import unittest.mock
2803 from unittest import TestCase
2804-from libertine.service import tasks, apt
2805+from libertine.service import tasks, apt, operations_monitor
2806 from libertine.ContainersConfig import ContainersConfig
2807
2808
2809@@ -23,39 +23,37 @@
2810 def setUp(self):
2811 self.config = unittest.mock.create_autospec(ContainersConfig)
2812 self.cache = unittest.mock.create_autospec(apt.AptCache)
2813- self.connection = unittest.mock.Mock()
2814+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
2815+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
2816
2817 def test_app_not_found_causes_error(self):
2818 self.called_with = None
2819 def callback(t):
2820 self.called_with = t
2821
2822- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2823- progress = MockProgress.return_value
2824- self.cache.app_info.return_value = {}
2825- task = tasks.AppInfoTask('palpatine', self.cache, 'lightside', [1, 2], self.config, self.connection, callback)
2826- task._instant_callback = True
2827- task.start().join()
2828-
2829- progress.error.assert_called_once_with('Could not find app info for \'lightside\' in container \'palpatine\'')
2830-
2831- self.assertEqual(task, self.called_with)
2832+ self.cache.app_info.return_value = {}
2833+ task = tasks.AppInfoTask('palpatine', self.cache, 'lightside', [1, 2], self.config, self.monitor, callback)
2834+ task._instant_callback = True
2835+ task.start().join()
2836+
2837+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Could not find app info for \'lightside\' in container \'palpatine\'')
2838+
2839+ self.assertEqual(task, self.called_with)
2840
2841 def test_success_sends_data(self):
2842 self.called_with = None
2843 def callback(t):
2844 self.called_with = t
2845
2846- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2847- progress = MockProgress.return_value
2848- progress.done = False
2849-
2850- self.cache.app_info.return_value = {'package': 'darkside-common'}
2851- self.config.get_package_install_status.return_value = 'installed'
2852- task = tasks.AppInfoTask('palpatine', self.cache, 'darkside', [1, 2, 3], self.config, self.connection, callback)
2853- task._instant_callback = True
2854- task.start().join()
2855-
2856- progress.data.assert_called_once_with(str({'package': 'darkside-common', 'status': 'installed', 'task_ids': [1, 2, 3]}))
2857-
2858- self.assertEqual(task, self.called_with)
2859+ self.monitor.done.return_value = False
2860+
2861+ self.cache.app_info.return_value = {'package': 'darkside-common'}
2862+ self.config.get_package_install_status.return_value = 'installed'
2863+ task = tasks.AppInfoTask('palpatine', self.cache, 'darkside', [1, 2, 3], self.config, self.monitor, callback)
2864+ task._instant_callback = True
2865+ task.start().join()
2866+
2867+ self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, str({'package': 'darkside-common', 'status': 'installed', 'task_ids': [1, 2, 3]}))
2868+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
2869+
2870+ self.assertEqual(task, self.called_with)
2871
2872=== modified file 'tests/unit/service/tasks/test_container_info_task.py'
2873--- tests/unit/service/tasks/test_container_info_task.py 2017-01-27 16:42:22 +0000
2874+++ tests/unit/service/tasks/test_container_info_task.py 2017-03-31 20:05:48 +0000
2875@@ -1,4 +1,4 @@
2876-# Copyright 2016 Canonical Ltd.
2877+# Copyright 2016-2017 Canonical Ltd.
2878 #
2879 # This program is free software: you can redistribute it and/or modify it
2880 # under the terms of the GNU General Public License version 3, as published
2881@@ -17,40 +17,39 @@
2882 import unittest.mock
2883 from unittest import TestCase
2884 from libertine import utils
2885-from libertine.service import tasks
2886+from libertine.service import tasks, operations_monitor
2887 from libertine.ContainersConfig import ContainersConfig
2888
2889
2890 class TestContainerInfoTask(TestCase):
2891 def setUp(self):
2892- self.config = unittest.mock.create_autospec(ContainersConfig)
2893- self.connection = unittest.mock.Mock()
2894+ self.config = unittest.mock.create_autospec(ContainersConfig)
2895+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
2896+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
2897
2898 def test_success_sends_data(self):
2899 self.called_with = None
2900 def callback(t):
2901 self.called_with = t
2902
2903- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2904- progress = MockProgress.return_value
2905- progress.done = False
2906-
2907- self.config.get_container_install_status.return_value = 'ready'
2908- self.config.get_container_name.return_value = 'Palpatine'
2909- task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.connection, callback)
2910- task._instant_callback = True
2911- task.start().join()
2912-
2913- progress.data.assert_called_once_with(unittest.mock.ANY)
2914- args, kwargs = progress.data.call_args
2915- self.assertEqual({'id': 'palpatine',
2916- 'status': 'ready',
2917- 'task_ids': [1, 2, 3],
2918- 'name': 'Palpatine',
2919- 'root': utils.get_libertine_container_rootfs_path('palpatine'),
2920- 'home': utils.get_libertine_container_home_dir('palpatine')},
2921- ast.literal_eval(args[0]))
2922-
2923- progress.finished.assert_called_once_with('palpatine')
2924-
2925- self.assertEqual(task, self.called_with)
2926+ self.monitor.done.return_value = False
2927+
2928+ self.config.get_container_install_status.return_value = 'ready'
2929+ self.config.get_container_name.return_value = 'Palpatine'
2930+ task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.monitor, callback)
2931+ task._instant_callback = True
2932+ task.start().join()
2933+
2934+ self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, unittest.mock.ANY)
2935+ args, kwargs = self.monitor.data.call_args
2936+ self.assertEqual({'id': 'palpatine',
2937+ 'status': 'ready',
2938+ 'task_ids': [1, 2, 3],
2939+ 'name': 'Palpatine',
2940+ 'root': utils.get_libertine_container_rootfs_path('palpatine'),
2941+ 'home': utils.get_libertine_container_home_dir('palpatine')},
2942+ ast.literal_eval(args[1]))
2943+
2944+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
2945+
2946+ self.assertEqual(task, self.called_with)
2947
2948=== modified file 'tests/unit/service/tasks/test_create_task.py'
2949--- tests/unit/service/tasks/test_create_task.py 2016-11-07 18:51:17 +0000
2950+++ tests/unit/service/tasks/test_create_task.py 2017-03-31 20:05:48 +0000
2951@@ -1,4 +1,4 @@
2952-# Copyright 2016 Canonical Ltd.
2953+# Copyright 2016-2017 Canonical Ltd.
2954 #
2955 # This program is free software: you can redistribute it and/or modify it
2956 # under the terms of the GNU General Public License version 3, as published
2957@@ -15,15 +15,18 @@
2958
2959 import unittest.mock
2960 from unittest import TestCase
2961-from libertine.service import tasks
2962+from libertine.service import tasks, operations_monitor
2963 from libertine.ContainersConfig import ContainersConfig
2964
2965
2966 class TestCreateTask(TestCase):
2967 def setUp(self):
2968- self.config = unittest.mock.create_autospec(ContainersConfig)
2969- self.connection = unittest.mock.Mock()
2970- self.lock = unittest.mock.MagicMock()
2971+ self.config = unittest.mock.create_autospec(ContainersConfig)
2972+ self.lock = unittest.mock.MagicMock()
2973+ self.client = unittest.mock.Mock()
2974+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
2975+
2976+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
2977 self.called_with = None
2978
2979 def callback(self, task):
2980@@ -31,204 +34,195 @@
2981
2982 def test_success_creates_lxc_container(self):
2983 self.config.container_exists.return_value = False
2984- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2985- progress = MockProgress.return_value
2986- progress.done = False
2987- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
2988- task._instant_callback = True
2989-
2990- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
2991- MockHostInfo.return_value.is_distro_valid.return_value = True
2992- MockHostInfo.return_value.has_lxc_support.return_value = True
2993-
2994- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
2995- MockContainer.return_value.create_libertine_container.return_value = True
2996- task.start().join()
2997-
2998- progress.finished.assert_called_once_with('palpatine')
2999- self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3000- self.config.update_container_install_status.assert_has_calls([
3001- unittest.mock.call('palpatine', 'installing'),
3002- unittest.mock.call('palpatine', 'ready')
3003- ], any_order=True)
3004- self.assertEqual(task, self.called_with)
3005+ self.monitor.done.return_value = False
3006+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
3007+ self.config, self.lock, self.monitor, self.client, self.callback)
3008+ task._instant_callback = True
3009+
3010+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3011+ MockHostInfo.return_value.is_distro_valid.return_value = True
3012+ MockHostInfo.return_value.has_lxc_support.return_value = True
3013+
3014+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3015+ MockContainer.return_value.create_libertine_container.return_value = True
3016+ task.start().join()
3017+
3018+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3019+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3020+ self.config.update_container_install_status.assert_has_calls([
3021+ unittest.mock.call('palpatine', 'installing'),
3022+ unittest.mock.call('palpatine', 'ready')
3023+ ], any_order=True)
3024+ self.assertEqual(task, self.called_with)
3025
3026 def test_success_creates_chroot_container(self):
3027 self.config.container_exists.return_value = False
3028- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3029- progress = MockProgress.return_value
3030- progress.done = False
3031- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback)
3032- task._instant_callback = True
3033-
3034- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3035- MockHostInfo.return_value.is_distro_valid.return_value = True
3036-
3037- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3038- MockContainer.return_value.create_libertine_container.return_value = True
3039- task.start().join()
3040-
3041- progress.finished.assert_called_once_with('palpatine')
3042- self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
3043- self.config.update_container_install_status.assert_has_calls([
3044- unittest.mock.call('palpatine', 'installing'),
3045- unittest.mock.call('palpatine', 'ready')
3046- ], any_order=True)
3047- self.assertEqual(task, self.called_with)
3048+ self.monitor.done.return_value = False
3049+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', False,
3050+ self.config, self.lock, self.monitor, self.client, self.callback)
3051+ task._instant_callback = True
3052+
3053+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3054+ MockHostInfo.return_value.is_distro_valid.return_value = True
3055+
3056+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3057+ MockContainer.return_value.create_libertine_container.return_value = True
3058+ task.start().join()
3059+
3060+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3061+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
3062+ self.config.update_container_install_status.assert_has_calls([
3063+ unittest.mock.call('palpatine', 'installing'),
3064+ unittest.mock.call('palpatine', 'ready')
3065+ ], any_order=True)
3066+ self.assertEqual(task, self.called_with)
3067
3068 def test_container_runtime_error_sends_error(self):
3069 self.config.container_exists.return_value = False
3070- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3071- progress = MockProgress.return_value
3072- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
3073- task._instant_callback = True
3074-
3075- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3076- MockHostInfo.return_value.is_distro_valid.return_value = True
3077- MockHostInfo.return_value.has_lxc_support.return_value = True
3078-
3079- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3080- MockContainer.return_value.create_libertine_container.side_effect = RuntimeError('a great disturbance')
3081- task.start().join()
3082-
3083- progress.error.assert_called_once_with('a great disturbance')
3084-
3085- self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3086- self.config.update_container_install_status.assert_called_once_with('palpatine', 'installing')
3087- self.config.delete_container.assert_called_once_with('palpatine')
3088-
3089- self.assertEqual(task, self.called_with)
3090+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
3091+ self.config, self.lock, self.monitor, self.client, self.callback)
3092+ task._instant_callback = True
3093+
3094+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3095+ MockHostInfo.return_value.is_distro_valid.return_value = True
3096+ MockHostInfo.return_value.has_lxc_support.return_value = True
3097+
3098+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3099+ MockContainer.return_value.create_libertine_container.side_effect = RuntimeError('a great disturbance')
3100+ task.start().join()
3101+
3102+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'a great disturbance')
3103+
3104+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3105+ self.config.update_container_install_status.assert_called_once_with('palpatine', 'installing')
3106+ self.config.delete_container.assert_called_once_with('palpatine')
3107+
3108+ self.assertEqual(task, self.called_with)
3109
3110 def test_failed_container_exists_sends_error(self):
3111 self.config.container_exists.return_value = True
3112- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3113- progress = MockProgress.return_value
3114- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
3115- task._instant_callback = True
3116- task.start().join()
3117+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
3118+ self.config, self.lock, self.monitor, self.client, self.callback)
3119+ task._instant_callback = True
3120+ task.start().join()
3121
3122- progress.error.assert_called_once_with('Container \'palpatine\' already exists')
3123- self.assertEqual(task, self.called_with)
3124+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' already exists')
3125+ self.assertEqual(task, self.called_with)
3126
3127 def test_container_invalid_distro_error_sends_error(self):
3128 self.config.container_exists.return_value = False
3129- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3130- progress = MockProgress.return_value
3131- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3132- MockHostInfo.return_value.is_distro_valid.return_value = False
3133- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'vesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
3134- task._instant_callback = True
3135- task.start().join()
3136+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3137+ MockHostInfo.return_value.is_distro_valid.return_value = False
3138+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'vesty', 'lxc', False,
3139+ self.config, self.lock, self.monitor, self.client, self.callback)
3140+ task._instant_callback = True
3141+ task.start().join()
3142
3143- progress.error.assert_called_once_with('Invalid distro \'vesty\'.')
3144- self.assertEqual(task, self.called_with)
3145+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Invalid distro \'vesty\'.')
3146+ self.assertEqual(task, self.called_with)
3147
3148 def test_container_improper_lxc_error_sends_error(self):
3149 self.config.container_exists.return_value = False
3150- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3151- progress = MockProgress.return_value
3152- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3153- MockHostInfo.return_value.is_distro_valid.return_value = True
3154- MockHostInfo.return_value.has_lxc_support.return_value = False
3155- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
3156- task._instant_callback = True
3157- task.start().join()
3158+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3159+ MockHostInfo.return_value.is_distro_valid.return_value = True
3160+ MockHostInfo.return_value.has_lxc_support.return_value = False
3161+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False,
3162+ self.config, self.lock, self.monitor, self.client, self.callback)
3163+ task._instant_callback = True
3164+ task.start().join()
3165
3166- progress.error.assert_called_once_with('System kernel does not support lxc type containers. Please either use chroot or leave empty.')
3167- self.assertEqual(task, self.called_with)
3168+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, \
3169+ 'System kernel does not support lxc type containers. Please either use chroot or leave empty.')
3170+ self.assertEqual(task, self.called_with)
3171
3172 def test_sets_generic_name_when_empty(self):
3173 self.config.container_exists.return_value = False
3174- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3175- progress = MockProgress.return_value
3176- progress.done = False
3177- task = tasks.CreateTask('palpatine', None, 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback)
3178- task._instant_callback = True
3179-
3180- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3181- MockHostInfo.return_value.is_distro_valid.return_value = True
3182- MockHostInfo.return_value.get_distro_codename.return_value = 'Zesty Zapus 17.04'
3183-
3184- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3185- MockContainer.return_value.create_libertine_container.return_value = True
3186- task.start().join()
3187-
3188- progress.finished.assert_called_once_with('palpatine')
3189- self.config.add_new_container.assert_called_once_with('palpatine', 'Ubuntu \'Zesty Zapus 17.04\'', 'chroot', 'zesty')
3190- self.config.update_container_install_status.assert_has_calls([
3191- unittest.mock.call('palpatine', 'installing'),
3192- unittest.mock.call('palpatine', 'ready')
3193- ], any_order=True)
3194- self.assertEqual(task, self.called_with)
3195+ self.monitor.done.return_value = False
3196+ task = tasks.CreateTask('palpatine', None, 'zesty', 'chroot', False, self.config,
3197+ self.lock, self.monitor, self.client, self.callback)
3198+ task._instant_callback = True
3199+
3200+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3201+ MockHostInfo.return_value.is_distro_valid.return_value = True
3202+ MockHostInfo.return_value.get_distro_codename.return_value = 'Zesty Zapus 17.04'
3203+
3204+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3205+ MockContainer.return_value.create_libertine_container.return_value = True
3206+ task.start().join()
3207+
3208+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3209+ self.config.add_new_container.assert_called_once_with('palpatine', 'Ubuntu \'Zesty Zapus 17.04\'', 'chroot', 'zesty')
3210+ self.config.update_container_install_status.assert_has_calls([
3211+ unittest.mock.call('palpatine', 'installing'),
3212+ unittest.mock.call('palpatine', 'ready')
3213+ ], any_order=True)
3214+ self.assertEqual(task, self.called_with)
3215
3216 def test_sets_multiarch(self):
3217 self.config.container_exists.return_value = False
3218- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3219- progress = MockProgress.return_value
3220- progress.done = False
3221- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', True, self.config, self.lock, self.connection, self.callback)
3222- task._instant_callback = True
3223-
3224- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3225- MockHostInfo.return_value.is_distro_valid.return_value = True
3226-
3227- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3228- MockContainer.return_value.create_libertine_container.return_value = True
3229- task.start().join()
3230-
3231- progress.finished.assert_called_once_with('palpatine')
3232- self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
3233- self.config.update_container_multiarch_support.assert_called_once_with('palpatine', 'enabled')
3234- self.config.update_container_install_status.assert_has_calls([
3235- unittest.mock.call('palpatine', 'installing'),
3236- unittest.mock.call('palpatine', 'ready')
3237- ], any_order=True)
3238- self.assertEqual(task, self.called_with)
3239+ self.monitor.done.return_value = False
3240+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', True,
3241+ self.config, self.lock, self.monitor, self.client, self.callback)
3242+ task._instant_callback = True
3243+
3244+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3245+ MockHostInfo.return_value.is_distro_valid.return_value = True
3246+
3247+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3248+ MockContainer.return_value.create_libertine_container.return_value = True
3249+ task.start().join()
3250+
3251+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3252+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
3253+ self.config.update_container_multiarch_support.assert_called_once_with('palpatine', 'enabled')
3254+ self.config.update_container_install_status.assert_has_calls([
3255+ unittest.mock.call('palpatine', 'installing'),
3256+ unittest.mock.call('palpatine', 'ready')
3257+ ], any_order=True)
3258+ self.assertEqual(task, self.called_with)
3259
3260 def test_sets_default_type(self):
3261 self.config.container_exists.return_value = False
3262- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3263- progress = MockProgress.return_value
3264- progress.done = False
3265- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', None, False, self.config, self.lock, self.connection, self.callback)
3266- task._instant_callback = True
3267-
3268- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3269- MockHostInfo.return_value.is_distro_valid.return_value = True
3270- MockHostInfo.return_value.select_container_type_by_kernel.return_value = 'lxc'
3271-
3272- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3273- MockContainer.return_value.create_libertine_container.return_value = True
3274- task.start().join()
3275-
3276- progress.finished.assert_called_once_with('palpatine')
3277- self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3278- self.config.update_container_install_status.assert_has_calls([
3279- unittest.mock.call('palpatine', 'installing'),
3280- unittest.mock.call('palpatine', 'ready')
3281- ], any_order=True)
3282- self.assertEqual(task, self.called_with)
3283+ self.monitor.done.return_value = False
3284+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', None, False,
3285+ self.config, self.lock, self.monitor, self.client, self.callback)
3286+ task._instant_callback = True
3287+
3288+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3289+ MockHostInfo.return_value.is_distro_valid.return_value = True
3290+ MockHostInfo.return_value.select_container_type_by_kernel.return_value = 'lxc'
3291+
3292+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3293+ MockContainer.return_value.create_libertine_container.return_value = True
3294+ task.start().join()
3295+
3296+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3297+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3298+ self.config.update_container_install_status.assert_has_calls([
3299+ unittest.mock.call('palpatine', 'installing'),
3300+ unittest.mock.call('palpatine', 'ready')
3301+ ], any_order=True)
3302+ self.assertEqual(task, self.called_with)
3303
3304 def test_sets_default_distro(self):
3305 self.config.container_exists.return_value = False
3306- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3307- progress = MockProgress.return_value
3308- progress.done = False
3309- task = tasks.CreateTask('palpatine', 'Emperor Palpatine', None, 'lxc', False, self.config, self.lock, self.connection, self.callback)
3310- task._instant_callback = True
3311-
3312- with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3313- MockHostInfo.return_value.has_lxc_support.return_value = True
3314- MockHostInfo.return_value.get_host_distro_release.return_value = 'zesty'
3315-
3316- with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3317- MockContainer.return_value.create_libertine_container.return_value = True
3318- task.start().join()
3319-
3320- progress.finished.assert_called_once_with('palpatine')
3321- self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3322- self.config.update_container_install_status.assert_has_calls([
3323- unittest.mock.call('palpatine', 'installing'),
3324- unittest.mock.call('palpatine', 'ready')
3325- ], any_order=True)
3326- self.assertEqual(task, self.called_with)
3327+ self.monitor.done.return_value = False
3328+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', None, 'lxc', False,
3329+ self.config, self.lock, self.monitor, self.client, self.callback)
3330+ task._instant_callback = True
3331+
3332+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
3333+ MockHostInfo.return_value.has_lxc_support.return_value = True
3334+ MockHostInfo.return_value.get_host_distro_release.return_value = 'zesty'
3335+
3336+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
3337+ MockContainer.return_value.create_libertine_container.return_value = True
3338+ task.start().join()
3339+
3340+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3341+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
3342+ self.config.update_container_install_status.assert_has_calls([
3343+ unittest.mock.call('palpatine', 'installing'),
3344+ unittest.mock.call('palpatine', 'ready')
3345+ ], any_order=True)
3346+ self.assertEqual(task, self.called_with)
3347
3348=== modified file 'tests/unit/service/tasks/test_destroy_task.py'
3349--- tests/unit/service/tasks/test_destroy_task.py 2016-11-07 18:51:17 +0000
3350+++ tests/unit/service/tasks/test_destroy_task.py 2017-03-31 20:05:48 +0000
3351@@ -1,4 +1,4 @@
3352-# Copyright 2016 Canonical Ltd.
3353+# Copyright 2016-2017 Canonical Ltd.
3354 #
3355 # This program is free software: you can redistribute it and/or modify it
3356 # under the terms of the GNU General Public License version 3, as published
3357@@ -15,15 +15,18 @@
3358
3359 import unittest.mock
3360 from unittest import TestCase
3361-from libertine.service import tasks
3362+from libertine.service import tasks, operations_monitor
3363 from libertine.ContainersConfig import ContainersConfig
3364
3365
3366 class TestDestroyTask(TestCase):
3367 def setUp(self):
3368- self.config = unittest.mock.create_autospec(ContainersConfig)
3369- self.connection = unittest.mock.Mock()
3370- self.lock = unittest.mock.MagicMock()
3371+ self.config = unittest.mock.create_autospec(ContainersConfig)
3372+ self.lock = unittest.mock.MagicMock()
3373+ self.client = unittest.mock.Mock()
3374+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3375+
3376+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3377 self.called_with = None
3378
3379 def callback(self, task):
3380@@ -31,50 +34,45 @@
3381
3382 def test_sends_error_on_non_ready_container(self):
3383 self.config._get_value_by_key.return_value = ''
3384- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3385- progress = MockProgress.return_value
3386- progress.done = False
3387- task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)
3388- task._instant_callback = True
3389-
3390- with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3391- MockContainer.return_value.destroy_libertine_container.return_value = True
3392- task.start().join()
3393-
3394- progress.error.assert_called_once_with('Container \'palpatine\' does not exist')
3395- self.assertEqual(task, self.called_with)
3396+ self.monitor.done.return_value = False
3397+ task = tasks.DestroyTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3398+ task._instant_callback = True
3399+
3400+ with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3401+ MockContainer.return_value.destroy_libertine_container.return_value = True
3402+ task.start().join()
3403+
3404+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' does not exist')
3405+ self.assertEqual(task, self.called_with)
3406
3407 def test_sends_error_on_failed_destroy(self):
3408 self.config._get_value_by_key.return_value = 'ready'
3409- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3410- progress = MockProgress.return_value
3411- task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)
3412- task._instant_callback = True
3413-
3414- with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3415- MockContainer.return_value.destroy_libertine_container.return_value = False
3416- task.start().join()
3417-
3418- progress.error.assert_called_once_with('Destroying container \'palpatine\' failed')
3419- self.config.update_container_install_status.assert_has_calls([
3420- unittest.mock.call('palpatine', 'removing'),
3421- unittest.mock.call('palpatine', 'ready')
3422- ], any_order=True)
3423- self.assertEqual(task, self.called_with)
3424+ task = tasks.DestroyTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3425+ task._instant_callback = True
3426+
3427+ with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3428+ MockContainer.return_value.destroy_libertine_container.return_value = False
3429+ task.start().join()
3430+
3431+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Destroying container \'palpatine\' failed')
3432+ self.config.update_container_install_status.assert_has_calls([
3433+ unittest.mock.call('palpatine', 'removing'),
3434+ unittest.mock.call('palpatine', 'ready')
3435+ ], any_order=True)
3436+
3437+ self.assertEqual(task, self.called_with)
3438
3439 def test_successfully_destroys(self):
3440 self.config._get_value_by_key.return_value = 'ready'
3441- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3442- progress = MockProgress.return_value
3443- progress.done = False
3444- task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)
3445- task._instant_callback = True
3446-
3447- with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3448- MockContainer.return_value.destroy_libertine_container.return_value = True
3449- task.start().join()
3450-
3451- progress.finished.assert_called_once_with('palpatine')
3452- self.config.update_container_install_status.assert_called_once_with('palpatine', 'removing')
3453- self.config.delete_container.assert_called_once_with('palpatine')
3454- self.assertEqual(task, self.called_with)
3455+ self.monitor.done.return_value = False
3456+ task = tasks.DestroyTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3457+ task._instant_callback = True
3458+
3459+ with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
3460+ MockContainer.return_value.destroy_libertine_container.return_value = True
3461+ task.start().join()
3462+
3463+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3464+ self.config.update_container_install_status.assert_called_once_with('palpatine', 'removing')
3465+ self.config.delete_container.assert_called_once_with('palpatine')
3466+ self.assertEqual(task, self.called_with)
3467
3468=== modified file 'tests/unit/service/tasks/test_install_task.py'
3469--- tests/unit/service/tasks/test_install_task.py 2016-11-08 15:20:20 +0000
3470+++ tests/unit/service/tasks/test_install_task.py 2017-03-31 20:05:48 +0000
3471@@ -1,4 +1,4 @@
3472-# Copyright 2016 Canonical Ltd.
3473+# Copyright 2016-2017 Canonical Ltd.
3474 #
3475 # This program is free software: you can redistribute it and/or modify it
3476 # under the terms of the GNU General Public License version 3, as published
3477@@ -15,15 +15,18 @@
3478
3479 import unittest.mock
3480 from unittest import TestCase
3481-from libertine.service import tasks
3482+from libertine.service import tasks, operations_monitor
3483 from libertine.ContainersConfig import ContainersConfig
3484
3485
3486 class TestInstallTask(TestCase):
3487 def setUp(self):
3488- self.config = unittest.mock.create_autospec(ContainersConfig)
3489- self.connection = unittest.mock.Mock()
3490- self.lock = unittest.mock.MagicMock()
3491+ self.config = unittest.mock.create_autospec(ContainersConfig)
3492+ self.lock = unittest.mock.MagicMock()
3493+ self.client = unittest.mock.Mock()
3494+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3495+
3496+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3497 self.called_with = None
3498
3499 def callback(self, task):
3500@@ -31,46 +34,40 @@
3501
3502 def test_sends_error_on_existing_package(self):
3503 self.config.package_exists.return_value = True
3504- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3505- progress = MockProgress.return_value
3506- task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
3507- task._instant_callback = True
3508- task.start().join()
3509+ task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3510+ task._instant_callback = True
3511+ task.start().join()
3512
3513- progress.error.assert_called_once_with('Package \'darkside-common\' already exists, skipping install')
3514- self.assertEqual(task, self.called_with)
3515+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Package \'darkside-common\' already exists, skipping install')
3516+ self.assertEqual(task, self.called_with)
3517
3518 def test_sends_error_on_failed_install(self):
3519 self.config.package_exists.return_value = False
3520- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3521- progress = MockProgress.return_value
3522- task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
3523- task._instant_callback = True
3524-
3525- with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
3526- MockContainer.return_value.install_package.return_value = False
3527- task.start().join()
3528-
3529- progress.error.assert_called_once_with("Package installation failed for 'darkside-common'")
3530- self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'installing')
3531- self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
3532- self.assertEqual(task, self.called_with)
3533+ task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3534+ task._instant_callback = True
3535+
3536+ with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
3537+ MockContainer.return_value.install_package.return_value = False
3538+ task.start().join()
3539+
3540+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, "Package installation failed for 'darkside-common'")
3541+ self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'installing')
3542+ self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
3543+ self.assertEqual(task, self.called_with)
3544
3545 def test_successfully_install(self):
3546 self.config.package_exists.return_value = False
3547- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3548- progress = MockProgress.return_value
3549- progress.done = False
3550- task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
3551- task._instant_callback = True
3552-
3553- with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
3554- MockContainer.return_value.install_package.return_value = True
3555- task.start().join()
3556-
3557- progress.finished.assert_called_once_with('palpatine')
3558- self.config.update_package_install_status.assert_has_calls([
3559- unittest.mock.call('palpatine', 'darkside-common', 'installing'),
3560- unittest.mock.call('palpatine', 'darkside-common', 'installed')
3561- ], any_order=True)
3562- self.assertEqual(task, self.called_with)
3563+ self.monitor.done.return_value = False
3564+ task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3565+ task._instant_callback = True
3566+
3567+ with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
3568+ MockContainer.return_value.install_package.return_value = True
3569+ task.start().join()
3570+
3571+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3572+ self.config.update_package_install_status.assert_has_calls([
3573+ unittest.mock.call('palpatine', 'darkside-common', 'installing'),
3574+ unittest.mock.call('palpatine', 'darkside-common', 'installed')
3575+ ], any_order=True)
3576+ self.assertEqual(task, self.called_with)
3577
3578=== modified file 'tests/unit/service/tasks/test_list_app_ids_task.py'
3579--- tests/unit/service/tasks/test_list_app_ids_task.py 2017-02-07 12:35:48 +0000
3580+++ tests/unit/service/tasks/test_list_app_ids_task.py 2017-03-31 20:05:48 +0000
3581@@ -15,15 +15,17 @@
3582 import json
3583 import unittest.mock
3584 from unittest import TestCase
3585-from libertine.service import tasks
3586+from libertine.service import tasks, operations_monitor
3587 from libertine.ContainersConfig import ContainersConfig
3588
3589
3590 class TestListAppIdsTask(TestCase):
3591 def setUp(self):
3592 self.config = unittest.mock.create_autospec(ContainersConfig)
3593- self.connection = unittest.mock.Mock()
3594 self.lock = unittest.mock.MagicMock()
3595+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3596+
3597+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3598 self.called_with = None
3599
3600 def callback(self, task):
3601@@ -31,29 +33,25 @@
3602
3603 def test_sends_error_on_non_existent_container(self):
3604 self.config.container_exists.return_value = False
3605- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3606- progress = MockProgress.return_value
3607- task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback)
3608- task._instant_callback = True
3609-
3610- with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3611- task.start().join()
3612-
3613- progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list')
3614- self.assertEqual(task, self.called_with)
3615+ task = tasks.ListAppIdsTask('palpatine', self.config, self.monitor, self.callback)
3616+ task._instant_callback = True
3617+
3618+ with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3619+ task.start().join()
3620+
3621+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' does not exist, skipping list')
3622+ self.assertEqual(task, self.called_with)
3623
3624 def test_successfully_lists_apps(self):
3625 self.config.container_exists.return_value = True
3626- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3627- progress = MockProgress.return_value
3628- progress.done = False
3629- task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback)
3630- task._instant_callback = True
3631-
3632- with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3633- MockContainer.return_value.list_app_ids.return_value = '["palpatine_gedit_0.0","palpatine_xterm_0.0"]'
3634- task.start().join()
3635-
3636- progress.finished.assert_called_once_with('palpatine')
3637- progress.data.assert_called_once_with(json.dumps('["palpatine_gedit_0.0","palpatine_xterm_0.0"]'))
3638- self.assertEqual(task, self.called_with)
3639+ self.monitor.done.return_value = False
3640+ task = tasks.ListAppIdsTask('palpatine', self.config, self.monitor, self.callback)
3641+ task._instant_callback = True
3642+
3643+ with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer:
3644+ MockContainer.return_value.list_app_ids.return_value = '["palpatine_gedit_0.0","palpatine_xterm_0.0"]'
3645+ task.start().join()
3646+
3647+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3648+ self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, json.dumps('["palpatine_gedit_0.0","palpatine_xterm_0.0"]'))
3649+ self.assertEqual(task, self.called_with)
3650
3651=== modified file 'tests/unit/service/tasks/test_list_task.py'
3652--- tests/unit/service/tasks/test_list_task.py 2017-01-24 18:00:57 +0000
3653+++ tests/unit/service/tasks/test_list_task.py 2017-03-31 20:05:48 +0000
3654@@ -17,31 +17,30 @@
3655 import unittest.mock
3656
3657 from unittest import TestCase
3658-from libertine.service import tasks
3659+from libertine.service import tasks, operations_monitor
3660 from libertine.ContainersConfig import ContainersConfig
3661
3662
3663 class TestListTask(TestCase):
3664 def setUp(self):
3665 self.config = unittest.mock.create_autospec(ContainersConfig)
3666- self.connection = unittest.mock.Mock()
3667+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3668+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3669
3670 def test_success_sends_data(self):
3671 self.called_with = None
3672 def callback(t):
3673 self.called_with = t
3674
3675- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3676- progress = MockProgress.return_value
3677- progress.done = False
3678-
3679- task = tasks.ListTask(self.config, self.connection, callback)
3680- task._instant_callback = True
3681-
3682- self.config.get_containers.return_value = ['palatine', 'vader', 'maul']
3683- task.start().join()
3684-
3685- progress.data.assert_called_once_with(json.dumps(['palatine', 'vader', 'maul']))
3686- progress.finished.assert_called_once_with('')
3687-
3688- self.assertEqual(task, self.called_with)
3689+ self.monitor.done.return_value = False
3690+
3691+ task = tasks.ListTask(self.config, self.monitor, callback)
3692+ task._instant_callback = True
3693+
3694+ self.config.get_containers.return_value = ['palatine', 'vader', 'maul']
3695+ task.start().join()
3696+
3697+ self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, json.dumps(['palatine', 'vader', 'maul']))
3698+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3699+
3700+ self.assertEqual(task, self.called_with)
3701
3702=== modified file 'tests/unit/service/tasks/test_remove_task.py'
3703--- tests/unit/service/tasks/test_remove_task.py 2016-11-08 15:20:20 +0000
3704+++ tests/unit/service/tasks/test_remove_task.py 2017-03-31 20:05:48 +0000
3705@@ -1,4 +1,4 @@
3706-# Copyright 2016 Canonical Ltd.
3707+# Copyright 2016-2017 Canonical Ltd.
3708 #
3709 # This program is free software: you can redistribute it and/or modify it
3710 # under the terms of the GNU General Public License version 3, as published
3711@@ -15,15 +15,18 @@
3712
3713 import unittest.mock
3714 from unittest import TestCase
3715-from libertine.service import tasks
3716+from libertine.service import tasks, operations_monitor
3717 from libertine.ContainersConfig import ContainersConfig
3718
3719
3720 class TestRemoveTask(TestCase):
3721 def setUp(self):
3722- self.config = unittest.mock.create_autospec(ContainersConfig)
3723- self.connection = unittest.mock.Mock()
3724- self.lock = unittest.mock.MagicMock()
3725+ self.config = unittest.mock.create_autospec(ContainersConfig)
3726+ self.lock = unittest.mock.MagicMock()
3727+ self.client = unittest.mock.Mock()
3728+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3729+
3730+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3731 self.called_with = None
3732
3733 def callback(self, task):
3734@@ -31,46 +34,40 @@
3735
3736 def test_sends_error_on_non_installed_package(self):
3737 self.config.get_package_install_status.return_value = 'installing'
3738- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3739- progress = MockProgress.return_value
3740- task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
3741- task._instant_callback = True
3742- task.start().join()
3743+ task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3744+ task._instant_callback = True
3745+ task.start().join()
3746
3747- progress.error.assert_called_once_with('Package \'darkside-common\' not installed, skipping remove')
3748- self.assertEqual(task, self.called_with)
3749+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Package \'darkside-common\' not installed, skipping remove')
3750+ self.assertEqual(task, self.called_with)
3751
3752 def test_sends_error_on_failed_install(self):
3753 self.config.get_package_install_status.return_value = 'installed'
3754- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3755- progress = MockProgress.return_value
3756- task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
3757- task._instant_callback = True
3758-
3759- with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
3760- MockContainer.return_value.remove_package.return_value = False
3761- task.start().join()
3762-
3763- progress.error.assert_called_once_with("Package removal failed for 'darkside-common'")
3764- self.config.update_package_install_status.assert_has_calls([
3765- unittest.mock.call('palpatine', 'darkside-common', 'removing'),
3766- unittest.mock.call('palpatine', 'darkside-common', 'installed')
3767- ], any_order=True)
3768- self.assertEqual(task, self.called_with)
3769+ task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3770+ task._instant_callback = True
3771+
3772+ with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
3773+ MockContainer.return_value.remove_package.return_value = False
3774+ task.start().join()
3775+
3776+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, "Package removal failed for 'darkside-common'")
3777+ self.config.update_package_install_status.assert_has_calls([
3778+ unittest.mock.call('palpatine', 'darkside-common', 'removing'),
3779+ unittest.mock.call('palpatine', 'darkside-common', 'installed')
3780+ ], any_order=True)
3781+ self.assertEqual(task, self.called_with)
3782
3783 def test_successfully_install(self):
3784 self.config.get_package_install_status.return_value = 'installed'
3785- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3786- progress = MockProgress.return_value
3787- progress.done = False
3788- task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
3789- task._instant_callback = True
3790-
3791- with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
3792- MockContainer.return_value.remove_package.return_value = True
3793- task.start().join()
3794-
3795- progress.finished.assert_called_once_with('palpatine')
3796- self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'removing')
3797- self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
3798- self.assertEqual(task, self.called_with)
3799+ self.monitor.done.return_value = False
3800+ task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3801+ task._instant_callback = True
3802+
3803+ with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
3804+ MockContainer.return_value.remove_package.return_value = True
3805+ task.start().join()
3806+
3807+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3808+ self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'removing')
3809+ self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
3810+ self.assertEqual(task, self.called_with)
3811
3812=== modified file 'tests/unit/service/tasks/test_search_task.py'
3813--- tests/unit/service/tasks/test_search_task.py 2016-11-07 18:51:17 +0000
3814+++ tests/unit/service/tasks/test_search_task.py 2017-03-31 20:05:48 +0000
3815@@ -1,4 +1,4 @@
3816-# Copyright 2016 Canonical Ltd.
3817+# Copyright 2016-2017 Canonical Ltd.
3818 #
3819 # This program is free software: you can redistribute it and/or modify it
3820 # under the terms of the GNU General Public License version 3, as published
3821@@ -15,29 +15,29 @@
3822
3823 import unittest.mock
3824 from unittest import TestCase
3825-from libertine.service import tasks, apt
3826+from libertine.service import tasks, apt, operations_monitor
3827
3828
3829 class TestSearchTask(TestCase):
3830 def setUp(self):
3831- self.connection = unittest.mock.Mock()
3832 self.lock = unittest.mock.MagicMock()
3833 self.cache = unittest.mock.create_autospec(apt.AptCache)
3834+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3835+
3836+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3837 self.called_with = None
3838
3839 def callback(self, task):
3840 self.called_with = task
3841
3842 def test_successfully_lists_apps(self):
3843- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3844- progress = MockProgress.return_value
3845- progress.done = False
3846- task = tasks.SearchTask('palpatine', self.cache, 'jarjar', self.connection, self.callback)
3847+ self.monitor.done.return_value = False
3848+ task = tasks.SearchTask('palpatine', self.cache, 'jarjar', self.monitor, self.callback)
3849 task._instant_callback = True
3850
3851 self.cache.search.return_value = ['jarjar', 'sidius']
3852 task.start().join()
3853
3854- progress.finished.assert_called_once_with('palpatine')
3855- progress.data.assert_called_once_with(str(['jarjar', 'sidius']))
3856+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3857+ self.monitor.data.assert_called_once_with(self.monitor.new_operation.return_value, str(['jarjar', 'sidius']))
3858 self.assertEqual(task, self.called_with)
3859
3860=== modified file 'tests/unit/service/tasks/test_update_task.py'
3861--- tests/unit/service/tasks/test_update_task.py 2016-11-08 15:20:20 +0000
3862+++ tests/unit/service/tasks/test_update_task.py 2017-03-31 20:05:48 +0000
3863@@ -1,4 +1,4 @@
3864-# Copyright 2016 Canonical Ltd.
3865+# Copyright 2016-2017 Canonical Ltd.
3866 #
3867 # This program is free software: you can redistribute it and/or modify it
3868 # under the terms of the GNU General Public License version 3, as published
3869@@ -15,15 +15,18 @@
3870
3871 import unittest.mock
3872 from unittest import TestCase
3873-from libertine.service import tasks
3874+from libertine.service import tasks, operations_monitor
3875 from libertine.ContainersConfig import ContainersConfig
3876
3877
3878 class TestUpdateTask(TestCase):
3879 def setUp(self):
3880- self.config = unittest.mock.create_autospec(ContainersConfig)
3881- self.connection = unittest.mock.Mock()
3882- self.lock = unittest.mock.MagicMock()
3883+ self.config = unittest.mock.create_autospec(ContainersConfig)
3884+ self.lock = unittest.mock.MagicMock()
3885+ self.client = unittest.mock.Mock()
3886+ self.monitor = unittest.mock.create_autospec(operations_monitor.OperationsMonitor)
3887+
3888+ self.monitor.new_operation.return_value = "/com/canonical/libertine/Service/Download/123456"
3889 self.called_with = None
3890
3891 def callback(self, task):
3892@@ -31,50 +34,47 @@
3893
3894 def test_sends_error_on_non_existent_container(self):
3895 self.config.container_exists.return_value = False
3896- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3897- progress = MockProgress.return_value
3898- task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)
3899- task._instant_callback = True
3900-
3901- with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3902- task.start().join()
3903-
3904- progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping update')
3905- self.assertEqual(task, self.called_with)
3906+ task = tasks.UpdateTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3907+ task._instant_callback = True
3908+
3909+ with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3910+ task.start().join()
3911+
3912+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Container \'palpatine\' does not exist, skipping update')
3913+
3914+ self.assertEqual(task, self.called_with)
3915
3916 def test_sends_error_on_failed_update(self):
3917 self.config.container_exists.return_value = True
3918- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3919- progress = MockProgress.return_value
3920- task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)
3921- task._instant_callback = True
3922-
3923- with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3924- MockContainer.return_value.update_libertine_container.return_value = False
3925- task.start().join()
3926-
3927- progress.error.assert_called_once_with('Failed to update container \'palpatine\'')
3928- self.config.update_container_install_status.assert_has_calls([
3929- unittest.mock.call('palpatine', 'updating'),
3930- unittest.mock.call('palpatine', 'ready')
3931- ], any_order=True)
3932- self.assertEqual(task, self.called_with)
3933+ task = tasks.UpdateTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3934+ task._instant_callback = True
3935+
3936+ with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3937+ MockContainer.return_value.update_libertine_container.return_value = False
3938+ task.start().join()
3939+
3940+ self.monitor.error.assert_called_once_with(self.monitor.new_operation.return_value, 'Failed to update container \'palpatine\'')
3941+ self.config.update_container_install_status.assert_has_calls([
3942+ unittest.mock.call('palpatine', 'updating'),
3943+ unittest.mock.call('palpatine', 'ready')
3944+ ], any_order=True)
3945+
3946+ self.assertEqual(task, self.called_with)
3947
3948 def test_successfully_updates(self):
3949 self.config.container_exists.return_value = True
3950- with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
3951- progress = MockProgress.return_value
3952- progress.done = False
3953- task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)
3954- task._instant_callback = True
3955-
3956- with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3957- MockContainer.return_value.update_libertine_container.return_value = True
3958- task.start().join()
3959-
3960- progress.finished.assert_called_once_with('palpatine')
3961- self.config.update_container_install_status.assert_has_calls([
3962- unittest.mock.call('palpatine', 'updating'),
3963- unittest.mock.call('palpatine', 'ready')
3964- ], any_order=True)
3965- self.assertEqual(task, self.called_with)
3966+ self.monitor.done.return_value = False
3967+ task = tasks.UpdateTask('palpatine', self.config, self.lock, self.monitor, self.client, self.callback)
3968+ task._instant_callback = True
3969+
3970+ with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
3971+ MockContainer.return_value.update_libertine_container.return_value = True
3972+ task.start().join()
3973+
3974+ self.monitor.finished.assert_called_once_with(self.monitor.new_operation.return_value)
3975+ self.config.update_container_install_status.assert_has_calls([
3976+ unittest.mock.call('palpatine', 'updating'),
3977+ unittest.mock.call('palpatine', 'ready')
3978+ ], any_order=True)
3979+
3980+ self.assertEqual(task, self.called_with)
3981
3982=== modified file 'tests/unit/service/test_container.py'
3983--- tests/unit/service/test_container.py 2017-02-07 12:35:48 +0000
3984+++ tests/unit/service/test_container.py 2017-03-31 20:05:48 +0000
3985@@ -20,31 +20,32 @@
3986
3987 class TestContainer(TestCase):
3988 def setUp(self):
3989- self._connection = unittest.mock.Mock()
3990+ self._monitor = unittest.mock.Mock()
3991 self._config = unittest.mock.Mock()
3992+ self._client = unittest.mock.Mock()
3993
3994 def test_search_creates_search_task(self):
3995 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
3996 cache = MockCache.return_value
3997- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
3998+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
3999 with unittest.mock.patch('libertine.service.container.SearchTask') as MockSearchTask:
4000 c.search('darkseid')
4001- MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._connection, unittest.mock.ANY)
4002+ MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._monitor, unittest.mock.ANY)
4003 MockSearchTask.return_value.start.assert_called_once_with()
4004
4005 def test_app_info_creates_app_info_task(self):
4006 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4007 cache = MockCache.return_value
4008- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4009+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4010 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
4011 c.app_info('force')
4012- MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._connection, unittest.mock.ANY)
4013+ MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._monitor, unittest.mock.ANY)
4014 MockAppInfoTask.return_value.start.assert_called_once_with()
4015
4016 def test_app_info_gets_related_task_info(self):
4017 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4018 cache = MockCache.return_value
4019- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4020+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4021 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4022 MockInstallTask.return_value.package = 'darkside'
4023 MockInstallTask.return_value.matches.return_value = False
4024@@ -55,111 +56,111 @@
4025 with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
4026 c.app_info('darkside')
4027 MockAppInfoTask.assert_called_once_with('palpatine', cache, 'darkside', [install_task_id, remove_task_id],
4028- self._config, self._connection, unittest.mock.ANY)
4029+ self._config, self._monitor, unittest.mock.ANY)
4030
4031 def test_install_creates_install_task(self):
4032 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4033- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4034+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4035 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4036 c.install('force')
4037- MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4038+ MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4039 MockInstallTask.return_value.start.assert_called_once_with()
4040
4041 def test_install_only_calls_once_when_unfinished(self):
4042 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4043- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4044+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4045 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4046 c.install('darkside')
4047 c.install('darkside')
4048 c.install('darkside')
4049- MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4050+ MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4051 MockInstallTask.return_value.start.assert_called_once_with()
4052
4053 def test_remove_creates_remove_task(self):
4054 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4055- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4056+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4057 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
4058 c.remove('force')
4059- MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4060+ MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4061 MockRemoveTask.return_value.start.assert_called_once_with()
4062
4063 def test_remove_only_calls_once_when_unfinished(self):
4064 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4065- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4066+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4067 with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
4068 c.remove('darkside')
4069 c.remove('darkside')
4070 c.remove('darkside')
4071- MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4072+ MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4073 MockRemoveTask.return_value.start.assert_called_once_with()
4074
4075 def test_create_creates_create_task(self):
4076 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4077- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4078+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4079 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
4080 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4081 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
4082- self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4083+ self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4084 MockCreateTask.return_value.start.assert_called_once_with()
4085
4086 def test_create_only_calls_once_when_unfinished(self):
4087 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4088- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4089+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4090 with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
4091 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4092 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4093 c.create('Emperor Palpatine', 'zesty', 'lxd', False)
4094 MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
4095- self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4096+ self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4097 MockCreateTask.return_value.start.assert_called_once_with()
4098
4099 def test_destroy_creates_destroy_task(self):
4100 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4101- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4102+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4103 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
4104 c.destroy()
4105- MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4106+ MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4107 MockDestroyTask.return_value.start.assert_called_once_with()
4108
4109 def test_destroy_only_calls_once_when_unfinished(self):
4110 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4111- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4112+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4113 with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
4114 c.destroy()
4115 c.destroy()
4116 c.destroy()
4117- MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4118+ MockDestroyTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4119 MockDestroyTask.return_value.start.assert_called_once_with()
4120
4121 def test_update_creates_update_task(self):
4122 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4123- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4124+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4125 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
4126 c.update()
4127- MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4128+ MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4129 MockUpdateTask.return_value.start.assert_called_once_with()
4130
4131 def test_update_only_calls_once_when_unfinished(self):
4132 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4133- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4134+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4135 with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
4136 c.update()
4137 c.update()
4138 c.update()
4139- MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY)
4140+ MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._monitor, self._client, unittest.mock.ANY)
4141 MockUpdateTask.return_value.start.assert_called_once_with()
4142
4143 def test_list_app_ids_creates_list_app_ids_task(self):
4144 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4145- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4146+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4147 with unittest.mock.patch('libertine.service.container.ListAppIdsTask') as MockListAppIdsTask:
4148 c.list_app_ids()
4149- MockListAppIdsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY)
4150+ MockListAppIdsTask.assert_called_once_with('palpatine', self._config, self._monitor, self._client, unittest.mock.ANY)
4151 MockListAppIdsTask.return_value.start.assert_called_once_with()
4152
4153 def test_removes_task_during_callback(self):
4154 with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
4155- c = container.Container('palpatine', self._config, self._connection, lambda task: task)
4156+ c = container.Container('palpatine', self._config, self._monitor, self._client, lambda task: task)
4157 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4158 MockInstallTask.return_value.package = 'force'
4159 c.install('force')
4160@@ -176,7 +177,7 @@
4161 self._container_id = None
4162 def callback(container):
4163 self._container_id = container.id
4164- c = container.Container('palpatine', self._config, self._connection, callback)
4165+ c = container.Container('palpatine', self._config, self._monitor, self._client, callback)
4166 with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
4167 c.install('force')
4168 name, args, kwargs = MockInstallTask.mock_calls[0]
4169
4170=== added file 'tests/unit/service/test_operations_monitor.py'
4171--- tests/unit/service/test_operations_monitor.py 1970-01-01 00:00:00 +0000
4172+++ tests/unit/service/test_operations_monitor.py 2017-03-31 20:05:48 +0000
4173@@ -0,0 +1,146 @@
4174+# Copyright 2017 Canonical Ltd.
4175+#
4176+# This program is free software: you can redistribute it and/or modify it
4177+# under the terms of the GNU General Public License version 3, as published
4178+# by the Free Software Foundation.
4179+#
4180+# This program is distributed in the hope that it will be useful, but
4181+# WITHOUT ANY WARRANTY; without even the implied warranties of
4182+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4183+# PURPOSE. See the GNU General Public License for more details.
4184+#
4185+# You should have received a copy of the GNU General Public License along
4186+# with this program. If not, see <http://www.gnu.org/licenses/>.
4187+
4188+import unittest.mock
4189+from unittest import TestCase
4190+from libertine.service import operations_monitor, download
4191+
4192+
4193+class TestOperationsMonitor(TestCase):
4194+ def setUp(self):
4195+ self._connection = unittest.mock.Mock()
4196+
4197+ def test_new_operation_returns_some_id(self):
4198+ with unittest.mock.patch('dbus.service.Object'):
4199+ monitor = operations_monitor.OperationsMonitor(self._connection)
4200+ monitor._connection = self._connection
4201+
4202+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4203+ self.assertIsNotNone(monitor.new_operation())
4204+
4205+ def test_remove_connection(self):
4206+ with unittest.mock.patch('dbus.service.Object'):
4207+ monitor = operations_monitor.OperationsMonitor(self._connection)
4208+ monitor._connection = self._connection
4209+
4210+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4211+ monitor.remove_from_connection(monitor.new_operation())
4212+ MockDownload.return_value.remove_from_connection.assert_called_once_with()
4213+
4214+ def test_returns_done_for_operation(self):
4215+ with unittest.mock.patch('dbus.service.Object'):
4216+ monitor = operations_monitor.OperationsMonitor(self._connection)
4217+ monitor._connection = self._connection
4218+
4219+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4220+ MockDownload.return_value.done = True
4221+ self.assertTrue(monitor.done(monitor.new_operation()))
4222+
4223+ MockDownload.return_value.done = False
4224+ self.assertFalse(monitor.done(monitor.new_operation()))
4225+
4226+ # non-existent operation
4227+ self.assertFalse(monitor.done("123456"))
4228+
4229+ def test_running_returns_download_state(self):
4230+ with unittest.mock.patch('dbus.service.Object'):
4231+ monitor = operations_monitor.OperationsMonitor(self._connection)
4232+ monitor._connection = self._connection
4233+
4234+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4235+ MockDownload.return_value.done = True
4236+ self.assertFalse(monitor.running(monitor.new_operation()))
4237+
4238+ MockDownload.return_value.done = False
4239+ self.assertTrue(monitor.running(monitor.new_operation()))
4240+
4241+ # non-existent operation
4242+ self.assertFalse(monitor.running("123456"))
4243+
4244+ def test_result_returns_download_results(self):
4245+ with unittest.mock.patch('dbus.service.Object'):
4246+ monitor = operations_monitor.OperationsMonitor(self._connection)
4247+ monitor._connection = self._connection
4248+
4249+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4250+ MockDownload.return_value.result = "pokemongus"
4251+ self.assertEqual("pokemongus", monitor.result(monitor.new_operation()))
4252+
4253+ # non-existent operation
4254+ self.assertEqual("", monitor.result("123456"))
4255+
4256+ def test_last_error_returns_download_last_errors(self):
4257+ with unittest.mock.patch('dbus.service.Object'):
4258+ monitor = operations_monitor.OperationsMonitor(self._connection)
4259+ monitor._connection = self._connection
4260+
4261+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4262+ MockDownload.return_value.last_error = "pokemongus"
4263+ self.assertEqual("pokemongus", monitor.last_error(monitor.new_operation()))
4264+
4265+ # non-existent operation
4266+ self.assertEqual("", monitor.last_error("123456"))
4267+
4268+ def test_forwards_finished(self):
4269+ with unittest.mock.patch('dbus.service.Object'):
4270+ monitor = operations_monitor.OperationsMonitor(self._connection)
4271+ monitor._connection = self._connection
4272+ monitor._locations = []
4273+
4274+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4275+ path = monitor.new_operation()
4276+ monitor.finished(path)
4277+ MockDownload.return_value.finished.assert_called_once_with(path)
4278+
4279+ # test does not crash on empty
4280+ MockDownload.reset_mock()
4281+ monitor.finished("some/junk")
4282+ MockDownload.return_value.finished.assert_not_called()
4283+
4284+ def test_forwards_error(self):
4285+ with unittest.mock.patch('dbus.service.Object'):
4286+ monitor = operations_monitor.OperationsMonitor(self._connection)
4287+ monitor._connection = self._connection
4288+ monitor._locations = []
4289+
4290+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4291+ path = monitor.new_operation()
4292+ monitor.error(path, "something messed up")
4293+ MockDownload.return_value.error.assert_called_once_with("something messed up")
4294+
4295+ # test does not crash on empty
4296+ MockDownload.reset_mock()
4297+ monitor.error("some/junk", "something messed up")
4298+ MockDownload.return_value.error.assert_not_called()
4299+
4300+ def test_forwards_data(self):
4301+ with unittest.mock.patch('dbus.service.Object'):
4302+ monitor = operations_monitor.OperationsMonitor(self._connection)
4303+ monitor._connection = self._connection
4304+ monitor._locations = []
4305+
4306+ with unittest.mock.patch('libertine.service.operations_monitor.download.Download') as MockDownload:
4307+ path = monitor.new_operation()
4308+ monitor.data(path, "some of that gud data")
4309+ MockDownload.return_value.data.assert_called_once_with("some of that gud data")
4310+
4311+ # test does not crash on empty
4312+ MockDownload.reset_mock()
4313+ monitor.data("some/junk", "some of that gud data")
4314+ MockDownload.return_value.data.assert_not_called()
4315+
4316+
4317+
4318+if __name__ == '__main__':
4319+ unittest.main()
4320
4321=== modified file 'tests/unit/service/test_task_dispatcher.py'
4322--- tests/unit/service/test_task_dispatcher.py 2017-02-07 12:35:48 +0000
4323+++ tests/unit/service/test_task_dispatcher.py 2017-03-31 20:05:48 +0000
4324@@ -20,9 +20,10 @@
4325 class TestTaskDispatcher(TestCase):
4326 def setUp(self):
4327 self._connection = unittest.mock.Mock()
4328+ self._client = unittest.mock.Mock()
4329 self._config_patcher = unittest.mock.patch('libertine.service.task_dispatcher.libertine.ContainersConfig.ContainersConfig')
4330 self._config_patcher.start()
4331- self._dispatcher = task_dispatcher.TaskDispatcher(self._connection)
4332+ self._dispatcher = task_dispatcher.TaskDispatcher(self._connection, self._client)
4333
4334 def tearDown(self):
4335 self._config_patcher.stop()
4336@@ -90,20 +91,20 @@
4337 self._dispatcher.list_app_ids('palpatine')
4338 self._dispatcher.list_app_ids('palpatine')
4339 self._dispatcher.list_app_ids('palpatine')
4340- MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)
4341+ MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY)
4342
4343 def test_container_callback_removes_container(self):
4344 with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
4345 c = MockContainer.return_value
4346 c.id = 'palpatine'
4347 self._dispatcher.list_app_ids('palpatine')
4348- MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)
4349+ MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY)
4350 name, args, kwargs = MockContainer.mock_calls[0]
4351 args[len(args)-1](MockContainer.return_value)
4352 self._dispatcher.list_app_ids('palpatine')
4353 MockContainer.assert_has_calls([ # verify container constructed twice
4354- unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY),
4355- unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY)
4356+ unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY),
4357+ unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, self._client, unittest.mock.ANY)
4358 ], any_order=True)
4359
4360 def test_container_info_creates_container_info_task(self):
4361
4362=== modified file 'tools/libertine-container-manager'
4363--- tools/libertine-container-manager 2017-03-03 18:34:34 +0000
4364+++ tools/libertine-container-manager 2017-03-31 20:05:48 +0000
4365@@ -24,6 +24,10 @@
4366 import sys
4367 import re
4368
4369+import gettext
4370+gettext.textdomain('libertine')
4371+_ = gettext.gettext
4372+
4373 from libertine import ContainerRunning, LibertineContainer
4374 from libertine.ContainersConfig import ContainersConfig
4375 from libertine.HostInfo import HostInfo
4376@@ -35,6 +39,16 @@
4377 self.containers_config = ContainersConfig()
4378 self.host_info = HostInfo()
4379
4380+
4381+ def _container(self, container_id):
4382+ try:
4383+ return LibertineContainer(container_id, self.containers_config)
4384+ except ImportError as e:
4385+ container_type = self.containers_config.get_container_type(container_id)
4386+ libertine.utils.get_logger().error(_("Backend for container '{id}' not installed. Install "
4387+ "'python3-libertine-{type}' and try again.").format(id=container_id, type=container_type))
4388+ sys.exit(1)
4389+
4390 def _get_updated_locale(self, container_id):
4391 host_locale = self.host_info.get_host_locale()
4392
4393@@ -99,16 +113,18 @@
4394
4395 try:
4396 self.containers_config.update_container_locale(args.id, self.host_info.get_host_locale())
4397- container = LibertineContainer(args.id)
4398- self.containers_config.update_container_install_status(args.id, "installing")
4399- if not container.create_libertine_container(password, args.multiarch):
4400- libertine.utils.get_logger().error("Failed to create container")
4401- self.containers_config.delete_container(args.id)
4402- sys.exit(1)
4403+ container = LibertineContainer(args.id, self.containers_config)
4404+ try:
4405+ self.containers_config.update_container_install_status(args.id, "installing")
4406+ if not container.create_libertine_container(password, args.multiarch):
4407+ libertine.utils.get_logger().error("Failed to create container")
4408+ self.containers_config.delete_container(args.id)
4409+ sys.exit(1)
4410+ except Exception as e:
4411+ container.destroy_libertine_container(force=True)
4412+ raise
4413 except Exception as e:
4414 libertine.utils.get_logger().error("Failed to create container: '{}'".format(str(e)))
4415- if container:
4416- container.destroy_libertine_container(force=True)
4417
4418 self.containers_config.delete_container(args.id)
4419 sys.exit(1)
4420@@ -130,7 +146,7 @@
4421
4422 def destroy(self, args):
4423 container_id = self.containers_config.check_container_id(args.id)
4424- container = LibertineContainer(container_id, self.containers_config)
4425+ container = self._container(container_id)
4426
4427 self.destroy_container(container, args.force)
4428
4429@@ -138,11 +154,14 @@
4430
4431 def install_package(self, args):
4432 container_id = self.containers_config.check_container_id(args.id)
4433- container = LibertineContainer(container_id, self.containers_config)
4434+ container = self._container(container_id)
4435 failure = False
4436
4437 with ContainerRunning(container.container):
4438 for i, pkg in enumerate(args.package):
4439+ if not pkg:
4440+ continue
4441+
4442 is_debian_package = pkg.endswith('.deb')
4443
4444 if is_debian_package:
4445@@ -193,11 +212,14 @@
4446
4447 def remove_package(self, args):
4448 container_id = self.containers_config.check_container_id(args.id)
4449- container = LibertineContainer(container_id)
4450+ container = self._container(container_id)
4451 failure = False
4452
4453 with ContainerRunning(container.container):
4454 for pkg in args.package:
4455+ if not pkg:
4456+ continue
4457+
4458 if self.containers_config.get_package_install_status(container_id, pkg) != 'installed':
4459 libertine.utils.get_logger().error("Package \'%s\' is not installed." % pkg)
4460 failure = True
4461@@ -216,8 +238,8 @@
4462
4463 def search_cache(self, args):
4464 container_id = self.containers_config.check_container_id(args.id)
4465+ container = self._container(container_id)
4466
4467- container = LibertineContainer(container_id)
4468 if container.search_package_cache(args.search_string) is not 0:
4469 libertine.utils.get_logger().error("Search for '{}' in container '{}' exited with non-zero status"
4470 .format(args.id, args.search_string))
4471@@ -225,10 +247,10 @@
4472
4473 def update(self, args):
4474 container_id = self.containers_config.check_container_id(args.id)
4475+ container = self._container(container_id)
4476+
4477 new_locale = self._get_updated_locale(container_id)
4478
4479- container = LibertineContainer(container_id)
4480-
4481 if not container.update_libertine_container(new_locale):
4482 sys.exit(1)
4483
4484@@ -242,7 +264,7 @@
4485 def list_apps(self, args):
4486 container_id = self.containers_config.check_container_id(args.id)
4487
4488- app_ids = LibertineContainer(container_id).list_app_ids()
4489+ app_ids = self._container(container_id).list_app_ids()
4490 if args.json:
4491 print(json.dumps(app_ids))
4492 else:
4493@@ -252,7 +274,7 @@
4494 def exec(self, args):
4495 container_id = self.containers_config.check_container_id(args.id)
4496
4497- container = LibertineContainer(container_id)
4498+ container = self._container(container_id)
4499
4500 if not container.exec_command(args.command):
4501 sys.exit(1)
4502@@ -269,7 +291,7 @@
4503
4504 def configure(self, args):
4505 container_id = self.containers_config.check_container_id(args.id)
4506- container = LibertineContainer(container_id)
4507+ container = self._container(container_id)
4508
4509 if args.multiarch and self.host_info.get_host_architecture() == 'amd64':
4510 multiarch = 'disabled'
4511@@ -377,7 +399,7 @@
4512 def fix_integrity(self, args):
4513 if 'containerList' in self.containers_config.container_list:
4514 for container in self.containers_config.container_list['containerList']:
4515- libertine_container = LibertineContainer(container['id'])
4516+ libertine_container = self._container(container['id'])
4517
4518 if 'installStatus' not in container or container['installStatus'] == 'removing':
4519 self.destroy_container(libertine_container)
4520@@ -411,7 +433,7 @@
4521 libertine.utils.get_logger().error("The restart subcommand is only valid for LXC and LXD type containers.")
4522 sys.exit(1)
4523
4524- container = LibertineContainer(container_id)
4525+ container = self._container(container_id)
4526
4527 container.restart_libertine_container()
4528
4529
4530=== modified file 'tools/libertine-launch'
4531--- tools/libertine-launch 2017-02-23 18:18:29 +0000
4532+++ tools/libertine-launch 2017-03-31 20:05:48 +0000
4533@@ -19,6 +19,10 @@
4534 import os
4535 import sys
4536
4537+import gettext
4538+gettext.textdomain('libertine')
4539+_ = gettext.gettext
4540+
4541 from libertine import launcher
4542
4543 def main():
4544@@ -30,8 +34,14 @@
4545 utils.get_logger().error("No container with id '%s'" % config.container_id)
4546 sys.exit(1)
4547
4548- from libertine import LibertineContainer
4549- container = LibertineContainer(container_id=config.container_id)
4550+ try:
4551+ from libertine import LibertineContainer
4552+ container = LibertineContainer(container_id=config.container_id)
4553+ except ImportError as e:
4554+ container_type = self.containers_config.get_container_type(container_id)
4555+ libertine.utils.get_logger().error(_("Backend for container '{id}' not installed. Install "
4556+ "'python3-libertine-{type}' and try again.").format(id=config.container_id, type=container_type))
4557+ sys.exit(1)
4558 else:
4559 from libertine import NoContainer
4560
4561
4562=== modified file 'tools/libertine-lxd-setup'
4563--- tools/libertine-lxd-setup 2017-01-06 15:32:00 +0000
4564+++ tools/libertine-lxd-setup 2017-03-31 20:05:48 +0000
4565@@ -20,20 +20,43 @@
4566 echo ${idmap} | tee -a /etc/subuid /etc/subgid
4567 fi
4568
4569-# find the right lxc command
4570-lxc=`which lxc`
4571-if [ -z "${lxc}" ]; then
4572- if [ -n `which lxd.lxc` ]; then
4573- lxc=`which lxd.lxc`
4574- else
4575- echo "No lxc command found on this system."
4576- exit 1
4577- fi
4578-fi
4579-
4580-# Run lxd init if there are no containers already on the system
4581-if [ 2 -ge `${lxc} list | grep -e "^+" | wc -l` ]; then
4582- lxd init
4583+
4584+lxc profile device list default | grep "nic\|disk" > /dev/null
4585+requires_init=$?
4586+version=`lxd --version`
4587+if [ ${version%%.*} -lt 2 -o ${version#*.} -lt 3 ]; then
4588+ if [ "$requires_init" -ne "0" ]; then
4589+ # Running lxd init without args to force the caller to pick the options
4590+ lxd init
4591+ fi
4592+else
4593+ if [ "$requires_init" -ne "0" ]; then
4594+ # Running lxd init with auto flag to choose most of the right options
4595+ lxd init --auto
4596+ fi
4597+
4598+ # networking
4599+ ifconfig lxdbr0 > /dev/null
4600+ bridge_exists=$?
4601+
4602+ lxc network show lxdbr0 > /dev/null
4603+ network_exists=$?
4604+
4605+ # no managed network, no bridge means we need to create a managed network
4606+ if [ "$bridge_exists" -ne "0" ]; then
4607+ if [ "$network_exists" -ne "0" ]; then
4608+ lxc network create lxdbr0
4609+
4610+ network_name=`lxc profile device get default eth0 parent`
4611+ if [ "$network_name" != "lxdbr0" ]; then
4612+ lxc network attach-profile lxdbr0 default eth0
4613+ fi
4614+ else
4615+ # a managed network and no bridge means the bridge should be recreated
4616+ # when we restart the lxd service
4617+ service lxd restart
4618+ fi
4619+ fi
4620 fi
4621
4622 mkdir -p /home/$USERNAME/.config/lxc
4623
4624=== modified file 'tools/libertined'
4625--- tools/libertined 2017-02-15 21:12:37 +0000
4626+++ tools/libertined 2017-03-31 20:05:48 +0000
4627@@ -1,7 +1,7 @@
4628 #!/usr/bin/python3
4629 # -*- coding: utf-8 -*-
4630
4631-# Copyright 2016 Canonical Ltd.
4632+# Copyright 2016-2017 Canonical Ltd.
4633 #
4634 # This program is free software: you can redistribute it and/or modify
4635 # it under the terms of the GNU General Public License as published by
4636@@ -15,13 +15,17 @@
4637 # You should have received a copy of the GNU General Public License
4638 # along with this program. If not, see <http://www.gnu.org/licenses/>.
4639
4640+
4641 import argparse
4642+import dbus
4643+import os
4644 import signal
4645 import sys
4646-import os
4647-from gi.repository import GLib, GObject
4648+
4649+from dbus.mainloop.glib import DBusGMainLoop
4650+from gi.repository import GLib
4651 from libertine import utils
4652-from libertine.service import manager
4653+from libertine.service import constants, operations, container_control, container_control_client
4654
4655
4656 class OutputRedirector(object):
4657@@ -116,6 +120,7 @@
4658 signal.SIGTERM,
4659 self.sigterm,
4660 None)
4661+ DBusGMainLoop(set_as_default=True)
4662 self.loop = GLib.MainLoop()
4663
4664 def sigterm(self, code):
4665@@ -135,13 +140,29 @@
4666 config = Config()
4667
4668 with OutputRedirector(config):
4669- utils.get_logger().info("Starting libertine service")
4670- service = manager.Manager()
4671+ utils.get_logger().info("Initializing libertined...")
4672 loop = Loop()
4673- try:
4674+
4675+ try:
4676+ bus_name = dbus.service.BusName(constants.SERVICE_NAME,
4677+ bus=dbus.SessionBus(),
4678+ do_not_queue=True)
4679+ except dbus.exceptions.NameExistsException:
4680+ utils.get_logger().warning("service is already running")
4681+ raise
4682+
4683+ client = container_control_client.ContainerControlClient()
4684+ manager = operations.Operations(bus_name, client)
4685+ container_control.ContainerControl(manager.connection, client)
4686+
4687+ try:
4688+ utils.get_logger().info("libertined ready")
4689 loop.run()
4690 except KeyboardInterrupt:
4691 utils.get_logger().debug("keyboard interrupt received")
4692+ except Exception as e:
4693+ utils.get_logger().error("Unexpected exception occurred: '{}'".format(str(e)))
4694+ finally:
4695 loop.shutdown()
4696
4697

Subscribers

People subscribed via source and target branches