Merge lp:~larryprice/libertine/libertine-service into lp:libertine
- libertine-service
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Christopher Townsend |
Approved revision: | 372 |
Merged at revision: | 334 |
Proposed branch: | lp:~larryprice/libertine/libertine-service |
Merge into: | lp:libertine |
Diff against target: |
2873 lines (+2532/-14) 46 files modified
data/CMakeLists.txt (+1/-1) data/com.canonical.libertine.ContainerManager.service (+3/-0) debian/libertine-tools.install (+2/-0) debian/python3-libertine.install (+1/-0) python/libertine/ChrootContainer.py (+3/-1) python/libertine/ContainersConfig.py (+7/-5) python/libertine/HostInfo.py (+1/-1) python/libertine/Libertine.py (+1/-1) python/libertine/LxcContainer.py (+6/-3) python/libertine/service/apt.py (+77/-0) python/libertine/service/container.py (+139/-0) python/libertine/service/manager.py (+115/-0) python/libertine/service/progress.py (+83/-0) python/libertine/service/task_dispatcher.py (+114/-0) python/libertine/service/tasks/__init__.py (+39/-0) python/libertine/service/tasks/app_info_task.py (+35/-0) python/libertine/service/tasks/base_task.py (+94/-0) python/libertine/service/tasks/container_info_task.py (+29/-0) python/libertine/service/tasks/create_task.py (+72/-0) python/libertine/service/tasks/destroy_task.py (+42/-0) python/libertine/service/tasks/install_task.py (+49/-0) python/libertine/service/tasks/list_apps_task.py (+30/-0) python/libertine/service/tasks/list_task.py (+25/-0) python/libertine/service/tasks/remove_task.py (+48/-0) python/libertine/service/tasks/search_task.py (+27/-0) python/libertine/service/tasks/update_task.py (+39/-0) tests/unit/CMakeLists.txt (+1/-0) tests/unit/pytest.ini (+2/-0) tests/unit/service/CMakeLists.txt (+12/-0) tests/unit/service/tasks/CMakeLists.txt (+10/-0) tests/unit/service/tasks/test_app_info_task.py (+61/-0) tests/unit/service/tasks/test_container_info_task.py (+44/-0) tests/unit/service/tasks/test_create_task.py (+234/-0) tests/unit/service/tasks/test_destroy_task.py (+80/-0) tests/unit/service/tasks/test_install_task.py (+76/-0) tests/unit/service/tasks/test_list_apps_task.py (+59/-0) tests/unit/service/tasks/test_list_task.py (+45/-0) tests/unit/service/tasks/test_remove_task.py (+76/-0) tests/unit/service/tasks/test_search_task.py (+43/-0) tests/unit/service/tasks/test_update_task.py (+80/-0) tests/unit/service/test_apt.py (+134/-0) tests/unit/service/test_container.py (+202/-0) tests/unit/service/test_task_dispatcher.py (+148/-0) tools/CMakeLists.txt (+1/-1) tools/libertine-container-manager (+1/-1) tools/libertined (+141/-0) |
To merge this branch: | bzr merge lp:~larryprice/libertine/libertine-service |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Townsend | Approve | ||
Libertine CI Bot | continuous-integration | Approve | |
Review via email: mp+309794@code.launchpad.net |
Commit message
Initial implementation of a libertine d-bus service.
Description of the change
Initial implementation of a libertine d-bus service.
How do it work? Every command is asynchronous. After making a call to the service, you'll get an object path back. You can listen on that object path for incoming data or listen for the task to finish with finished (success) or error (failure). It currently emits a processing signal every .5 seconds to let things like the Scopes SDK know there is an ongoing process, but we might want to consider removing this if we don't plan to be used by a specific scope.
What's not implemented? All things configure, fix-integrity, exec, merge. There is currently no way to get output directly from the running command (such as install/
Libertine CI Bot (libertine-ci-bot) wrote : | # |
- 356. By Larry Price
-
Merge
- 357. By Larry Price
-
attempt to get this file in
- 358. By Larry Price
-
checkpoint
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:358
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 359. By Larry Price
-
trying to fix test_task_
dispatcher
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:359
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 360. By Larry Price
-
Removed appstream (find it at lp:~larryprice/+junk/libertine-service-with-appstream)
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:360
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 361. By Larry Price
-
Restructure checking task equality for the sake of vivid
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:361
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 362. By Larry Price
-
use a real function instead of a magic function to test fuzzy equality
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:362
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 363. By Larry Price
-
Merge
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:363
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 364. By Larry Price
-
Update for status reporting and delayed callback
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:364
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 365. By Larry Price
-
address minor threading issues
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:365
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 366. By Larry Price
-
update task retrieval
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:366
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 367. By Larry Price
-
upstreaming changes from integration branch
- 368. By Larry Price
-
fixing all the things
- 369. By Larry Price
-
stop double-running tests
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:368
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:369
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 370. By Larry Price
-
upstreaming changes from integration tests
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:370
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 371. By Larry Price
-
remove unused appstream dep
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:371
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:371
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 372. By Larry Price
-
merge
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:372
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Christopher Townsend (townsend) wrote : | # |
Ok, let's get this in:)
Preview Diff
1 | === modified file 'data/CMakeLists.txt' |
2 | --- data/CMakeLists.txt 2016-10-07 18:58:30 +0000 |
3 | +++ data/CMakeLists.txt 2016-11-16 17:34:14 +0000 |
4 | @@ -8,7 +8,7 @@ |
5 | DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) |
6 | install(FILES libertine-lxc-sudo |
7 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d) |
8 | -install(FILES com.canonical.libertine.LxcManager.service |
9 | +install(FILES com.canonical.libertine.LxcManager.service com.canonical.libertine.ContainerManager.service |
10 | DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services) |
11 | |
12 | configure_file("python3-libertine-chroot.click-hook.in" |
13 | |
14 | === added file 'data/com.canonical.libertine.ContainerManager.service' |
15 | --- data/com.canonical.libertine.ContainerManager.service 1970-01-01 00:00:00 +0000 |
16 | +++ data/com.canonical.libertine.ContainerManager.service 2016-11-16 17:34:14 +0000 |
17 | @@ -0,0 +1,3 @@ |
18 | +[D-BUS Service] |
19 | +Name=com.canonical.libertine.ContainerManager |
20 | +Exec=/usr/bin/libertined --cache-output |
21 | |
22 | === modified file 'debian/libertine-tools.install' |
23 | --- debian/libertine-tools.install 2016-10-26 20:48:08 +0000 |
24 | +++ debian/libertine-tools.install 2016-11-16 17:34:14 +0000 |
25 | @@ -1,4 +1,6 @@ |
26 | usr/bin/libertine-container-manager |
27 | usr/bin/libertine-launch |
28 | +usr/bin/libertined |
29 | +usr/share/dbus-1/services/com.canonical.libertine.ContainerManager.service |
30 | usr/share/bash-completion/completions/libertine-container-manager |
31 | usr/share/man |
32 | |
33 | === modified file 'debian/python3-libertine.install' |
34 | --- debian/python3-libertine.install 2016-10-13 18:28:34 +0000 |
35 | +++ debian/python3-libertine.install 2016-11-16 17:34:14 +0000 |
36 | @@ -4,4 +4,5 @@ |
37 | usr/lib/python*/*/libertine/Libertine.py |
38 | usr/lib/python*/*/libertine/__init__.py |
39 | usr/lib/python*/*/libertine/launcher |
40 | +usr/lib/python*/*/libertine/service |
41 | usr/lib/python*/*/libertine/utils.py |
42 | |
43 | === modified file 'python/libertine/ChrootContainer.py' |
44 | --- python/libertine/ChrootContainer.py 2016-11-16 16:38:16 +0000 |
45 | +++ python/libertine/ChrootContainer.py 2016-11-16 17:34:14 +0000 |
46 | @@ -64,8 +64,10 @@ |
47 | container_root = os.path.join(utils.get_libertine_containers_dir_path(), self.container_id) |
48 | try: |
49 | shutil.rmtree(container_root) |
50 | + return True |
51 | except Exception as e: |
52 | print("%s" % e) |
53 | + return False |
54 | |
55 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
56 | # Create the actual chroot |
57 | @@ -147,7 +149,7 @@ |
58 | def update_packages(self, verbosity=1): |
59 | retcode = super().update_packages(verbosity) |
60 | self._run_ldconfig(verbosity) |
61 | - return retcode |
62 | + return retcode == 0 |
63 | |
64 | def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): |
65 | returncode = super().install_package(package_name, verbosity, no_dialog, update_cache) |
66 | |
67 | === modified file 'python/libertine/ContainersConfig.py' |
68 | --- python/libertine/ContainersConfig.py 2016-11-09 18:25:33 +0000 |
69 | +++ python/libertine/ContainersConfig.py 2016-11-16 17:34:14 +0000 |
70 | @@ -28,6 +28,7 @@ |
71 | if (os.path.exists(container_config_file) and |
72 | os.path.getsize(container_config_file) != 0): |
73 | with open(container_config_file, 'r') as fd: |
74 | + fcntl.flock(fd, fcntl.LOCK_EX) |
75 | container_list = json.load(fd) |
76 | |
77 | return container_list |
78 | @@ -41,18 +42,18 @@ |
79 | container_list["_warning"] = "This file is automatically generated by Libertine and should not be manually edited." |
80 | |
81 | with open(container_config_file, 'w') as fd: |
82 | - fcntl.lockf(fd, fcntl.LOCK_EX) |
83 | + fcntl.flock(fd, fcntl.LOCK_EX) |
84 | json.dump(container_list, fd, sort_keys=True, indent=4) |
85 | fd.write('\n') |
86 | - fcntl.lockf(fd, fcntl.LOCK_UN) |
87 | |
88 | |
89 | def container_config_hash(): |
90 | checksum = md5() |
91 | container_config_file = libertine.utils.get_libertine_database_file_path() |
92 | if (os.path.exists(container_config_file) and os.path.getsize(container_config_file) != 0): |
93 | - with open(container_config_file, "rb") as f: |
94 | - for chunk in iter(lambda: f.read(128 * checksum.block_size), b""): |
95 | + with open(container_config_file, "rb") as fd: |
96 | + fcntl.flock(fd, fcntl.LOCK_EX) |
97 | + for chunk in iter(lambda: fd.read(128 * checksum.block_size), b""): |
98 | checksum.update(chunk) |
99 | return checksum.hexdigest() |
100 | |
101 | @@ -90,7 +91,8 @@ |
102 | return container[key] |
103 | |
104 | def _get_array_object_value_by_key(self, container_id, array_key, object_key, matcher, key): |
105 | - for item in self._get_value_by_key(container_id, array_key): |
106 | + items = self._get_value_by_key(container_id, array_key) or [] |
107 | + for item in items: |
108 | if item[object_key] == matcher: |
109 | return item[key] |
110 | |
111 | |
112 | === modified file 'python/libertine/HostInfo.py' |
113 | --- python/libertine/HostInfo.py 2016-07-07 20:00:10 +0000 |
114 | +++ python/libertine/HostInfo.py 2016-11-16 17:34:14 +0000 |
115 | @@ -36,7 +36,7 @@ |
116 | |
117 | return distinfo.get('CODENAME', 'n/a') |
118 | |
119 | - def is_distro_valid(self, distro, force): |
120 | + def is_distro_valid(self, distro, force=False): |
121 | if force: |
122 | return UbuntuDistroInfo().valid(distro) |
123 | |
124 | |
125 | === modified file 'python/libertine/Libertine.py' |
126 | --- python/libertine/Libertine.py 2016-11-10 16:36:05 +0000 |
127 | +++ python/libertine/Libertine.py 2016-11-16 17:34:14 +0000 |
128 | @@ -166,7 +166,7 @@ |
129 | :param verbosity: the chattiness of the output on a range from 0 to 2 |
130 | """ |
131 | self.update_apt_cache(verbosity) |
132 | - return self.run_in_container(apt_command_prefix(verbosity) + '--force-yes dist-upgrade') |
133 | + return self.run_in_container(apt_command_prefix(verbosity) + '--force-yes dist-upgrade') == 0 |
134 | |
135 | def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): |
136 | """ |
137 | |
138 | === modified file 'python/libertine/LxcContainer.py' |
139 | --- python/libertine/LxcContainer.py 2016-11-10 20:43:40 +0000 |
140 | +++ python/libertine/LxcContainer.py 2016-11-16 17:34:14 +0000 |
141 | @@ -199,9 +199,12 @@ |
142 | return super().update_packages(verbosity) |
143 | |
144 | def destroy_libertine_container(self): |
145 | - if self.container.defined: |
146 | - self.container.stop() |
147 | - self.container.destroy() |
148 | + if not self.container.defined: |
149 | + return False |
150 | + |
151 | + self.container.stop() |
152 | + self.container.destroy() |
153 | + return True |
154 | |
155 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
156 | if password is None: |
157 | |
158 | === added directory 'python/libertine/service' |
159 | === added file 'python/libertine/service/__init__.py' |
160 | === added file 'python/libertine/service/apt.py' |
161 | --- python/libertine/service/apt.py 1970-01-01 00:00:00 +0000 |
162 | +++ python/libertine/service/apt.py 2016-11-16 17:34:14 +0000 |
163 | @@ -0,0 +1,77 @@ |
164 | +# Copyright 2016 Canonical Ltd. |
165 | +# |
166 | +# This program is free software: you can redistribute it and/or modify |
167 | +# it under the terms of the GNU General Public License as published by |
168 | +# the Free Software Foundation; version 3 of the License. |
169 | +# |
170 | +# This program is distributed in the hope that it will be useful, |
171 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
172 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
173 | +# GNU General Public License for more details. |
174 | +# |
175 | +# You should have received a copy of the GNU General Public License |
176 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
177 | + |
178 | +import apt |
179 | +import re |
180 | + |
181 | +import gi |
182 | +gi.require_version('Libertine', '1') |
183 | +from gi.repository import Libertine |
184 | + |
185 | +from libertine import utils |
186 | +from os import path |
187 | +from threading import Lock |
188 | + |
189 | + |
190 | +class AptCache(object): |
191 | + """ |
192 | + Class to find app information using apt-cache |
193 | + """ |
194 | + def __init__(self, container_id): |
195 | + super(AptCache, self).__init__() |
196 | + self._container = container_id |
197 | + self._cache = None |
198 | + self._lock = Lock() |
199 | + |
200 | + def search(self, query): |
201 | + self._load() |
202 | + |
203 | + pkg_keys = [key for key in self._cache.keys() if re.match(query, key)] |
204 | + apps = [] |
205 | + for key in pkg_keys: |
206 | + apps.append(self._app_to_dict(key)) |
207 | + return apps |
208 | + |
209 | + def app_info(self, app_id): |
210 | + self._load() |
211 | + return self._app_to_dict(app_id) |
212 | + |
213 | + def _app_to_dict(self, app_id): |
214 | + app_data = {} |
215 | + if app_id in self._cache.keys(): |
216 | + app = self._cache[app_id] |
217 | + app_data["name"] = app.name |
218 | + app_data["id"] = app.name |
219 | + app_data["package"] = app.name |
220 | + if len(app.versions) > 0: |
221 | + app_data["summary"] = app.versions[0].summary |
222 | + app_data["website"] = app.versions[0].homepage |
223 | + app_data["description"] = app.versions[0].description |
224 | + app_data["package"] = app.name |
225 | + |
226 | + return app_data |
227 | + |
228 | + def _load(self): |
229 | + with self._lock: |
230 | + if self._cache is None: |
231 | + try: |
232 | + utils.get_logger().debug("Trying aptcache for container %s" % self._container) |
233 | + container_path = Libertine.container_path(self._container) |
234 | + if not container_path or not path.exists(container_path): |
235 | + raise PermissionError |
236 | + |
237 | + self._cache = apt.Cache(rootdir=container_path) |
238 | + except PermissionError: |
239 | + utils.get_logger().debug("Trying system aptcache") |
240 | + self._cache = apt.Cache() |
241 | |
242 | === added file 'python/libertine/service/container.py' |
243 | --- python/libertine/service/container.py 1970-01-01 00:00:00 +0000 |
244 | +++ python/libertine/service/container.py 2016-11-16 17:34:14 +0000 |
245 | @@ -0,0 +1,139 @@ |
246 | +# Copyright 2016 Canonical Ltd. |
247 | +# |
248 | +# This program is free software: you can redistribute it and/or modify |
249 | +# it under the terms of the GNU General Public License as published by |
250 | +# the Free Software Foundation; version 3 of the License. |
251 | +# |
252 | +# This program is distributed in the hope that it will be useful, |
253 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
254 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
255 | +# GNU General Public License for more details. |
256 | +# |
257 | +# You should have received a copy of the GNU General Public License |
258 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
259 | + |
260 | +from libertine.service.tasks import * |
261 | +from libertine import utils |
262 | +from libertine.service import apt |
263 | + |
264 | + |
265 | +class Container(object): |
266 | + def __init__(self, container_id, config, lock, connection, callback): |
267 | + self._id = container_id |
268 | + self._connection = connection |
269 | + self._callback = callback |
270 | + self._config = config |
271 | + self._lock = lock |
272 | + self._tasks = [] |
273 | + self._cache = apt.AptCache(self.id) |
274 | + |
275 | + def _cleanup_task(self, task): |
276 | + utils.get_logger().debug("cleaning up tasks for container '%s'" % self.id) |
277 | + |
278 | + if task in self._tasks: |
279 | + self._tasks.remove(task) |
280 | + |
281 | + if len(self._tasks) == 0: |
282 | + self._callback(self) |
283 | + |
284 | + @property |
285 | + def id(self): |
286 | + return self._id |
287 | + |
288 | + @property |
289 | + def tasks(self): |
290 | + return [task.id for task in self._tasks if task.running] |
291 | + |
292 | + def search(self, query): |
293 | + utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query)) |
294 | + |
295 | + task = SearchTask(self.id, self._cache, query, self._connection, self._cleanup_task) |
296 | + self._tasks.append(task) |
297 | + task.start() |
298 | + |
299 | + return task.id |
300 | + |
301 | + def app_info(self, package_name): |
302 | + utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id)) |
303 | + |
304 | + related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running] |
305 | + task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._connection, self._cleanup_task) |
306 | + |
307 | + self._tasks.append(task) |
308 | + task.start() |
309 | + return task.id |
310 | + |
311 | + def install(self, package_name): |
312 | + utils.get_logger().debug("Install package '%s' from container '%s'" % (package_name, self.id)) |
313 | + |
314 | + tasks = [t for t in self._tasks if t.matches(package_name, InstallTask) and t.running] |
315 | + if len(tasks) > 0: |
316 | + utils.get_logger().debug("Install already in progress for '%s':'%s'" % (package_name, self.id)) |
317 | + return tasks[0].id |
318 | + |
319 | + task = InstallTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task) |
320 | + self._tasks.append(task) |
321 | + task.start() |
322 | + return task.id |
323 | + |
324 | + def remove(self, package_name): |
325 | + utils.get_logger().debug("Remove package '%s' from container '%s'" % (package_name, self.id)) |
326 | + |
327 | + tasks = [t for t in self._tasks if t.matches(package_name, RemoveTask) and t.running] |
328 | + if len(tasks) > 0: |
329 | + utils.get_logger().debug("Remove already in progress for '%s':'%s'" % (package_name, self.id)) |
330 | + return tasks[0].id |
331 | + |
332 | + task = RemoveTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task) |
333 | + self._tasks.append(task) |
334 | + task.start() |
335 | + return task.id |
336 | + |
337 | + def create(self, container_name, distro, container_type, enable_multiarch): |
338 | + utils.get_logger().debug("Create container with ID '%s'" % self.id) |
339 | + |
340 | + tasks = [t for t in self._tasks if t.matches(self.id, CreateTask) and t.running] |
341 | + if len(tasks) > 0: |
342 | + utils.get_logger().debug("Create already in progress for '%s'" % self.id) |
343 | + return tasks[0].id |
344 | + |
345 | + task = CreateTask(self.id, container_name, distro, container_type, enable_multiarch, |
346 | + self._config, self._lock, self._connection, self._cleanup_task) |
347 | + self._tasks.append(task) |
348 | + task.start() |
349 | + return task.id |
350 | + |
351 | + def destroy(self): |
352 | + utils.get_logger().debug("Destroy container with ID '%s'" % self.id) |
353 | + |
354 | + tasks = [t for t in self._tasks if t.matches(self.id, DestroyTask) and t.running] |
355 | + if len(tasks) > 0: |
356 | + utils.get_logger().debug("Destroy already in progress for '%s'" % self.id) |
357 | + return tasks[0].id |
358 | + |
359 | + task = DestroyTask(self.id, self._config, self._lock, self._connection, self._cleanup_task) |
360 | + self._tasks.append(task) |
361 | + task.start() |
362 | + return task.id |
363 | + |
364 | + def update(self): |
365 | + utils.get_logger().debug("Update container with ID '%s'" % self.id) |
366 | + |
367 | + tasks = [t for t in self._tasks if t.matches(self.id, UpdateTask) and t.running] |
368 | + if len(tasks) > 0: |
369 | + utils.get_logger().debug("Update already in progress for '%s'" % self.id) |
370 | + return tasks[0].id |
371 | + |
372 | + task = UpdateTask(self.id, self._config, self._lock, self._connection, self._cleanup_task) |
373 | + self._tasks.append(task) |
374 | + task.start() |
375 | + return task.id |
376 | + |
377 | + def list_apps(self): |
378 | + utils.get_logger().debug("List all apps in container '%s'" % self.id) |
379 | + |
380 | + task = ListAppsTask(self.id, self._config, self._connection, self._cleanup_task) |
381 | + |
382 | + self._tasks.append(task) |
383 | + task.start() |
384 | + return task.id |
385 | |
386 | === added file 'python/libertine/service/manager.py' |
387 | --- python/libertine/service/manager.py 1970-01-01 00:00:00 +0000 |
388 | +++ python/libertine/service/manager.py 2016-11-16 17:34:14 +0000 |
389 | @@ -0,0 +1,115 @@ |
390 | +# Copyright 2016 Canonical Ltd. |
391 | +# |
392 | +# This program is free software: you can redistribute it and/or modify |
393 | +# it under the terms of the GNU General Public License as published by |
394 | +# the Free Software Foundation; version 3 of the License. |
395 | +# |
396 | +# This program is distributed in the hope that it will be useful, |
397 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
398 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
399 | +# GNU General Public License for more details. |
400 | +# |
401 | +# You should have received a copy of the GNU General Public License |
402 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
403 | + |
404 | +import dbus |
405 | +import dbus.service |
406 | +import libertine.service.task_dispatcher |
407 | +from dbus.mainloop.glib import DBusGMainLoop |
408 | +from libertine.service import container |
409 | +from libertine import utils |
410 | + |
411 | + |
412 | +LIBERTINE_MANAGER_NAME = "com.canonical.libertine.Service" |
413 | +LIBERTINE_MANAGER_INTERFACE = LIBERTINE_MANAGER_NAME |
414 | +LIBERTINE_STORE_PATH = "/Manager" |
415 | + |
416 | + |
417 | +class Manager(dbus.service.Object): |
418 | + def __init__(self): |
419 | + utils.get_logger().debug("creating service") |
420 | + DBusGMainLoop(set_as_default=True) |
421 | + try: |
422 | + bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME, |
423 | + bus=dbus.SessionBus(), |
424 | + do_not_queue=True) |
425 | + except dbus.exceptions.NameExistsException: |
426 | + utils.get_logger().warning("service is already running") |
427 | + raise |
428 | + |
429 | + super().__init__(bus_name, LIBERTINE_STORE_PATH) |
430 | + |
431 | + self._dispatcher = libertine.service.task_dispatcher.TaskDispatcher(self.connection) |
432 | + |
433 | + # Information |
434 | + |
435 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
436 | + in_signature='ss', |
437 | + out_signature='o') |
438 | + def search(self, container_id, search_string): |
439 | + utils.get_logger().debug("search('{}', '{}') called".format(container_id, search_string)) |
440 | + return self._dispatcher.search(container_id, search_string) |
441 | + |
442 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
443 | + in_signature='ss', |
444 | + out_signature='o') |
445 | + def app_info(self, container_id, app_id): |
446 | + utils.get_logger().debug("app_info('{}', '{}') called".format(container_id, app_id)) |
447 | + return self._dispatcher.app_info(container_id, app_id) |
448 | + |
449 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
450 | + in_signature='s', |
451 | + out_signature='o') |
452 | + def container_info(self, container_id): |
453 | + utils.get_logger().debug("container_info('{}')".format(container_id)) |
454 | + return self._dispatcher.container_info(container_id) |
455 | + |
456 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
457 | + in_signature='s', |
458 | + out_signature='o') |
459 | + def list_apps(self, container_id): |
460 | + utils.get_logger().debug("list_apps('{}')".format(container_id)) |
461 | + return self._dispatcher.list_apps(container_id) |
462 | + |
463 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
464 | + out_signature='o') |
465 | + def list(self): |
466 | + utils.get_logger().debug("list()") |
467 | + return self._dispatcher.list() |
468 | + |
469 | + # Operations |
470 | + |
471 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
472 | + in_signature='ssssb', |
473 | + out_signature='o') |
474 | + def create(self, container_id, container_name='', distro='', container_type='', enable_multiarch=False): |
475 | + utils.get_logger().debug("create('{}', '{}', '{}', '{}', '{}')".format(container_id, container_name, distro, container_type, enable_multiarch)) |
476 | + return self._dispatcher.create(container_id, container_name, distro, container_type, enable_multiarch) |
477 | + |
478 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
479 | + in_signature='s', |
480 | + out_signature='o') |
481 | + def destroy(self, container_id): |
482 | + utils.get_logger().debug("destroy('{}')".format(container_id)) |
483 | + return self._dispatcher.destroy(container_id) |
484 | + |
485 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
486 | + in_signature='s', |
487 | + out_signature='o') |
488 | + def update(self, container_id): |
489 | + utils.get_logger().debug("update('{}')".format(container_id)) |
490 | + return self._dispatcher.update(container_id) |
491 | + |
492 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
493 | + in_signature='ss', |
494 | + out_signature='o') |
495 | + def install(self, container_id, package_name): |
496 | + utils.get_logger().debug("install('%s', '%s')" % (container_id, package_name)) |
497 | + return self._dispatcher.install(container_id, package_name) |
498 | + |
499 | + @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
500 | + in_signature='ss', |
501 | + out_signature='o') |
502 | + def remove(self, container_id, package_name): |
503 | + utils.get_logger().debug("remove('%s', '%s')" % (container_id, package_name)) |
504 | + return self._dispatcher.remove(container_id, package_name) |
505 | |
506 | === added file 'python/libertine/service/progress.py' |
507 | --- python/libertine/service/progress.py 1970-01-01 00:00:00 +0000 |
508 | +++ python/libertine/service/progress.py 2016-11-16 17:34:14 +0000 |
509 | @@ -0,0 +1,83 @@ |
510 | +# Copyright 2016 Canonical Ltd. |
511 | +# |
512 | +# This program is free software: you can redistribute it and/or modify |
513 | +# it under the terms of the GNU General Public License as published by |
514 | +# the Free Software Foundation; version 3 of the License. |
515 | +# |
516 | +# This program is distributed in the hope that it will be useful, |
517 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
518 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
519 | +# GNU General Public License for more details. |
520 | +# |
521 | +# You should have received a copy of the GNU General Public License |
522 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
523 | + |
524 | +import dbus.service |
525 | +import threading |
526 | +from libertine import utils |
527 | +from time import time |
528 | + |
529 | +DOWNLOAD_INTERFACE = "com.canonical.applications.Download" |
530 | +PROGRESS_INTERFACE = "com.canonical.libertine.Progress" |
531 | + |
532 | +class Progress(dbus.service.Object): |
533 | + def __init__(self, connection): |
534 | + utils.get_logger().debug("creating a Progress object") |
535 | + self._finished = False |
536 | + self._result = [] |
537 | + self._error = '' |
538 | + dbus.service.Object.__init__(self, conn=connection, object_path=("/Progress/%s" % hex(int(time()*10000000))[2:])) |
539 | + |
540 | + self.emit_processing() |
541 | + |
542 | + @property |
543 | + def id(self): |
544 | + return self._object_path |
545 | + |
546 | + def emit_processing(self): |
547 | + if not self.done: |
548 | + self.processing(self.id) |
549 | + threading.Timer(0.5, self.emit_processing).start() |
550 | + |
551 | + @property |
552 | + def done(self): |
553 | + return self._finished |
554 | + |
555 | + @dbus.service.signal(DOWNLOAD_INTERFACE) |
556 | + def processing(self, path): |
557 | + utils.get_logger().debug("emit processing('%s')" % path) |
558 | + |
559 | + @dbus.service.signal(DOWNLOAD_INTERFACE) |
560 | + def finished(self, path): |
561 | + utils.get_logger().debug("emit finished('%s')" % path) |
562 | + self._finished = True |
563 | + |
564 | + @dbus.service.signal(DOWNLOAD_INTERFACE) |
565 | + def progress(self, received, total): |
566 | + utils.get_logger().debug("emit progress(%d, %d)" % (received, total)) |
567 | + |
568 | + @dbus.service.signal(DOWNLOAD_INTERFACE) |
569 | + def error(self, message): |
570 | + utils.get_logger().error("emit error(%s)" % message) |
571 | + self._error = message |
572 | + self._finished = True |
573 | + |
574 | + @dbus.service.signal(PROGRESS_INTERFACE) |
575 | + def data(self, message): |
576 | + utils.get_logger().debug("emit data(%s)" % message) |
577 | + self._result.append(message) |
578 | + |
579 | + @dbus.service.method(PROGRESS_INTERFACE, out_signature='b') |
580 | + def running(self): |
581 | + utils.get_logger().debug("running()") |
582 | + return not self.done |
583 | + |
584 | + @dbus.service.method(PROGRESS_INTERFACE, out_signature='s') |
585 | + def result(self): |
586 | + utils.get_logger().debug("result()") |
587 | + return "\n".join(self._result) |
588 | + |
589 | + @dbus.service.method(PROGRESS_INTERFACE, out_signature='s') |
590 | + def last_error(self): |
591 | + utils.get_logger().debug("last_error()") |
592 | + return self._error |
593 | |
594 | === added file 'python/libertine/service/task_dispatcher.py' |
595 | --- python/libertine/service/task_dispatcher.py 1970-01-01 00:00:00 +0000 |
596 | +++ python/libertine/service/task_dispatcher.py 2016-11-16 17:34:14 +0000 |
597 | @@ -0,0 +1,114 @@ |
598 | +# Copyright 2016 Canonical Ltd. |
599 | +# |
600 | +# This program is free software: you can redistribute it and/or modify |
601 | +# it under the terms of the GNU General Public License as published by |
602 | +# the Free Software Foundation; version 3 of the License. |
603 | +# |
604 | +# This program is distributed in the hope that it will be useful, |
605 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
606 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
607 | +# GNU General Public License for more details. |
608 | +# |
609 | +# You should have received a copy of the GNU General Public License |
610 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
611 | + |
612 | + |
613 | +import libertine.ContainersConfig |
614 | +from libertine.service.container import Container |
615 | +from libertine.service.tasks import * |
616 | +from threading import Lock |
617 | +from libertine import utils |
618 | + |
619 | + |
620 | +class TaskDispatcher(object): |
621 | + def __init__(self, connection): |
622 | + self._connection = connection |
623 | + self._config = libertine.ContainersConfig.ContainersConfig() |
624 | + self._lock = Lock() |
625 | + self._containerless_tasks = [] |
626 | + self._tasks = [] |
627 | + self._containers = [] |
628 | + |
629 | + def _cleanup_task(self, task): |
630 | + utils.get_logger().debug("cleaning up containerless task '%s'" % task.id) |
631 | + if task in self._tasks: |
632 | + self._tasks.remove(task) |
633 | + |
634 | + def _cleanup_container(self, container): |
635 | + utils.get_logger().debug("cleaning up container '%s'" % container) |
636 | + if container in self._containers: |
637 | + self._containers.remove(container) |
638 | + |
639 | + def _find_or_create_container(self, container_id): |
640 | + utils.get_logger().debug("finding or creating container '%s'" % container_id) |
641 | + container = self._find_container(container_id) |
642 | + if container is not None: |
643 | + utils.get_logger().debug("using existing container '%s'" % container_id) |
644 | + return container |
645 | + container = Container(container_id, self._config, self._lock, self._connection, self._cleanup_container) |
646 | + self._containers.append(container) |
647 | + |
648 | + return container |
649 | + |
650 | + def _find_container(self, container_id): |
651 | + containers = [c for c in self._containers if c.id == container_id] |
652 | + if len(containers) > 0: |
653 | + return containers[0] |
654 | + |
655 | + # Tasks (usually) run within a container |
656 | + |
657 | + def search(self, container_id, query): |
658 | + utils.get_logger().debug("dispatching search in container '%s' for package '%s'" % (container_id, query)) |
659 | + return self._find_or_create_container(container_id).search(query) |
660 | + |
661 | + def app_info(self, container_id, app_id): |
662 | + utils.get_logger().debug("dispatching app_info in container '%s' for package '%s'" % (container_id, app_id)) |
663 | + return self._find_or_create_container(container_id).app_info(app_id) |
664 | + |
665 | + def install(self, container_id, package_name): |
666 | + utils.get_logger().debug("dispatching install of package '%s' from container '%s'" % (package_name, container_id)) |
667 | + return self._find_or_create_container(container_id).install(package_name) |
668 | + |
669 | + def remove(self, container_id, package_name): |
670 | + utils.get_logger().debug("dispatching remove of package '%s' from container '%s'" % (package_name, container_id)) |
671 | + return self._find_or_create_container(container_id).remove(package_name) |
672 | + |
673 | + def create(self, container_id, container_name, distro, container_type, enable_multiarch): |
674 | + utils.get_logger().debug("dispatching create of container '%s'" % container_id) |
675 | + return self._find_or_create_container(container_id).create(container_name, distro, container_type, enable_multiarch) |
676 | + |
677 | + def destroy(self, container_id): |
678 | + utils.get_logger().debug("dispatching destroy container '%s'" % container_id) |
679 | + return self._find_or_create_container(container_id).destroy() |
680 | + |
681 | + def update(self, container_id): |
682 | + utils.get_logger().debug("dispatching update container '%s'" % container_id) |
683 | + return self._find_or_create_container(container_id).update() |
684 | + |
685 | + def list_apps(self, container_id): |
686 | + utils.get_logger().debug("dispatching list all apps in container '%s'" % container_id) |
687 | + return self._find_or_create_container(container_id).list_apps() |
688 | + |
689 | + # Containerless Tasks |
690 | + |
691 | + def container_info(self, container_id): |
692 | + utils.get_logger().debug("dispatching get info for container '%s'" % container_id) |
693 | + |
694 | + related_task_ids = [] |
695 | + container = self._find_container(container_id) |
696 | + if container is not None: |
697 | + related_task_ids = container.tasks |
698 | + task = ContainerInfoTask(container_id, related_task_ids, self._config, self._connection, self._cleanup_task) |
699 | + self._tasks.append(task) |
700 | + task.start() |
701 | + |
702 | + return task.id |
703 | + |
704 | + def list(self): |
705 | + utils.get_logger().debug("dispatching list all containers") |
706 | + |
707 | + task = ListTask(self._connection, self._cleanup_task) |
708 | + self._tasks.append(task) |
709 | + task.start() |
710 | + |
711 | + return task.id |
712 | |
713 | === added directory 'python/libertine/service/tasks' |
714 | === added file 'python/libertine/service/tasks/__init__.py' |
715 | --- python/libertine/service/tasks/__init__.py 1970-01-01 00:00:00 +0000 |
716 | +++ python/libertine/service/tasks/__init__.py 2016-11-16 17:34:14 +0000 |
717 | @@ -0,0 +1,39 @@ |
718 | +# Copyright 2016 Canonical Ltd. |
719 | +# |
720 | +# This program is free software: you can redistribute it and/or modify it |
721 | +# under the terms of the GNU General Public License version 3, as published |
722 | +# by the Free Software Foundation. |
723 | +# |
724 | +# This program is distributed in the hope that it will be useful, but |
725 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
726 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
727 | +# PURPOSE. See the GNU General Public License for more details. |
728 | +# |
729 | +# You should have received a copy of the GNU General Public License along |
730 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
731 | + |
732 | +from .base_task import BaseTask |
733 | +from .app_info_task import AppInfoTask |
734 | +from .container_info_task import ContainerInfoTask |
735 | +from .create_task import CreateTask |
736 | +from .destroy_task import DestroyTask |
737 | +from .install_task import InstallTask |
738 | +from .remove_task import RemoveTask |
739 | +from .search_task import SearchTask |
740 | +from .update_task import UpdateTask |
741 | +from .list_task import ListTask |
742 | +from .list_apps_task import ListAppsTask |
743 | + |
744 | +__all__ = [ |
745 | + 'AppInfoTask', |
746 | + 'BaseTask', |
747 | + 'ContainerInfoTask', |
748 | + 'CreateTask', |
749 | + 'DestroyTask', |
750 | + 'InstallTask', |
751 | + 'RemoveTask', |
752 | + 'SearchTask', |
753 | + 'UpdateTask', |
754 | + 'ListTask', |
755 | + 'ListAppsTask' |
756 | + ] |
757 | |
758 | === added file 'python/libertine/service/tasks/app_info_task.py' |
759 | --- python/libertine/service/tasks/app_info_task.py 1970-01-01 00:00:00 +0000 |
760 | +++ python/libertine/service/tasks/app_info_task.py 2016-11-16 17:34:14 +0000 |
761 | @@ -0,0 +1,35 @@ |
762 | +# Copyright 2016 Canonical Ltd. |
763 | +# |
764 | +# This program is free software: you can redistribute it and/or modify |
765 | +# it under the terms of the GNU General Public License as published by |
766 | +# the Free Software Foundation; version 3 of the License. |
767 | +# |
768 | +# This program is distributed in the hope that it will be useful, |
769 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
770 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
771 | +# GNU General Public License for more details. |
772 | +# |
773 | +# You should have received a copy of the GNU General Public License |
774 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
775 | + |
776 | + |
777 | +from .base_task import BaseTask |
778 | +from libertine import utils |
779 | + |
780 | + |
781 | +class AppInfoTask(BaseTask): |
782 | + def __init__(self, container_id, cache, app_id, tasks, config, connection, callback): |
783 | + super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback) |
784 | + self._cache = cache |
785 | + self._app_id = app_id |
786 | + self._tasks = tasks |
787 | + |
788 | + def _run(self): |
789 | + app = self._cache.app_info(self._app_id) |
790 | + if app == {}: |
791 | + self._progress.error("Could not find app info for '%s' in container '%s'" % (self._app_id, self._container)) |
792 | + return |
793 | + |
794 | + app['status'] = self._config.get_package_install_status(self._container, app['package']) or '' |
795 | + app['task_ids'] = self._tasks |
796 | + self._progress.data(str(app)) |
797 | |
798 | === added file 'python/libertine/service/tasks/base_task.py' |
799 | --- python/libertine/service/tasks/base_task.py 1970-01-01 00:00:00 +0000 |
800 | +++ python/libertine/service/tasks/base_task.py 2016-11-16 17:34:14 +0000 |
801 | @@ -0,0 +1,94 @@ |
802 | +# Copyright 2016 Canonical Ltd. |
803 | +# |
804 | +# This program is free software: you can redistribute it and/or modify |
805 | +# it under the terms of the GNU General Public License as published by |
806 | +# the Free Software Foundation; version 3 of the License. |
807 | +# |
808 | +# This program is distributed in the hope that it will be useful, |
809 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
810 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
811 | +# GNU General Public License for more details. |
812 | +# |
813 | +# You should have received a copy of the GNU General Public License |
814 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
815 | + |
816 | +import libertine.service.progress |
817 | +import threading |
818 | +from abc import ABCMeta, abstractmethod |
819 | +from libertine import utils |
820 | + |
821 | + |
822 | +class BaseTask(metaclass=ABCMeta): |
823 | + """ |
824 | + Abstract class for performing long-running, synchronous operations on a |
825 | + Libertine container. Child classes must implement _run, which will execute |
826 | + in a separate thread. Override _before to implement pre-execution actions |
827 | + without locking; if _before returns False, _run will not be executed. |
828 | + """ |
829 | + def __init__(self, lock, container_id, config, connection, callback): |
830 | + self._lock = lock |
831 | + self._container = container_id |
832 | + self._config = config |
833 | + self._callback = callback |
834 | + self._connection = connection |
835 | + self._progress = None |
836 | + self._instant_callback = False |
837 | + |
838 | + def matches(self, container, klass): |
839 | + return self._container == container and self.__class__ == klass |
840 | + |
841 | + @property |
842 | + def id(self): |
843 | + if self._progress is not None: |
844 | + return self._progress.id |
845 | + else: |
846 | + return None |
847 | + |
848 | + @property |
849 | + def container(self): |
850 | + return self._container or '' |
851 | + |
852 | + @property |
853 | + def package(self): |
854 | + return '' |
855 | + |
856 | + @property |
857 | + def running(self): |
858 | + return not self._progress.done |
859 | + |
860 | + def _delayed_callback(self): |
861 | + if self._instant_callback: |
862 | + self._callback(self) |
863 | + else: |
864 | + threading.Timer(30, lambda: self._callback(self)).start() |
865 | + |
866 | + def start(self): |
867 | + self._progress = libertine.service.progress.Progress(self._connection) |
868 | + thread = threading.Thread(target=self.run) |
869 | + thread.start() |
870 | + return thread |
871 | + |
872 | + def run(self): |
873 | + if not self._before(): |
874 | + self._progress.finished(self.container) |
875 | + self._delayed_callback() |
876 | + return |
877 | + |
878 | + if self._lock is not None: |
879 | + with self._lock: |
880 | + self._run() |
881 | + else: |
882 | + self._run() |
883 | + |
884 | + if self.running: |
885 | + self._progress.finished(self.container) |
886 | + utils.refresh_libertine_scope() |
887 | + |
888 | + self._delayed_callback() |
889 | + |
890 | + @abstractmethod |
891 | + def _run(self): |
892 | + pass |
893 | + |
894 | + def _before(self): |
895 | + return True |
896 | |
897 | === added file 'python/libertine/service/tasks/container_info_task.py' |
898 | --- python/libertine/service/tasks/container_info_task.py 1970-01-01 00:00:00 +0000 |
899 | +++ python/libertine/service/tasks/container_info_task.py 2016-11-16 17:34:14 +0000 |
900 | @@ -0,0 +1,29 @@ |
901 | +# Copyright 2016 Canonical Ltd. |
902 | +# |
903 | +# This program is free software: you can redistribute it and/or modify |
904 | +# it under the terms of the GNU General Public License as published by |
905 | +# the Free Software Foundation; version 3 of the License. |
906 | +# |
907 | +# This program is distributed in the hope that it will be useful, |
908 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
909 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
910 | +# GNU General Public License for more details. |
911 | +# |
912 | +# You should have received a copy of the GNU General Public License |
913 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
914 | + |
915 | + |
916 | +from .base_task import BaseTask |
917 | +from libertine import utils |
918 | + |
919 | + |
920 | +class ContainerInfoTask(BaseTask): |
921 | + def __init__(self, container_id, tasks, config, connection, callback): |
922 | + super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback) |
923 | + self._tasks = tasks |
924 | + |
925 | + def _run(self): |
926 | + container = {'id': str(self._container)} |
927 | + container['status'] = self._config._get_value_by_key(self._container, 'installStatus') or '' |
928 | + container['task_ids'] = self._tasks |
929 | + self._progress.data(str(container)) |
930 | |
931 | === added file 'python/libertine/service/tasks/create_task.py' |
932 | --- python/libertine/service/tasks/create_task.py 1970-01-01 00:00:00 +0000 |
933 | +++ python/libertine/service/tasks/create_task.py 2016-11-16 17:34:14 +0000 |
934 | @@ -0,0 +1,72 @@ |
935 | +# Copyright 2016 Canonical Ltd. |
936 | +# |
937 | +# This program is free software: you can redistribute it and/or modify |
938 | +# it under the terms of the GNU General Public License as published by |
939 | +# the Free Software Foundation; version 3 of the License. |
940 | +# |
941 | +# This program is distributed in the hope that it will be useful, |
942 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
943 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
944 | +# GNU General Public License for more details. |
945 | +# |
946 | +# You should have received a copy of the GNU General Public License |
947 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
948 | + |
949 | + |
950 | +from .base_task import BaseTask |
951 | +from libertine import LibertineContainer, utils |
952 | +from libertine.HostInfo import HostInfo |
953 | + |
954 | + |
955 | +class CreateTask(BaseTask): |
956 | + def __init__(self, container_id, container_name, distro, container_type, enable_multiarch, config, lock, connection, callback): |
957 | + super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback) |
958 | + self._name = container_name |
959 | + self._distro = distro |
960 | + self._type = container_type |
961 | + self._multiarch = enable_multiarch |
962 | + |
963 | + def _run(self): |
964 | + utils.get_logger().debug("Creating container '%s'" % self._container) |
965 | + |
966 | + try: |
967 | + container = LibertineContainer(self._container, self._config) |
968 | + |
969 | + if not container.create_libertine_container(password='', multiarch=self._multiarch): |
970 | + self._config.delete_container(self._container) |
971 | + self._progress.error("Creating container '%s' failed" % self._container) |
972 | + else: |
973 | + self._config.update_container_install_status(self._container, "ready") |
974 | + except RuntimeError as e: |
975 | + self._progress.error(str(e)) |
976 | + self._config.delete_container(self._container) |
977 | + |
978 | + def _before(self): |
979 | + utils.get_logger().debug("CreateTask::_before") |
980 | + if self._config.container_exists(self._container): |
981 | + self._progress.error("Container '%s' already exists" % self._container) |
982 | + return False |
983 | + |
984 | + info = HostInfo() |
985 | + if not self._distro: |
986 | + self._distro = info.get_host_distro_release() |
987 | + elif not info.is_distro_valid(self._distro): |
988 | + self._progress.error("Invalid distro '%s'." % self._distro) |
989 | + return False |
990 | + |
991 | + if not self._type: |
992 | + self._type = info.select_container_type_by_kernel() |
993 | + elif self._type == 'lxc' and not info.has_lxc_support(): |
994 | + self._progress.error("System kernel does not support lxc type containers. Please either use chroot or leave empty.") |
995 | + return False |
996 | + |
997 | + if not self._name: |
998 | + self._name = "Ubuntu \'" + info.get_distro_codename(self._distro) + "\'" |
999 | + |
1000 | + self._config.add_new_container(self._container, self._name, self._type, self._distro) |
1001 | + |
1002 | + if self._multiarch: |
1003 | + self._config.update_container_multiarch_support(self._container, 'enabled') |
1004 | + |
1005 | + self._config.update_container_install_status(self._container, 'installing') |
1006 | + return True |
1007 | |
1008 | === added file 'python/libertine/service/tasks/destroy_task.py' |
1009 | --- python/libertine/service/tasks/destroy_task.py 1970-01-01 00:00:00 +0000 |
1010 | +++ python/libertine/service/tasks/destroy_task.py 2016-11-16 17:34:14 +0000 |
1011 | @@ -0,0 +1,42 @@ |
1012 | +# Copyright 2016 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 | + |
1027 | +from .base_task import BaseTask |
1028 | +from libertine import LibertineContainer, utils |
1029 | + |
1030 | + |
1031 | +class DestroyTask(BaseTask): |
1032 | + def __init__(self, container_id, config, lock, connection, callback): |
1033 | + super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback) |
1034 | + |
1035 | + def _run(self): |
1036 | + utils.get_logger().debug("Destroying container '%s'" % self._container) |
1037 | + |
1038 | + container = LibertineContainer(self._container, self._config) |
1039 | + if not container.destroy_libertine_container(): |
1040 | + self._progress.error("Destroying container '%s' failed" % self._container) |
1041 | + self._config.update_container_install_status(self._container, "ready") |
1042 | + return |
1043 | + |
1044 | + self._config.delete_container(self._container) |
1045 | + |
1046 | + def _before(self): |
1047 | + utils.get_logger().debug("CreateTask::_before") |
1048 | + if self._config._get_value_by_key(self._container, 'installStatus') != 'ready': |
1049 | + self._progress.error("Container '%s' does not exist" % self._container) |
1050 | + return False |
1051 | + |
1052 | + self._config.update_container_install_status(self._container, 'removing') |
1053 | + return True |
1054 | |
1055 | === added file 'python/libertine/service/tasks/install_task.py' |
1056 | --- python/libertine/service/tasks/install_task.py 1970-01-01 00:00:00 +0000 |
1057 | +++ python/libertine/service/tasks/install_task.py 2016-11-16 17:34:14 +0000 |
1058 | @@ -0,0 +1,49 @@ |
1059 | +# Copyright 2016 Canonical Ltd. |
1060 | +# |
1061 | +# This program is free software: you can redistribute it and/or modify |
1062 | +# it under the terms of the GNU General Public License as published by |
1063 | +# the Free Software Foundation; version 3 of the License. |
1064 | +# |
1065 | +# This program is distributed in the hope that it will be useful, |
1066 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1067 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1068 | +# GNU General Public License for more details. |
1069 | +# |
1070 | +# You should have received a copy of the GNU General Public License |
1071 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1072 | + |
1073 | + |
1074 | +from .base_task import BaseTask |
1075 | +from libertine import LibertineContainer, utils |
1076 | + |
1077 | + |
1078 | +class InstallTask(BaseTask): |
1079 | + def __init__(self, package_name, container_id, config, lock, connection, callback): |
1080 | + super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback) |
1081 | + self._package = package_name |
1082 | + |
1083 | + def matches(self, package, klass): |
1084 | + return self._package == package and self.__class__ == klass |
1085 | + |
1086 | + @property |
1087 | + def package(self): |
1088 | + return self._package |
1089 | + |
1090 | + def _run(self): |
1091 | + utils.get_logger().debug("Installing package '%s'" % self._package) |
1092 | + container = LibertineContainer(self._container, self._config) |
1093 | + if container.install_package(self._package): |
1094 | + self._config.update_package_install_status(self._container, self._package, "installed") |
1095 | + else: |
1096 | + self._config.delete_package(self._container, self._package) |
1097 | + self._progress.error("Package installation failed for '%s'" % self._package) |
1098 | + |
1099 | + def _before(self): |
1100 | + utils.get_logger().debug("InstallTask::_before") |
1101 | + if self._config.package_exists(self._container, self._package): |
1102 | + self._progress.error("Package '%s' already exists, skipping install" % self._package) |
1103 | + return False |
1104 | + else: |
1105 | + self._config.add_new_package(self._container, self._package) |
1106 | + self._config.update_package_install_status(self._container, self._package, "installing") |
1107 | + return True |
1108 | |
1109 | === added file 'python/libertine/service/tasks/list_apps_task.py' |
1110 | --- python/libertine/service/tasks/list_apps_task.py 1970-01-01 00:00:00 +0000 |
1111 | +++ python/libertine/service/tasks/list_apps_task.py 2016-11-16 17:34:14 +0000 |
1112 | @@ -0,0 +1,30 @@ |
1113 | +# Copyright 2016 Canonical Ltd. |
1114 | +# |
1115 | +# This program is free software: you can redistribute it and/or modify |
1116 | +# it under the terms of the GNU General Public License as published by |
1117 | +# the Free Software Foundation; version 3 of the License. |
1118 | +# |
1119 | +# This program is distributed in the hope that it will be useful, |
1120 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1121 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1122 | +# GNU General Public License for more details. |
1123 | +# |
1124 | +# You should have received a copy of the GNU General Public License |
1125 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1126 | + |
1127 | + |
1128 | +from .base_task import BaseTask |
1129 | +from libertine import LibertineContainer, utils |
1130 | + |
1131 | + |
1132 | +class ListAppsTask(BaseTask): |
1133 | + def __init__(self, container_id, config, connection, callback): |
1134 | + super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback) |
1135 | + |
1136 | + def _run(self): |
1137 | + utils.get_logger().debug("Listing apps in container '%s'" % self._container) |
1138 | + if not self._config.container_exists(self._container): |
1139 | + self._progress.error("Container '%s' does not exist, skipping list" % self._container) |
1140 | + else: |
1141 | + container = LibertineContainer(self._container, self._config) |
1142 | + self._progress.data(str(container.list_app_launchers(use_json=True))) |
1143 | |
1144 | === added file 'python/libertine/service/tasks/list_task.py' |
1145 | --- python/libertine/service/tasks/list_task.py 1970-01-01 00:00:00 +0000 |
1146 | +++ python/libertine/service/tasks/list_task.py 2016-11-16 17:34:14 +0000 |
1147 | @@ -0,0 +1,25 @@ |
1148 | +# Copyright 2016 Canonical Ltd. |
1149 | +# |
1150 | +# This program is free software: you can redistribute it and/or modify |
1151 | +# it under the terms of the GNU General Public License as published by |
1152 | +# the Free Software Foundation; version 3 of the License. |
1153 | +# |
1154 | +# This program is distributed in the hope that it will be useful, |
1155 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1156 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1157 | +# GNU General Public License for more details. |
1158 | +# |
1159 | +# You should have received a copy of the GNU General Public License |
1160 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1161 | + |
1162 | + |
1163 | +from .base_task import BaseTask |
1164 | +from libertine import utils |
1165 | + |
1166 | + |
1167 | +class ListTask(BaseTask): |
1168 | + def __init__(self, connection, callback): |
1169 | + super().__init__(lock=None, container_id=None, config=None, connection=connection, callback=callback) |
1170 | + |
1171 | + def _run(self): |
1172 | + self._progress.data(str(utils.Libertine.list_containers())) |
1173 | |
1174 | === added file 'python/libertine/service/tasks/remove_task.py' |
1175 | --- python/libertine/service/tasks/remove_task.py 1970-01-01 00:00:00 +0000 |
1176 | +++ python/libertine/service/tasks/remove_task.py 2016-11-16 17:34:14 +0000 |
1177 | @@ -0,0 +1,48 @@ |
1178 | +# Copyright 2016 Canonical Ltd. |
1179 | +# |
1180 | +# This program is free software: you can redistribute it and/or modify |
1181 | +# it under the terms of the GNU General Public License as published by |
1182 | +# the Free Software Foundation; version 3 of the License. |
1183 | +# |
1184 | +# This program is distributed in the hope that it will be useful, |
1185 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1186 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1187 | +# GNU General Public License for more details. |
1188 | +# |
1189 | +# You should have received a copy of the GNU General Public License |
1190 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1191 | + |
1192 | + |
1193 | +from .base_task import BaseTask |
1194 | +from libertine import LibertineContainer, utils |
1195 | + |
1196 | + |
1197 | +class RemoveTask(BaseTask): |
1198 | + def __init__(self, package_name, container_id, config, lock, connection, callback): |
1199 | + super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback) |
1200 | + self._package = package_name |
1201 | + |
1202 | + def matches(self, package, klass): |
1203 | + return self._package == package and self.__class__ == klass |
1204 | + |
1205 | + @property |
1206 | + def package(self): |
1207 | + return self._package |
1208 | + |
1209 | + def _run(self): |
1210 | + utils.get_logger().debug("Removing package '%s'" % self._package) |
1211 | + container = LibertineContainer(self._container, self._config) |
1212 | + if container.remove_package(self._package): |
1213 | + self._config.delete_package(self._container, self._package) |
1214 | + else: |
1215 | + self._config.update_package_install_status(self._container, self._package, 'installed') |
1216 | + self._progress.error("Package removal failed for '%s'" % self._package) |
1217 | + |
1218 | + def _before(self): |
1219 | + utils.get_logger().debug("RemoveTask::_before") |
1220 | + if self._config.get_package_install_status(self._container, self._package) == 'installed': |
1221 | + self._config.update_package_install_status(self._container, self._package, "removing") |
1222 | + return True |
1223 | + else: |
1224 | + self._progress.error("Package '%s' not installed, skipping remove" % self._package) |
1225 | + return False |
1226 | |
1227 | === added file 'python/libertine/service/tasks/search_task.py' |
1228 | --- python/libertine/service/tasks/search_task.py 1970-01-01 00:00:00 +0000 |
1229 | +++ python/libertine/service/tasks/search_task.py 2016-11-16 17:34:14 +0000 |
1230 | @@ -0,0 +1,27 @@ |
1231 | +# Copyright 2016 Canonical Ltd. |
1232 | +# |
1233 | +# This program is free software: you can redistribute it and/or modify |
1234 | +# it under the terms of the GNU General Public License as published by |
1235 | +# the Free Software Foundation; version 3 of the License. |
1236 | +# |
1237 | +# This program is distributed in the hope that it will be useful, |
1238 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1239 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1240 | +# GNU General Public License for more details. |
1241 | +# |
1242 | +# You should have received a copy of the GNU General Public License |
1243 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1244 | + |
1245 | + |
1246 | +from .base_task import BaseTask |
1247 | +from libertine import utils |
1248 | + |
1249 | + |
1250 | +class SearchTask(BaseTask): |
1251 | + def __init__(self, container_id, cache, query, connection, callback): |
1252 | + super().__init__(lock=None, container_id=container_id, config=None, connection=connection, callback=callback) |
1253 | + self._cache = cache |
1254 | + self._query = query |
1255 | + |
1256 | + def _run(self): |
1257 | + self._progress.data(str(self._cache.search(self._query))) |
1258 | |
1259 | === added file 'python/libertine/service/tasks/update_task.py' |
1260 | --- python/libertine/service/tasks/update_task.py 1970-01-01 00:00:00 +0000 |
1261 | +++ python/libertine/service/tasks/update_task.py 2016-11-16 17:34:14 +0000 |
1262 | @@ -0,0 +1,39 @@ |
1263 | +# Copyright 2016 Canonical Ltd. |
1264 | +# |
1265 | +# This program is free software: you can redistribute it and/or modify |
1266 | +# it under the terms of the GNU General Public License as published by |
1267 | +# the Free Software Foundation; version 3 of the License. |
1268 | +# |
1269 | +# This program is distributed in the hope that it will be useful, |
1270 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1271 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1272 | +# GNU General Public License for more details. |
1273 | +# |
1274 | +# You should have received a copy of the GNU General Public License |
1275 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1276 | + |
1277 | + |
1278 | +from .base_task import BaseTask |
1279 | +from libertine import LibertineContainer, utils |
1280 | + |
1281 | + |
1282 | +class UpdateTask(BaseTask): |
1283 | + def __init__(self, container_id, config, lock, connection, callback): |
1284 | + super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback) |
1285 | + |
1286 | + def _run(self): |
1287 | + utils.get_logger().debug("Updating container '%s'" % self._container) |
1288 | + container = LibertineContainer(self._container, self._config) |
1289 | + self._config.update_container_install_status(self._container, "updating") |
1290 | + if not container.update_libertine_container(): |
1291 | + self._progress.error("Failed to update container '%s'" % self._container) |
1292 | + |
1293 | + self._config.update_container_install_status(self._container, "ready") |
1294 | + |
1295 | + def _before(self): |
1296 | + utils.get_logger().debug("UpdateTask::_before") |
1297 | + if not self._config.container_exists(self._container): |
1298 | + self._progress.error("Container '%s' does not exist, skipping update" % self._container) |
1299 | + return False |
1300 | + else: |
1301 | + return True |
1302 | |
1303 | === modified file 'tests/unit/CMakeLists.txt' |
1304 | --- tests/unit/CMakeLists.txt 2016-09-27 00:55:28 +0000 |
1305 | +++ tests/unit/CMakeLists.txt 2016-11-16 17:34:14 +0000 |
1306 | @@ -30,3 +30,4 @@ |
1307 | ENVIRONMENT |
1308 | "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
1309 | |
1310 | +add_subdirectory(service) |
1311 | |
1312 | === added file 'tests/unit/pytest.ini' |
1313 | --- tests/unit/pytest.ini 1970-01-01 00:00:00 +0000 |
1314 | +++ tests/unit/pytest.ini 2016-11-16 17:34:14 +0000 |
1315 | @@ -0,0 +1,2 @@ |
1316 | +[pytest] |
1317 | +norecursedirs = service |
1318 | |
1319 | === added directory 'tests/unit/service' |
1320 | === added file 'tests/unit/service/CMakeLists.txt' |
1321 | --- tests/unit/service/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1322 | +++ tests/unit/service/CMakeLists.txt 2016-11-16 17:34:14 +0000 |
1323 | @@ -0,0 +1,12 @@ |
1324 | +function(create_service_unit_test test_name) |
1325 | + add_test(${test_name} /usr/bin/python3 -m testtools.run ${test_name}) |
1326 | + set_tests_properties(${test_name} |
1327 | + PROPERTIES ENVIRONMENT |
1328 | + "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}") |
1329 | +endfunction(create_service_unit_test) |
1330 | + |
1331 | +create_service_unit_test(test_container) |
1332 | +create_service_unit_test(test_apt) |
1333 | +create_service_unit_test(test_task_dispatcher) |
1334 | + |
1335 | +add_subdirectory(tasks) |
1336 | |
1337 | === added directory 'tests/unit/service/containerroot' |
1338 | === added directory 'tests/unit/service/containerroot/var' |
1339 | === added directory 'tests/unit/service/containerroot/var/cache' |
1340 | === added directory 'tests/unit/service/containerroot/var/cache/app-info' |
1341 | === added directory 'tests/unit/service/containerroot/var/cache/app-info/gv' |
1342 | === added file 'tests/unit/service/containerroot/var/cache/app-info/gv/en_US.gvz' |
1343 | === added directory 'tests/unit/service/tasks' |
1344 | === added file 'tests/unit/service/tasks/CMakeLists.txt' |
1345 | --- tests/unit/service/tasks/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1346 | +++ tests/unit/service/tasks/CMakeLists.txt 2016-11-16 17:34:14 +0000 |
1347 | @@ -0,0 +1,10 @@ |
1348 | +create_service_unit_test(test_app_info_task) |
1349 | +create_service_unit_test(test_container_info_task) |
1350 | +create_service_unit_test(test_create_task) |
1351 | +create_service_unit_test(test_destroy_task) |
1352 | +create_service_unit_test(test_install_task) |
1353 | +create_service_unit_test(test_list_task) |
1354 | +create_service_unit_test(test_list_apps_task) |
1355 | +create_service_unit_test(test_remove_task) |
1356 | +create_service_unit_test(test_search_task) |
1357 | +create_service_unit_test(test_update_task) |
1358 | |
1359 | === added file 'tests/unit/service/tasks/test_app_info_task.py' |
1360 | --- tests/unit/service/tasks/test_app_info_task.py 1970-01-01 00:00:00 +0000 |
1361 | +++ tests/unit/service/tasks/test_app_info_task.py 2016-11-16 17:34:14 +0000 |
1362 | @@ -0,0 +1,61 @@ |
1363 | +# Copyright 2016 Canonical Ltd. |
1364 | +# |
1365 | +# This program is free software: you can redistribute it and/or modify it |
1366 | +# under the terms of the GNU General Public License version 3, as published |
1367 | +# by the Free Software Foundation. |
1368 | +# |
1369 | +# This program is distributed in the hope that it will be useful, but |
1370 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1371 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1372 | +# PURPOSE. See the GNU General Public License for more details. |
1373 | +# |
1374 | +# You should have received a copy of the GNU General Public License along |
1375 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1376 | + |
1377 | + |
1378 | +import unittest.mock |
1379 | +from unittest import TestCase |
1380 | +from libertine.service import tasks, apt |
1381 | +from libertine.ContainersConfig import ContainersConfig |
1382 | + |
1383 | + |
1384 | +class TestAppInfoTask(TestCase): |
1385 | + def setUp(self): |
1386 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1387 | + self.cache = unittest.mock.create_autospec(apt.AptCache) |
1388 | + self.connection = unittest.mock.Mock() |
1389 | + |
1390 | + def test_app_not_found_causes_error(self): |
1391 | + self.called_with = None |
1392 | + def callback(t): |
1393 | + self.called_with = t |
1394 | + |
1395 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1396 | + progress = MockProgress.return_value |
1397 | + self.cache.app_info.return_value = {} |
1398 | + task = tasks.AppInfoTask('palpatine', self.cache, 'lightside', [1, 2], self.config, self.connection, callback) |
1399 | + task._instant_callback = True |
1400 | + task.start().join() |
1401 | + |
1402 | + progress.error.assert_called_once_with('Could not find app info for \'lightside\' in container \'palpatine\'') |
1403 | + |
1404 | + self.assertEqual(task, self.called_with) |
1405 | + |
1406 | + def test_success_sends_data(self): |
1407 | + self.called_with = None |
1408 | + def callback(t): |
1409 | + self.called_with = t |
1410 | + |
1411 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1412 | + progress = MockProgress.return_value |
1413 | + progress.done = False |
1414 | + |
1415 | + self.cache.app_info.return_value = {'package': 'darkside-common'} |
1416 | + self.config.get_package_install_status.return_value = 'installed' |
1417 | + task = tasks.AppInfoTask('palpatine', self.cache, 'darkside', [1, 2, 3], self.config, self.connection, callback) |
1418 | + task._instant_callback = True |
1419 | + task.start().join() |
1420 | + |
1421 | + progress.data.assert_called_once_with(str({'package': 'darkside-common', 'status': 'installed', 'task_ids': [1, 2, 3]})) |
1422 | + |
1423 | + self.assertEqual(task, self.called_with) |
1424 | |
1425 | === added file 'tests/unit/service/tasks/test_container_info_task.py' |
1426 | --- tests/unit/service/tasks/test_container_info_task.py 1970-01-01 00:00:00 +0000 |
1427 | +++ tests/unit/service/tasks/test_container_info_task.py 2016-11-16 17:34:14 +0000 |
1428 | @@ -0,0 +1,44 @@ |
1429 | +# Copyright 2016 Canonical Ltd. |
1430 | +# |
1431 | +# This program is free software: you can redistribute it and/or modify it |
1432 | +# under the terms of the GNU General Public License version 3, as published |
1433 | +# by the Free Software Foundation. |
1434 | +# |
1435 | +# This program is distributed in the hope that it will be useful, but |
1436 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1437 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1438 | +# PURPOSE. See the GNU General Public License for more details. |
1439 | +# |
1440 | +# You should have received a copy of the GNU General Public License along |
1441 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1442 | + |
1443 | + |
1444 | +import unittest.mock |
1445 | +from unittest import TestCase |
1446 | +from libertine.service import tasks |
1447 | +from libertine.ContainersConfig import ContainersConfig |
1448 | + |
1449 | + |
1450 | +class TestContainerInfoTask(TestCase): |
1451 | + def setUp(self): |
1452 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1453 | + self.connection = unittest.mock.Mock() |
1454 | + |
1455 | + def test_success_sends_data(self): |
1456 | + self.called_with = None |
1457 | + def callback(t): |
1458 | + self.called_with = t |
1459 | + |
1460 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1461 | + progress = MockProgress.return_value |
1462 | + progress.done = False |
1463 | + |
1464 | + self.config._get_value_by_key.return_value = 'ready' |
1465 | + task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.connection, callback) |
1466 | + task._instant_callback = True |
1467 | + task.start().join() |
1468 | + |
1469 | + progress.data.assert_called_once_with(str({'id': 'palpatine', 'status': 'ready', 'task_ids': [1, 2, 3]})) |
1470 | + progress.finished.assert_called_once_with('palpatine') |
1471 | + |
1472 | + self.assertEqual(task, self.called_with) |
1473 | |
1474 | === added file 'tests/unit/service/tasks/test_create_task.py' |
1475 | --- tests/unit/service/tasks/test_create_task.py 1970-01-01 00:00:00 +0000 |
1476 | +++ tests/unit/service/tasks/test_create_task.py 2016-11-16 17:34:14 +0000 |
1477 | @@ -0,0 +1,234 @@ |
1478 | +# Copyright 2016 Canonical Ltd. |
1479 | +# |
1480 | +# This program is free software: you can redistribute it and/or modify it |
1481 | +# under the terms of the GNU General Public License version 3, as published |
1482 | +# by the Free Software Foundation. |
1483 | +# |
1484 | +# This program is distributed in the hope that it will be useful, but |
1485 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1486 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1487 | +# PURPOSE. See the GNU General Public License for more details. |
1488 | +# |
1489 | +# You should have received a copy of the GNU General Public License along |
1490 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1491 | + |
1492 | + |
1493 | +import unittest.mock |
1494 | +from unittest import TestCase |
1495 | +from libertine.service import tasks |
1496 | +from libertine.ContainersConfig import ContainersConfig |
1497 | + |
1498 | + |
1499 | +class TestCreateTask(TestCase): |
1500 | + def setUp(self): |
1501 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1502 | + self.connection = unittest.mock.Mock() |
1503 | + self.lock = unittest.mock.MagicMock() |
1504 | + self.called_with = None |
1505 | + |
1506 | + def callback(self, task): |
1507 | + self.called_with = task |
1508 | + |
1509 | + def test_success_creates_lxc_container(self): |
1510 | + self.config.container_exists.return_value = False |
1511 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1512 | + progress = MockProgress.return_value |
1513 | + progress.done = False |
1514 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback) |
1515 | + task._instant_callback = True |
1516 | + |
1517 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1518 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1519 | + MockHostInfo.return_value.has_lxc_support.return_value = True |
1520 | + |
1521 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1522 | + MockContainer.return_value.create_libertine_container.return_value = True |
1523 | + task.start().join() |
1524 | + |
1525 | + progress.finished.assert_called_once_with('palpatine') |
1526 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty') |
1527 | + self.config.update_container_install_status.assert_has_calls([ |
1528 | + unittest.mock.call('palpatine', 'installing'), |
1529 | + unittest.mock.call('palpatine', 'ready') |
1530 | + ], any_order=True) |
1531 | + self.assertEqual(task, self.called_with) |
1532 | + |
1533 | + def test_success_creates_chroot_container(self): |
1534 | + self.config.container_exists.return_value = False |
1535 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1536 | + progress = MockProgress.return_value |
1537 | + progress.done = False |
1538 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback) |
1539 | + task._instant_callback = True |
1540 | + |
1541 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1542 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1543 | + |
1544 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1545 | + MockContainer.return_value.create_libertine_container.return_value = True |
1546 | + task.start().join() |
1547 | + |
1548 | + progress.finished.assert_called_once_with('palpatine') |
1549 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty') |
1550 | + self.config.update_container_install_status.assert_has_calls([ |
1551 | + unittest.mock.call('palpatine', 'installing'), |
1552 | + unittest.mock.call('palpatine', 'ready') |
1553 | + ], any_order=True) |
1554 | + self.assertEqual(task, self.called_with) |
1555 | + |
1556 | + def test_container_runtime_error_sends_error(self): |
1557 | + self.config.container_exists.return_value = False |
1558 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1559 | + progress = MockProgress.return_value |
1560 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback) |
1561 | + task._instant_callback = True |
1562 | + |
1563 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1564 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1565 | + MockHostInfo.return_value.has_lxc_support.return_value = True |
1566 | + |
1567 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1568 | + MockContainer.return_value.create_libertine_container.side_effect = RuntimeError('a great disturbance') |
1569 | + task.start().join() |
1570 | + |
1571 | + progress.error.assert_called_once_with('a great disturbance') |
1572 | + |
1573 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty') |
1574 | + self.config.update_container_install_status.assert_called_once_with('palpatine', 'installing') |
1575 | + self.config.delete_container.assert_called_once_with('palpatine') |
1576 | + |
1577 | + self.assertEqual(task, self.called_with) |
1578 | + |
1579 | + def test_failed_container_exists_sends_error(self): |
1580 | + self.config.container_exists.return_value = True |
1581 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1582 | + progress = MockProgress.return_value |
1583 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback) |
1584 | + task._instant_callback = True |
1585 | + task.start().join() |
1586 | + |
1587 | + progress.error.assert_called_once_with('Container \'palpatine\' already exists') |
1588 | + self.assertEqual(task, self.called_with) |
1589 | + |
1590 | + def test_container_invalid_distro_error_sends_error(self): |
1591 | + self.config.container_exists.return_value = False |
1592 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1593 | + progress = MockProgress.return_value |
1594 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1595 | + MockHostInfo.return_value.is_distro_valid.return_value = False |
1596 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'vesty', 'lxc', False, self.config, self.lock, self.connection, self.callback) |
1597 | + task._instant_callback = True |
1598 | + task.start().join() |
1599 | + |
1600 | + progress.error.assert_called_once_with('Invalid distro \'vesty\'.') |
1601 | + self.assertEqual(task, self.called_with) |
1602 | + |
1603 | + def test_container_improper_lxc_error_sends_error(self): |
1604 | + self.config.container_exists.return_value = False |
1605 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1606 | + progress = MockProgress.return_value |
1607 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1608 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1609 | + MockHostInfo.return_value.has_lxc_support.return_value = False |
1610 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback) |
1611 | + task._instant_callback = True |
1612 | + task.start().join() |
1613 | + |
1614 | + progress.error.assert_called_once_with('System kernel does not support lxc type containers. Please either use chroot or leave empty.') |
1615 | + self.assertEqual(task, self.called_with) |
1616 | + |
1617 | + def test_sets_generic_name_when_empty(self): |
1618 | + self.config.container_exists.return_value = False |
1619 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1620 | + progress = MockProgress.return_value |
1621 | + progress.done = False |
1622 | + task = tasks.CreateTask('palpatine', None, 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback) |
1623 | + task._instant_callback = True |
1624 | + |
1625 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1626 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1627 | + MockHostInfo.return_value.get_distro_codename.return_value = 'Zesty Zapus 17.04' |
1628 | + |
1629 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1630 | + MockContainer.return_value.create_libertine_container.return_value = True |
1631 | + task.start().join() |
1632 | + |
1633 | + progress.finished.assert_called_once_with('palpatine') |
1634 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Ubuntu \'Zesty Zapus 17.04\'', 'chroot', 'zesty') |
1635 | + self.config.update_container_install_status.assert_has_calls([ |
1636 | + unittest.mock.call('palpatine', 'installing'), |
1637 | + unittest.mock.call('palpatine', 'ready') |
1638 | + ], any_order=True) |
1639 | + self.assertEqual(task, self.called_with) |
1640 | + |
1641 | + def test_sets_multiarch(self): |
1642 | + self.config.container_exists.return_value = False |
1643 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1644 | + progress = MockProgress.return_value |
1645 | + progress.done = False |
1646 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', True, self.config, self.lock, self.connection, self.callback) |
1647 | + task._instant_callback = True |
1648 | + |
1649 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1650 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1651 | + |
1652 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1653 | + MockContainer.return_value.create_libertine_container.return_value = True |
1654 | + task.start().join() |
1655 | + |
1656 | + progress.finished.assert_called_once_with('palpatine') |
1657 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty') |
1658 | + self.config.update_container_multiarch_support.assert_called_once_with('palpatine', 'enabled') |
1659 | + self.config.update_container_install_status.assert_has_calls([ |
1660 | + unittest.mock.call('palpatine', 'installing'), |
1661 | + unittest.mock.call('palpatine', 'ready') |
1662 | + ], any_order=True) |
1663 | + self.assertEqual(task, self.called_with) |
1664 | + |
1665 | + def test_sets_default_type(self): |
1666 | + self.config.container_exists.return_value = False |
1667 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1668 | + progress = MockProgress.return_value |
1669 | + progress.done = False |
1670 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', None, False, self.config, self.lock, self.connection, self.callback) |
1671 | + task._instant_callback = True |
1672 | + |
1673 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1674 | + MockHostInfo.return_value.is_distro_valid.return_value = True |
1675 | + MockHostInfo.return_value.select_container_type_by_kernel.return_value = 'lxc' |
1676 | + |
1677 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1678 | + MockContainer.return_value.create_libertine_container.return_value = True |
1679 | + task.start().join() |
1680 | + |
1681 | + progress.finished.assert_called_once_with('palpatine') |
1682 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty') |
1683 | + self.config.update_container_install_status.assert_has_calls([ |
1684 | + unittest.mock.call('palpatine', 'installing'), |
1685 | + unittest.mock.call('palpatine', 'ready') |
1686 | + ], any_order=True) |
1687 | + self.assertEqual(task, self.called_with) |
1688 | + |
1689 | + def test_sets_default_distro(self): |
1690 | + self.config.container_exists.return_value = False |
1691 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1692 | + progress = MockProgress.return_value |
1693 | + progress.done = False |
1694 | + task = tasks.CreateTask('palpatine', 'Emperor Palpatine', None, 'lxc', False, self.config, self.lock, self.connection, self.callback) |
1695 | + task._instant_callback = True |
1696 | + |
1697 | + with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo: |
1698 | + MockHostInfo.return_value.has_lxc_support.return_value = True |
1699 | + MockHostInfo.return_value.get_host_distro_release.return_value = 'zesty' |
1700 | + |
1701 | + with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer: |
1702 | + MockContainer.return_value.create_libertine_container.return_value = True |
1703 | + task.start().join() |
1704 | + |
1705 | + progress.finished.assert_called_once_with('palpatine') |
1706 | + self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty') |
1707 | + self.config.update_container_install_status.assert_has_calls([ |
1708 | + unittest.mock.call('palpatine', 'installing'), |
1709 | + unittest.mock.call('palpatine', 'ready') |
1710 | + ], any_order=True) |
1711 | + self.assertEqual(task, self.called_with) |
1712 | |
1713 | === added file 'tests/unit/service/tasks/test_destroy_task.py' |
1714 | --- tests/unit/service/tasks/test_destroy_task.py 1970-01-01 00:00:00 +0000 |
1715 | +++ tests/unit/service/tasks/test_destroy_task.py 2016-11-16 17:34:14 +0000 |
1716 | @@ -0,0 +1,80 @@ |
1717 | +# Copyright 2016 Canonical Ltd. |
1718 | +# |
1719 | +# This program is free software: you can redistribute it and/or modify it |
1720 | +# under the terms of the GNU General Public License version 3, as published |
1721 | +# by the Free Software Foundation. |
1722 | +# |
1723 | +# This program is distributed in the hope that it will be useful, but |
1724 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1725 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1726 | +# PURPOSE. See the GNU General Public License for more details. |
1727 | +# |
1728 | +# You should have received a copy of the GNU General Public License along |
1729 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1730 | + |
1731 | + |
1732 | +import unittest.mock |
1733 | +from unittest import TestCase |
1734 | +from libertine.service import tasks |
1735 | +from libertine.ContainersConfig import ContainersConfig |
1736 | + |
1737 | + |
1738 | +class TestDestroyTask(TestCase): |
1739 | + def setUp(self): |
1740 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1741 | + self.connection = unittest.mock.Mock() |
1742 | + self.lock = unittest.mock.MagicMock() |
1743 | + self.called_with = None |
1744 | + |
1745 | + def callback(self, task): |
1746 | + self.called_with = task |
1747 | + |
1748 | + def test_sends_error_on_non_ready_container(self): |
1749 | + self.config._get_value_by_key.return_value = '' |
1750 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1751 | + progress = MockProgress.return_value |
1752 | + progress.done = False |
1753 | + task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback) |
1754 | + task._instant_callback = True |
1755 | + |
1756 | + with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer: |
1757 | + MockContainer.return_value.destroy_libertine_container.return_value = True |
1758 | + task.start().join() |
1759 | + |
1760 | + progress.error.assert_called_once_with('Container \'palpatine\' does not exist') |
1761 | + self.assertEqual(task, self.called_with) |
1762 | + |
1763 | + def test_sends_error_on_failed_destroy(self): |
1764 | + self.config._get_value_by_key.return_value = 'ready' |
1765 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1766 | + progress = MockProgress.return_value |
1767 | + task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback) |
1768 | + task._instant_callback = True |
1769 | + |
1770 | + with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer: |
1771 | + MockContainer.return_value.destroy_libertine_container.return_value = False |
1772 | + task.start().join() |
1773 | + |
1774 | + progress.error.assert_called_once_with('Destroying container \'palpatine\' failed') |
1775 | + self.config.update_container_install_status.assert_has_calls([ |
1776 | + unittest.mock.call('palpatine', 'removing'), |
1777 | + unittest.mock.call('palpatine', 'ready') |
1778 | + ], any_order=True) |
1779 | + self.assertEqual(task, self.called_with) |
1780 | + |
1781 | + def test_successfully_destroys(self): |
1782 | + self.config._get_value_by_key.return_value = 'ready' |
1783 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1784 | + progress = MockProgress.return_value |
1785 | + progress.done = False |
1786 | + task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback) |
1787 | + task._instant_callback = True |
1788 | + |
1789 | + with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer: |
1790 | + MockContainer.return_value.destroy_libertine_container.return_value = True |
1791 | + task.start().join() |
1792 | + |
1793 | + progress.finished.assert_called_once_with('palpatine') |
1794 | + self.config.update_container_install_status.assert_called_once_with('palpatine', 'removing') |
1795 | + self.config.delete_container.assert_called_once_with('palpatine') |
1796 | + self.assertEqual(task, self.called_with) |
1797 | |
1798 | === added file 'tests/unit/service/tasks/test_install_task.py' |
1799 | --- tests/unit/service/tasks/test_install_task.py 1970-01-01 00:00:00 +0000 |
1800 | +++ tests/unit/service/tasks/test_install_task.py 2016-11-16 17:34:14 +0000 |
1801 | @@ -0,0 +1,76 @@ |
1802 | +# Copyright 2016 Canonical Ltd. |
1803 | +# |
1804 | +# This program is free software: you can redistribute it and/or modify it |
1805 | +# under the terms of the GNU General Public License version 3, as published |
1806 | +# by the Free Software Foundation. |
1807 | +# |
1808 | +# This program is distributed in the hope that it will be useful, but |
1809 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1810 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1811 | +# PURPOSE. See the GNU General Public License for more details. |
1812 | +# |
1813 | +# You should have received a copy of the GNU General Public License along |
1814 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1815 | + |
1816 | + |
1817 | +import unittest.mock |
1818 | +from unittest import TestCase |
1819 | +from libertine.service import tasks |
1820 | +from libertine.ContainersConfig import ContainersConfig |
1821 | + |
1822 | + |
1823 | +class TestInstallTask(TestCase): |
1824 | + def setUp(self): |
1825 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1826 | + self.connection = unittest.mock.Mock() |
1827 | + self.lock = unittest.mock.MagicMock() |
1828 | + self.called_with = None |
1829 | + |
1830 | + def callback(self, task): |
1831 | + self.called_with = task |
1832 | + |
1833 | + def test_sends_error_on_existing_package(self): |
1834 | + self.config.package_exists.return_value = True |
1835 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1836 | + progress = MockProgress.return_value |
1837 | + task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback) |
1838 | + task._instant_callback = True |
1839 | + task.start().join() |
1840 | + |
1841 | + progress.error.assert_called_once_with('Package \'darkside-common\' already exists, skipping install') |
1842 | + self.assertEqual(task, self.called_with) |
1843 | + |
1844 | + def test_sends_error_on_failed_install(self): |
1845 | + self.config.package_exists.return_value = False |
1846 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1847 | + progress = MockProgress.return_value |
1848 | + task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback) |
1849 | + task._instant_callback = True |
1850 | + |
1851 | + with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer: |
1852 | + MockContainer.return_value.install_package.return_value = False |
1853 | + task.start().join() |
1854 | + |
1855 | + progress.error.assert_called_once_with("Package installation failed for 'darkside-common'") |
1856 | + self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'installing') |
1857 | + self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common') |
1858 | + self.assertEqual(task, self.called_with) |
1859 | + |
1860 | + def test_successfully_install(self): |
1861 | + self.config.package_exists.return_value = False |
1862 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1863 | + progress = MockProgress.return_value |
1864 | + progress.done = False |
1865 | + task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback) |
1866 | + task._instant_callback = True |
1867 | + |
1868 | + with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer: |
1869 | + MockContainer.return_value.install_package.return_value = True |
1870 | + task.start().join() |
1871 | + |
1872 | + progress.finished.assert_called_once_with('palpatine') |
1873 | + self.config.update_package_install_status.assert_has_calls([ |
1874 | + unittest.mock.call('palpatine', 'darkside-common', 'installing'), |
1875 | + unittest.mock.call('palpatine', 'darkside-common', 'installed') |
1876 | + ], any_order=True) |
1877 | + self.assertEqual(task, self.called_with) |
1878 | |
1879 | === added file 'tests/unit/service/tasks/test_list_apps_task.py' |
1880 | --- tests/unit/service/tasks/test_list_apps_task.py 1970-01-01 00:00:00 +0000 |
1881 | +++ tests/unit/service/tasks/test_list_apps_task.py 2016-11-16 17:34:14 +0000 |
1882 | @@ -0,0 +1,59 @@ |
1883 | +# Copyright 2016 Canonical Ltd. |
1884 | +# |
1885 | +# This program is free software: you can redistribute it and/or modify it |
1886 | +# under the terms of the GNU General Public License version 3, as published |
1887 | +# by the Free Software Foundation. |
1888 | +# |
1889 | +# This program is distributed in the hope that it will be useful, but |
1890 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1891 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1892 | +# PURPOSE. See the GNU General Public License for more details. |
1893 | +# |
1894 | +# You should have received a copy of the GNU General Public License along |
1895 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1896 | + |
1897 | + |
1898 | +import unittest.mock |
1899 | +from unittest import TestCase |
1900 | +from libertine.service import tasks |
1901 | +from libertine.ContainersConfig import ContainersConfig |
1902 | + |
1903 | + |
1904 | +class TestListAppsTask(TestCase): |
1905 | + def setUp(self): |
1906 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1907 | + self.connection = unittest.mock.Mock() |
1908 | + self.lock = unittest.mock.MagicMock() |
1909 | + self.called_with = None |
1910 | + |
1911 | + def callback(self, task): |
1912 | + self.called_with = task |
1913 | + |
1914 | + def test_sends_error_on_non_existent_container(self): |
1915 | + self.config.container_exists.return_value = False |
1916 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1917 | + progress = MockProgress.return_value |
1918 | + task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback) |
1919 | + task._instant_callback = True |
1920 | + |
1921 | + with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer: |
1922 | + task.start().join() |
1923 | + |
1924 | + progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list') |
1925 | + self.assertEqual(task, self.called_with) |
1926 | + |
1927 | + def test_successfully_lists_apps(self): |
1928 | + self.config.container_exists.return_value = True |
1929 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1930 | + progress = MockProgress.return_value |
1931 | + progress.done = False |
1932 | + task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback) |
1933 | + task._instant_callback = True |
1934 | + |
1935 | + with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer: |
1936 | + MockContainer.return_value.list_app_launchers.return_value = 'jarjar\nsidius' |
1937 | + task.start().join() |
1938 | + |
1939 | + progress.finished.assert_called_once_with('palpatine') |
1940 | + progress.data.assert_called_once_with('jarjar\nsidius') |
1941 | + self.assertEqual(task, self.called_with) |
1942 | |
1943 | === added file 'tests/unit/service/tasks/test_list_task.py' |
1944 | --- tests/unit/service/tasks/test_list_task.py 1970-01-01 00:00:00 +0000 |
1945 | +++ tests/unit/service/tasks/test_list_task.py 2016-11-16 17:34:14 +0000 |
1946 | @@ -0,0 +1,45 @@ |
1947 | +# Copyright 2016 Canonical Ltd. |
1948 | +# |
1949 | +# This program is free software: you can redistribute it and/or modify it |
1950 | +# under the terms of the GNU General Public License version 3, as published |
1951 | +# by the Free Software Foundation. |
1952 | +# |
1953 | +# This program is distributed in the hope that it will be useful, but |
1954 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1955 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1956 | +# PURPOSE. See the GNU General Public License for more details. |
1957 | +# |
1958 | +# You should have received a copy of the GNU General Public License along |
1959 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1960 | + |
1961 | + |
1962 | +import unittest.mock |
1963 | +from unittest import TestCase |
1964 | +from libertine.service import tasks |
1965 | +from libertine.ContainersConfig import ContainersConfig |
1966 | + |
1967 | + |
1968 | +class TestListTask(TestCase): |
1969 | + def setUp(self): |
1970 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
1971 | + self.connection = unittest.mock.Mock() |
1972 | + |
1973 | + def test_success_sends_data(self): |
1974 | + self.called_with = None |
1975 | + def callback(t): |
1976 | + self.called_with = t |
1977 | + |
1978 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
1979 | + progress = MockProgress.return_value |
1980 | + progress.done = False |
1981 | + task = tasks.ListTask(self.connection, callback) |
1982 | + task._instant_callback = True |
1983 | + |
1984 | + with unittest.mock.patch('libertine.service.tasks.list_task.utils.Libertine') as MockLibertine: |
1985 | + MockLibertine.list_containers.return_value = 'palpatine\nvader\nmaul' |
1986 | + task.start().join() |
1987 | + |
1988 | + progress.data.assert_called_once_with('palpatine\nvader\nmaul') |
1989 | + progress.finished.assert_called_once_with('') |
1990 | + |
1991 | + self.assertEqual(task, self.called_with) |
1992 | |
1993 | === added file 'tests/unit/service/tasks/test_remove_task.py' |
1994 | --- tests/unit/service/tasks/test_remove_task.py 1970-01-01 00:00:00 +0000 |
1995 | +++ tests/unit/service/tasks/test_remove_task.py 2016-11-16 17:34:14 +0000 |
1996 | @@ -0,0 +1,76 @@ |
1997 | +# Copyright 2016 Canonical Ltd. |
1998 | +# |
1999 | +# This program is free software: you can redistribute it and/or modify it |
2000 | +# under the terms of the GNU General Public License version 3, as published |
2001 | +# by the Free Software Foundation. |
2002 | +# |
2003 | +# This program is distributed in the hope that it will be useful, but |
2004 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2005 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2006 | +# PURPOSE. See the GNU General Public License for more details. |
2007 | +# |
2008 | +# You should have received a copy of the GNU General Public License along |
2009 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2010 | + |
2011 | + |
2012 | +import unittest.mock |
2013 | +from unittest import TestCase |
2014 | +from libertine.service import tasks |
2015 | +from libertine.ContainersConfig import ContainersConfig |
2016 | + |
2017 | + |
2018 | +class TestRemoveTask(TestCase): |
2019 | + def setUp(self): |
2020 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
2021 | + self.connection = unittest.mock.Mock() |
2022 | + self.lock = unittest.mock.MagicMock() |
2023 | + self.called_with = None |
2024 | + |
2025 | + def callback(self, task): |
2026 | + self.called_with = task |
2027 | + |
2028 | + def test_sends_error_on_non_installed_package(self): |
2029 | + self.config.get_package_install_status.return_value = 'installing' |
2030 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2031 | + progress = MockProgress.return_value |
2032 | + task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback) |
2033 | + task._instant_callback = True |
2034 | + task.start().join() |
2035 | + |
2036 | + progress.error.assert_called_once_with('Package \'darkside-common\' not installed, skipping remove') |
2037 | + self.assertEqual(task, self.called_with) |
2038 | + |
2039 | + def test_sends_error_on_failed_install(self): |
2040 | + self.config.get_package_install_status.return_value = 'installed' |
2041 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2042 | + progress = MockProgress.return_value |
2043 | + task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback) |
2044 | + task._instant_callback = True |
2045 | + |
2046 | + with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer: |
2047 | + MockContainer.return_value.remove_package.return_value = False |
2048 | + task.start().join() |
2049 | + |
2050 | + progress.error.assert_called_once_with("Package removal failed for 'darkside-common'") |
2051 | + self.config.update_package_install_status.assert_has_calls([ |
2052 | + unittest.mock.call('palpatine', 'darkside-common', 'removing'), |
2053 | + unittest.mock.call('palpatine', 'darkside-common', 'installed') |
2054 | + ], any_order=True) |
2055 | + self.assertEqual(task, self.called_with) |
2056 | + |
2057 | + def test_successfully_install(self): |
2058 | + self.config.get_package_install_status.return_value = 'installed' |
2059 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2060 | + progress = MockProgress.return_value |
2061 | + progress.done = False |
2062 | + task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback) |
2063 | + task._instant_callback = True |
2064 | + |
2065 | + with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer: |
2066 | + MockContainer.return_value.remove_package.return_value = True |
2067 | + task.start().join() |
2068 | + |
2069 | + progress.finished.assert_called_once_with('palpatine') |
2070 | + self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'removing') |
2071 | + self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common') |
2072 | + self.assertEqual(task, self.called_with) |
2073 | |
2074 | === added file 'tests/unit/service/tasks/test_search_task.py' |
2075 | --- tests/unit/service/tasks/test_search_task.py 1970-01-01 00:00:00 +0000 |
2076 | +++ tests/unit/service/tasks/test_search_task.py 2016-11-16 17:34:14 +0000 |
2077 | @@ -0,0 +1,43 @@ |
2078 | +# Copyright 2016 Canonical Ltd. |
2079 | +# |
2080 | +# This program is free software: you can redistribute it and/or modify it |
2081 | +# under the terms of the GNU General Public License version 3, as published |
2082 | +# by the Free Software Foundation. |
2083 | +# |
2084 | +# This program is distributed in the hope that it will be useful, but |
2085 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2086 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2087 | +# PURPOSE. See the GNU General Public License for more details. |
2088 | +# |
2089 | +# You should have received a copy of the GNU General Public License along |
2090 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2091 | + |
2092 | + |
2093 | +import unittest.mock |
2094 | +from unittest import TestCase |
2095 | +from libertine.service import tasks, apt |
2096 | + |
2097 | + |
2098 | +class TestSearchTask(TestCase): |
2099 | + def setUp(self): |
2100 | + self.connection = unittest.mock.Mock() |
2101 | + self.lock = unittest.mock.MagicMock() |
2102 | + self.cache = unittest.mock.create_autospec(apt.AptCache) |
2103 | + self.called_with = None |
2104 | + |
2105 | + def callback(self, task): |
2106 | + self.called_with = task |
2107 | + |
2108 | + def test_successfully_lists_apps(self): |
2109 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2110 | + progress = MockProgress.return_value |
2111 | + progress.done = False |
2112 | + task = tasks.SearchTask('palpatine', self.cache, 'jarjar', self.connection, self.callback) |
2113 | + task._instant_callback = True |
2114 | + |
2115 | + self.cache.search.return_value = ['jarjar', 'sidius'] |
2116 | + task.start().join() |
2117 | + |
2118 | + progress.finished.assert_called_once_with('palpatine') |
2119 | + progress.data.assert_called_once_with(str(['jarjar', 'sidius'])) |
2120 | + self.assertEqual(task, self.called_with) |
2121 | |
2122 | === added file 'tests/unit/service/tasks/test_update_task.py' |
2123 | --- tests/unit/service/tasks/test_update_task.py 1970-01-01 00:00:00 +0000 |
2124 | +++ tests/unit/service/tasks/test_update_task.py 2016-11-16 17:34:14 +0000 |
2125 | @@ -0,0 +1,80 @@ |
2126 | +# Copyright 2016 Canonical Ltd. |
2127 | +# |
2128 | +# This program is free software: you can redistribute it and/or modify it |
2129 | +# under the terms of the GNU General Public License version 3, as published |
2130 | +# by the Free Software Foundation. |
2131 | +# |
2132 | +# This program is distributed in the hope that it will be useful, but |
2133 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2134 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2135 | +# PURPOSE. See the GNU General Public License for more details. |
2136 | +# |
2137 | +# You should have received a copy of the GNU General Public License along |
2138 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2139 | + |
2140 | + |
2141 | +import unittest.mock |
2142 | +from unittest import TestCase |
2143 | +from libertine.service import tasks |
2144 | +from libertine.ContainersConfig import ContainersConfig |
2145 | + |
2146 | + |
2147 | +class TestUpdateTask(TestCase): |
2148 | + def setUp(self): |
2149 | + self.config = unittest.mock.create_autospec(ContainersConfig) |
2150 | + self.connection = unittest.mock.Mock() |
2151 | + self.lock = unittest.mock.MagicMock() |
2152 | + self.called_with = None |
2153 | + |
2154 | + def callback(self, task): |
2155 | + self.called_with = task |
2156 | + |
2157 | + def test_sends_error_on_non_existent_container(self): |
2158 | + self.config.container_exists.return_value = False |
2159 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2160 | + progress = MockProgress.return_value |
2161 | + task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback) |
2162 | + task._instant_callback = True |
2163 | + |
2164 | + with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer: |
2165 | + task.start().join() |
2166 | + |
2167 | + progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping update') |
2168 | + self.assertEqual(task, self.called_with) |
2169 | + |
2170 | + def test_sends_error_on_failed_update(self): |
2171 | + self.config.container_exists.return_value = True |
2172 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2173 | + progress = MockProgress.return_value |
2174 | + task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback) |
2175 | + task._instant_callback = True |
2176 | + |
2177 | + with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer: |
2178 | + MockContainer.return_value.update_libertine_container.return_value = False |
2179 | + task.start().join() |
2180 | + |
2181 | + progress.error.assert_called_once_with('Failed to update container \'palpatine\'') |
2182 | + self.config.update_container_install_status.assert_has_calls([ |
2183 | + unittest.mock.call('palpatine', 'updating'), |
2184 | + unittest.mock.call('palpatine', 'ready') |
2185 | + ], any_order=True) |
2186 | + self.assertEqual(task, self.called_with) |
2187 | + |
2188 | + def test_successfully_updates(self): |
2189 | + self.config.container_exists.return_value = True |
2190 | + with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: |
2191 | + progress = MockProgress.return_value |
2192 | + progress.done = False |
2193 | + task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback) |
2194 | + task._instant_callback = True |
2195 | + |
2196 | + with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer: |
2197 | + MockContainer.return_value.update_libertine_container.return_value = True |
2198 | + task.start().join() |
2199 | + |
2200 | + progress.finished.assert_called_once_with('palpatine') |
2201 | + self.config.update_container_install_status.assert_has_calls([ |
2202 | + unittest.mock.call('palpatine', 'updating'), |
2203 | + unittest.mock.call('palpatine', 'ready') |
2204 | + ], any_order=True) |
2205 | + self.assertEqual(task, self.called_with) |
2206 | |
2207 | === added file 'tests/unit/service/test_apt.py' |
2208 | --- tests/unit/service/test_apt.py 1970-01-01 00:00:00 +0000 |
2209 | +++ tests/unit/service/test_apt.py 2016-11-16 17:34:14 +0000 |
2210 | @@ -0,0 +1,134 @@ |
2211 | +# Copyright 2016 Canonical Ltd. |
2212 | +# |
2213 | +# This program is free software: you can redistribute it and/or modify it |
2214 | +# under the terms of the GNU General Public License version 3, as published |
2215 | +# by the Free Software Foundation. |
2216 | +# |
2217 | +# This program is distributed in the hope that it will be useful, but |
2218 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2219 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2220 | +# PURPOSE. See the GNU General Public License for more details. |
2221 | +# |
2222 | +# You should have received a copy of the GNU General Public License along |
2223 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2224 | + |
2225 | +import os |
2226 | +import unittest.mock |
2227 | +from unittest import TestCase |
2228 | +from libertine.service import apt |
2229 | + |
2230 | + |
2231 | +def build_mock_app(name, summary, website, description): |
2232 | + app = unittest.mock.Mock() |
2233 | + app.name = name |
2234 | + version = unittest.mock.Mock() |
2235 | + version.summary = summary |
2236 | + version.homepage = website |
2237 | + version.description = description |
2238 | + app.versions = [version] |
2239 | + return app |
2240 | + |
2241 | + |
2242 | +class TestAptCache(TestCase): |
2243 | + def test_search_returns_empty_when_no_matching_results(self): |
2244 | + with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache: |
2245 | + MockCache.return_value.keys.return_value = [] |
2246 | + |
2247 | + with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine: |
2248 | + MockLibertine.container_path.return_value = '/some/junk' |
2249 | + self.assertEqual(apt.AptCache('palpatine').search('vim'), []) |
2250 | + |
2251 | + def test_search_returns_matching_results(self): |
2252 | + with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache: |
2253 | + MockCache.return_value = { |
2254 | + "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"), |
2255 | + "gedit": build_mock_app("gedit", "text editor", "gedit.com", "visual text editor"), |
2256 | + "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "edit bitmap images"), |
2257 | + "vim-common": build_mock_app("vim-common", "common vim stuff", "vim.common", "dependencies") |
2258 | + } |
2259 | + |
2260 | + with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine: |
2261 | + MockLibertine.container_path.return_value = '/some/junk' |
2262 | + results = apt.AptCache('palpatine').search('vim') |
2263 | + |
2264 | + self.assertEqual(len(results), 2) |
2265 | + results = sorted(results, key=lambda xx: xx['id']) |
2266 | + self.assertEqual(results[0]['description'], 'who even uses raw vi') |
2267 | + self.assertEqual(results[0]['name'], 'vim') |
2268 | + self.assertEqual(results[0]['id'], 'vim') |
2269 | + self.assertEqual(results[0]['summary'], 'vi improved') |
2270 | + self.assertEqual(results[0]['website'], 'vim.com') |
2271 | + self.assertEqual(results[0]['package'], 'vim') |
2272 | + |
2273 | + self.assertEqual(results[1]['description'], 'dependencies') |
2274 | + self.assertEqual(results[1]['name'], 'vim-common') |
2275 | + self.assertEqual(results[1]['id'], 'vim-common') |
2276 | + self.assertEqual(results[1]['summary'], 'common vim stuff') |
2277 | + self.assertEqual(results[1]['website'], 'vim.common') |
2278 | + self.assertEqual(results[1]['package'], 'vim-common') |
2279 | + |
2280 | + MockCache.assert_called_once_with() |
2281 | + |
2282 | + def test_app_info_returns_empty_dict_when_no_such_app_exists(self): |
2283 | + with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache: |
2284 | + MockCache.return_value = {} |
2285 | + with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine: |
2286 | + MockLibertine.container_path.return_value = '/some/junk' |
2287 | + self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {}) |
2288 | + MockCache.assert_called_once_with() |
2289 | + |
2290 | + def test_app_info_returns_values_for_app(self): |
2291 | + with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache: |
2292 | + MockCache.return_value = { |
2293 | + "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"), |
2294 | + "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"), |
2295 | + } |
2296 | + with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine: |
2297 | + MockLibertine.container_path.return_value = '/some/junk' |
2298 | + self.assertEqual(apt.AptCache('palpatine').app_info("vim"), { |
2299 | + 'name': 'vim', |
2300 | + 'id': 'vim', |
2301 | + 'package': 'vim', |
2302 | + 'summary': 'vi improved', |
2303 | + 'description': 'who even uses raw vi', |
2304 | + 'website': 'vim.com' |
2305 | + }) |
2306 | + MockCache.assert_called_once_with() |
2307 | + |
2308 | + def test_loads_cache_from_container_directory(self): |
2309 | + with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache: |
2310 | + MockCache.return_value = { |
2311 | + "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"), |
2312 | + "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"), |
2313 | + } |
2314 | + with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine: |
2315 | + containerpath = "%s/containerroot" % os.path.dirname(os.path.realpath(__file__)) |
2316 | + MockLibertine.container_path.return_value = containerpath |
2317 | + self.assertEqual(apt.AptCache('palpatine').app_info("vim"), { |
2318 | + 'name': 'vim', |
2319 | + 'id': 'vim', |
2320 | + 'package': 'vim', |
2321 | + 'summary': 'vi improved', |
2322 | + 'description': 'who even uses raw vi', |
2323 | + 'website': 'vim.com' |
2324 | + }) |
2325 | + |
2326 | + MockCache.assert_called_once_with(rootdir=containerpath) |
2327 | + |
2328 | + def test_loads_cache_only_once(self): |
2329 | + with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache: |
2330 | + MockCache.return_value = { |
2331 | + "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"), |
2332 | + "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"), |
2333 | + } |
2334 | + with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine: |
2335 | + MockLibertine.container_path.return_value = '/some/junk' |
2336 | + cache = apt.AptCache('palpatine') |
2337 | + cache.app_info("vim") |
2338 | + cache.app_info("vim") |
2339 | + |
2340 | + MockCache.assert_called_once_with() |
2341 | + |
2342 | + |
2343 | +if __name__ == '__main__': |
2344 | + unittest.main() |
2345 | |
2346 | === added file 'tests/unit/service/test_container.py' |
2347 | --- tests/unit/service/test_container.py 1970-01-01 00:00:00 +0000 |
2348 | +++ tests/unit/service/test_container.py 2016-11-16 17:34:14 +0000 |
2349 | @@ -0,0 +1,202 @@ |
2350 | +# Copyright 2016 Canonical Ltd. |
2351 | +# |
2352 | +# This program is free software: you can redistribute it and/or modify it |
2353 | +# under the terms of the GNU General Public License version 3, as published |
2354 | +# by the Free Software Foundation. |
2355 | +# |
2356 | +# This program is distributed in the hope that it will be useful, but |
2357 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2358 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2359 | +# PURPOSE. See the GNU General Public License for more details. |
2360 | +# |
2361 | +# You should have received a copy of the GNU General Public License along |
2362 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2363 | + |
2364 | +import unittest.mock |
2365 | +from unittest import TestCase |
2366 | +from libertine.service import container |
2367 | +from libertine.service import tasks |
2368 | + |
2369 | + |
2370 | +class TestContainer(TestCase): |
2371 | + def setUp(self): |
2372 | + self._connection = unittest.mock.Mock() |
2373 | + self._config = unittest.mock.Mock() |
2374 | + self._lock = unittest.mock.Mock() |
2375 | + |
2376 | + def test_search_creates_search_task(self): |
2377 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2378 | + cache = MockCache.return_value |
2379 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2380 | + with unittest.mock.patch('libertine.service.container.SearchTask') as MockSearchTask: |
2381 | + c.search('darkseid') |
2382 | + MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._connection, unittest.mock.ANY) |
2383 | + MockSearchTask.return_value.start.assert_called_once_with() |
2384 | + |
2385 | + def test_app_info_creates_app_info_task(self): |
2386 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2387 | + cache = MockCache.return_value |
2388 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2389 | + with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask: |
2390 | + c.app_info('force') |
2391 | + MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._connection, unittest.mock.ANY) |
2392 | + MockAppInfoTask.return_value.start.assert_called_once_with() |
2393 | + |
2394 | + def test_app_info_gets_related_task_info(self): |
2395 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2396 | + cache = MockCache.return_value |
2397 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2398 | + with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask: |
2399 | + MockInstallTask.return_value.package = 'darkside' |
2400 | + MockInstallTask.return_value.matches.return_value = False |
2401 | + install_task_id = c.install('darkside') |
2402 | + with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask: |
2403 | + MockRemoveTask.return_value.package = 'darkside' |
2404 | + remove_task_id = c.remove('darkside') |
2405 | + with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask: |
2406 | + c.app_info('darkside') |
2407 | + MockAppInfoTask.assert_called_once_with('palpatine', cache, 'darkside', [install_task_id, remove_task_id], |
2408 | + self._config, self._connection, unittest.mock.ANY) |
2409 | + |
2410 | + def test_install_creates_install_task(self): |
2411 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2412 | + cache = MockCache.return_value |
2413 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2414 | + with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask: |
2415 | + c.install('force') |
2416 | + MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2417 | + MockInstallTask.return_value.start.assert_called_once_with() |
2418 | + |
2419 | + def test_install_only_calls_once_when_unfinished(self): |
2420 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2421 | + cache = MockCache.return_value |
2422 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2423 | + with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask: |
2424 | + c.install('darkside') |
2425 | + c.install('darkside') |
2426 | + c.install('darkside') |
2427 | + # MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2428 | + MockInstallTask.return_value.start.assert_called_once_with() |
2429 | + |
2430 | + def test_remove_creates_remove_task(self): |
2431 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2432 | + cache = MockCache.return_value |
2433 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2434 | + with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask: |
2435 | + c.remove('force') |
2436 | + MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2437 | + MockRemoveTask.return_value.start.assert_called_once_with() |
2438 | + |
2439 | + def test_remove_only_calls_once_when_unfinished(self): |
2440 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2441 | + cache = MockCache.return_value |
2442 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2443 | + with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask: |
2444 | + c.remove('darkside') |
2445 | + c.remove('darkside') |
2446 | + c.remove('darkside') |
2447 | + # MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2448 | + MockRemoveTask.return_value.start.assert_called_once_with() |
2449 | + |
2450 | + def test_create_creates_create_task(self): |
2451 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2452 | + cache = MockCache.return_value |
2453 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2454 | + with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask: |
2455 | + c.create('Emperor Palpatine', 'zesty', 'lxd', False) |
2456 | + MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False, |
2457 | + self._config, self._lock, self._connection, unittest.mock.ANY) |
2458 | + MockCreateTask.return_value.start.assert_called_once_with() |
2459 | + |
2460 | + def test_create_only_calls_once_when_unfinished(self): |
2461 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2462 | + cache = MockCache.return_value |
2463 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2464 | + with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask: |
2465 | + c.create('Emperor Palpatine', 'zesty', 'lxd', False) |
2466 | + c.create('Emperor Palpatine', 'zesty', 'lxd', False) |
2467 | + c.create('Emperor Palpatine', 'zesty', 'lxd', False) |
2468 | + # MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False, |
2469 | + # self._config, self._lock, self._connection, unittest.mock.ANY) |
2470 | + MockCreateTask.return_value.start.assert_called_once_with() |
2471 | + |
2472 | + def test_destroy_creates_destroy_task(self): |
2473 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2474 | + cache = MockCache.return_value |
2475 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2476 | + with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask: |
2477 | + c.destroy() |
2478 | + # MockDestroyTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2479 | + MockDestroyTask.return_value.start.assert_called_once_with() |
2480 | + |
2481 | + def test_destroy_only_calls_once_when_unfinished(self): |
2482 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2483 | + cache = MockCache.return_value |
2484 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2485 | + with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask: |
2486 | + c.destroy() |
2487 | + c.destroy() |
2488 | + c.destroy() |
2489 | + # MockDestroyTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2490 | + MockDestroyTask.return_value.start.assert_called_once_with() |
2491 | + |
2492 | + def test_update_creates_update_task(self): |
2493 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2494 | + cache = MockCache.return_value |
2495 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2496 | + with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask: |
2497 | + c.update() |
2498 | + MockUpdateTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2499 | + MockUpdateTask.return_value.start.assert_called_once_with() |
2500 | + |
2501 | + def test_update_only_calls_once_when_unfinished(self): |
2502 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2503 | + cache = MockCache.return_value |
2504 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2505 | + with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask: |
2506 | + c.update() |
2507 | + c.update() |
2508 | + c.update() |
2509 | + # MockUpdateTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY) |
2510 | + MockUpdateTask.return_value.start.assert_called_once_with() |
2511 | + |
2512 | + def test_list_apps_creates_list_apps_task(self): |
2513 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2514 | + cache = MockCache.return_value |
2515 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2516 | + with unittest.mock.patch('libertine.service.container.ListAppsTask') as MockListAppsTask: |
2517 | + c.list_apps() |
2518 | + MockListAppsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY) |
2519 | + MockListAppsTask.return_value.start.assert_called_once_with() |
2520 | + |
2521 | + def test_removes_task_during_callback(self): |
2522 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2523 | + cache = MockCache.return_value |
2524 | + c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task) |
2525 | + with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask: |
2526 | + MockInstallTask.return_value.package = 'force' |
2527 | + c.install('force') |
2528 | + self.assertEqual(1, len(MockInstallTask.return_value.start.mock_calls)) # ensure initial mocks were called |
2529 | + c.install('force') |
2530 | + self.assertEqual(1, len(MockInstallTask.return_value.start.mock_calls)) # ensure no more mocks were called |
2531 | + name, args, kwargs = MockInstallTask.mock_calls[0] |
2532 | + args[len(args)-1](MockInstallTask.return_value.start.return_value) |
2533 | + c.install('force') |
2534 | + self.assertEqual(2, len(MockInstallTask.return_value.start.mock_calls)) # ensure mocks were called again |
2535 | + |
2536 | + def test_completing_all_tasks_fires_callback(self): |
2537 | + with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
2538 | + cache = MockCache.return_value |
2539 | + self._container_id = None |
2540 | + def callback(container): |
2541 | + self._container_id = container.id |
2542 | + c = container.Container('palpatine', self._config, self._lock, self._connection, callback) |
2543 | + with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask: |
2544 | + c.install('force') |
2545 | + name, args, kwargs = MockInstallTask.mock_calls[0] |
2546 | + args[len(args)-1](MockInstallTask.return_value) |
2547 | + self.assertEqual('palpatine', self._container_id) |
2548 | + |
2549 | + |
2550 | +if __name__ == '__main__': |
2551 | + unittest.main() |
2552 | |
2553 | === added file 'tests/unit/service/test_task_dispatcher.py' |
2554 | --- tests/unit/service/test_task_dispatcher.py 1970-01-01 00:00:00 +0000 |
2555 | +++ tests/unit/service/test_task_dispatcher.py 2016-11-16 17:34:14 +0000 |
2556 | @@ -0,0 +1,148 @@ |
2557 | +# Copyright 2016 Canonical Ltd. |
2558 | +# |
2559 | +# This program is free software: you can redistribute it and/or modify it |
2560 | +# under the terms of the GNU General Public License version 3, as published |
2561 | +# by the Free Software Foundation. |
2562 | +# |
2563 | +# This program is distributed in the hope that it will be useful, but |
2564 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2565 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2566 | +# PURPOSE. See the GNU General Public License for more details. |
2567 | +# |
2568 | +# You should have received a copy of the GNU General Public License along |
2569 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2570 | + |
2571 | +import unittest.mock |
2572 | +from unittest import TestCase |
2573 | +from libertine.service import task_dispatcher |
2574 | + |
2575 | + |
2576 | +class TestTaskDispatcher(TestCase): |
2577 | + def setUp(self): |
2578 | + self._connection = unittest.mock.Mock() |
2579 | + self._config_patcher = unittest.mock.patch('libertine.service.task_dispatcher.libertine.ContainersConfig.ContainersConfig') |
2580 | + self._config_patcher.start() |
2581 | + self._dispatcher = task_dispatcher.TaskDispatcher(self._connection) |
2582 | + |
2583 | + def tearDown(self): |
2584 | + self._config_patcher.stop() |
2585 | + |
2586 | + def test_search_calls_search_on_container(self): |
2587 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2588 | + c = MockContainer.return_value |
2589 | + c.search.return_value = 123 |
2590 | + self.assertEqual(123, self._dispatcher.search('palpatine', 'sith')) |
2591 | + c.search.assert_called_once_with('sith') |
2592 | + |
2593 | + def test_app_info_calls_app_info_on_container(self): |
2594 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2595 | + c = MockContainer.return_value |
2596 | + c.app_info.return_value = 123 |
2597 | + self.assertEqual(123, self._dispatcher.app_info('palpatine', 'deathstar')) |
2598 | + c.app_info.assert_called_once_with('deathstar') |
2599 | + |
2600 | + def test_install_calls_install_on_container(self): |
2601 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2602 | + c = MockContainer.return_value |
2603 | + c.install.return_value = 123 |
2604 | + self.assertEqual(123, self._dispatcher.install('palpatine', 'darkside')) |
2605 | + c.install.assert_called_once_with('darkside') |
2606 | + |
2607 | + def test_remove_calls_remove_on_container(self): |
2608 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2609 | + c = MockContainer.return_value |
2610 | + c.remove.return_value = 123 |
2611 | + self.assertEqual(123, self._dispatcher.remove('palpatine', 'lightside')) |
2612 | + c.remove.assert_called_once_with('lightside') |
2613 | + |
2614 | + def test_create_calls_create_on_container(self): |
2615 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2616 | + c = MockContainer.return_value |
2617 | + c.create.return_value = 123 |
2618 | + self.assertEqual(123, self._dispatcher.create('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False)) |
2619 | + c.create.assert_called_once_with('Emperor Palpatine', 'zesty', 'lxd', False) |
2620 | + |
2621 | + def test_destroy_calls_destroy_on_container(self): |
2622 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2623 | + c = MockContainer.return_value |
2624 | + c.destroy.return_value = 123 |
2625 | + self.assertEqual(123, self._dispatcher.destroy('palpatine')) |
2626 | + c.destroy.assert_called_once_with() |
2627 | + |
2628 | + def test_update_calls_update_on_container(self): |
2629 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2630 | + c = MockContainer.return_value |
2631 | + c.update.return_value = 123 |
2632 | + self.assertEqual(123, self._dispatcher.update('palpatine')) |
2633 | + c.update.assert_called_once_with() |
2634 | + |
2635 | + def test_list_apps_calls_list_apps_on_container(self): |
2636 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2637 | + c = MockContainer.return_value |
2638 | + c.list_apps.return_value = 123 |
2639 | + self.assertEqual(123, self._dispatcher.list_apps('palpatine')) |
2640 | + c.list_apps.assert_called_once_with() |
2641 | + |
2642 | + def test_containers_reused_on_subsequent_calls(self): |
2643 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2644 | + c = MockContainer.return_value |
2645 | + c.id = 'palpatine' |
2646 | + self._dispatcher.list_apps('palpatine') |
2647 | + self._dispatcher.list_apps('palpatine') |
2648 | + self._dispatcher.list_apps('palpatine') |
2649 | + MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY) |
2650 | + |
2651 | + def test_container_callback_removes_container(self): |
2652 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2653 | + c = MockContainer.return_value |
2654 | + c.id = 'palpatine' |
2655 | + self._dispatcher.list_apps('palpatine') |
2656 | + MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY) |
2657 | + name, args, kwargs = MockContainer.mock_calls[0] |
2658 | + args[len(args)-1](MockContainer.return_value) |
2659 | + self._dispatcher.list_apps('palpatine') |
2660 | + MockContainer.assert_has_calls([ # verify container constructed twice |
2661 | + unittest.mock.call('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY), |
2662 | + unittest.mock.call('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY) |
2663 | + ], any_order=True) |
2664 | + |
2665 | + def test_container_info_creates_container_info_task(self): |
2666 | + with unittest.mock.patch('libertine.service.task_dispatcher.ContainerInfoTask') as MockContainerInfoTask: |
2667 | + task = MockContainerInfoTask.return_value |
2668 | + task.id = 123 |
2669 | + self.assertEqual(123, self._dispatcher.container_info('palpatine')) |
2670 | + MockContainerInfoTask.assert_called_once_with('palpatine', [], unittest.mock.ANY, self._connection, unittest.mock.ANY) |
2671 | + task.start.assert_called_once_with() |
2672 | + |
2673 | + def test_container_info_forwards_container_task_ids(self): |
2674 | + with unittest.mock.patch('libertine.service.task_dispatcher.ContainerInfoTask') as MockContainerInfoTask: |
2675 | + task = MockContainerInfoTask.return_value |
2676 | + with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
2677 | + c = MockContainer.return_value |
2678 | + c.tasks = [1, 2, 3] |
2679 | + c.id = 'palpatine' |
2680 | + self._dispatcher.list_apps('palpatine') # creates container |
2681 | + self._dispatcher.container_info('palpatine') |
2682 | + MockContainerInfoTask.assert_called_once_with('palpatine', [1, 2, 3], unittest.mock.ANY, self._connection, unittest.mock.ANY) |
2683 | + task.start.assert_called_once_with() |
2684 | + |
2685 | + def test_list_creates_list_task(self): |
2686 | + with unittest.mock.patch('libertine.service.task_dispatcher.ListTask') as MockListTask: |
2687 | + task = MockListTask.return_value |
2688 | + task.id = 123 |
2689 | + self.assertEqual(123, self._dispatcher.list()) |
2690 | + MockListTask.assert_called_once_with(self._connection, unittest.mock.ANY) |
2691 | + task.start.assert_called_once_with() |
2692 | + |
2693 | + def test_containerless_tasks_are_cleaned_up(self): |
2694 | + with unittest.mock.patch('libertine.service.task_dispatcher.ListTask') as MockListTask: |
2695 | + self._dispatcher.list() |
2696 | + MockListTask.assert_called_once_with(self._connection, unittest.mock.ANY) |
2697 | + name, args, kwargs = MockListTask.mock_calls[0] |
2698 | + self.assertEqual(1, len(self._dispatcher._tasks)) |
2699 | + args[len(args)-1](MockListTask.return_value) |
2700 | + self.assertEqual(0, len(self._dispatcher._tasks)) |
2701 | + |
2702 | + |
2703 | +if __name__ == '__main__': |
2704 | + unittest.main() |
2705 | |
2706 | === modified file 'tools/CMakeLists.txt' |
2707 | --- tools/CMakeLists.txt 2016-08-04 01:39:08 +0000 |
2708 | +++ tools/CMakeLists.txt 2016-11-16 17:34:14 +0000 |
2709 | @@ -1,4 +1,4 @@ |
2710 | -install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup |
2711 | +install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup libertined |
2712 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
2713 | install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 libertine-xmir.1 |
2714 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 |
2715 | |
2716 | === modified file 'tools/libertine-container-manager' |
2717 | --- tools/libertine-container-manager 2016-11-09 18:25:33 +0000 |
2718 | +++ tools/libertine-container-manager 2016-11-16 17:34:14 +0000 |
2719 | @@ -183,7 +183,7 @@ |
2720 | container = LibertineContainer(container_id) |
2721 | |
2722 | self.containers_config.update_container_install_status(container_id, "updating") |
2723 | - if container.update_libertine_container(args.verbosity) is not 0: |
2724 | + if not container.update_libertine_container(args.verbosity): |
2725 | self.containers_config.update_container_install_status(container_id, "ready") |
2726 | sys.exit(1) |
2727 | |
2728 | |
2729 | === added file 'tools/libertined' |
2730 | --- tools/libertined 1970-01-01 00:00:00 +0000 |
2731 | +++ tools/libertined 2016-11-16 17:34:14 +0000 |
2732 | @@ -0,0 +1,141 @@ |
2733 | +#!/usr/bin/python3 |
2734 | +# -*- coding: utf-8 -*- |
2735 | + |
2736 | +# Copyright 2016 Canonical Ltd. |
2737 | +# |
2738 | +# This program is free software: you can redistribute it and/or modify |
2739 | +# it under the terms of the GNU General Public License as published by |
2740 | +# the Free Software Foundation; version 3 of the License. |
2741 | +# |
2742 | +# This program is distributed in the hope that it will be useful, |
2743 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2744 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2745 | +# GNU General Public License for more details. |
2746 | +# |
2747 | +# You should have received a copy of the GNU General Public License |
2748 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2749 | + |
2750 | +import argparse |
2751 | +import signal |
2752 | +import sys |
2753 | +import os |
2754 | +from gi.repository import GLib, GObject |
2755 | +from libertine import utils |
2756 | +from libertine.service import manager |
2757 | + |
2758 | + |
2759 | +class OutputRedirector(object): |
2760 | + def __init__(self, config): |
2761 | + self.config = config |
2762 | + self.cache_path = '%s/.cache/libertined' % os.environ['HOME'] |
2763 | + self.cache_file = '%s/libertined.log' % self.cache_path |
2764 | + self.output_file = None |
2765 | + self.copied_stdout = None |
2766 | + self.copied_stderr = None |
2767 | + |
2768 | + def _fileno(self, file_or_fd): |
2769 | + return getattr(file_or_fd, 'fileno', lambda: file_or_fd)() |
2770 | + |
2771 | + def _do_redirect(self, stream): |
2772 | + fd = self._fileno(stream) |
2773 | + copied_stream = os.fdopen(os.dup(fd), 'wb') |
2774 | + stream.flush() |
2775 | + os.dup2(self._fileno(self.output_file), fd) |
2776 | + return copied_stream, fd |
2777 | + |
2778 | + def _undo_redirect(self, stream, stream_fd, copied_stream): |
2779 | + stream.flush() |
2780 | + os.dup2(copied_stream.fileno(), stream_fd) |
2781 | + copied_stream.close() |
2782 | + |
2783 | + def _rotate_logs(self): |
2784 | + os.makedirs(self.cache_path, exist_ok=True) |
2785 | + num_backups = 3 |
2786 | + for i in range(num_backups, 0, -1): |
2787 | + filename = '%s.%i' % (self.cache_file, i) |
2788 | + if os.path.exists(filename): |
2789 | + if i == num_backups: |
2790 | + os.remove(filename) |
2791 | + else: |
2792 | + os.rename(filename, '%s.%i' % (self.cache_file, i+1)) |
2793 | + if os.path.exists(self.cache_file): |
2794 | + os.rename(self.cache_file, '%s.1' % self.cache_file) |
2795 | + |
2796 | + def __enter__(self): |
2797 | + if self.config.debug: |
2798 | + os.environ['LIBERTINE_DEBUG'] = '1' |
2799 | + else: |
2800 | + if self.config.cache_output: |
2801 | + os.environ['LIBERTINE_DEBUG'] = '1' |
2802 | + self._rotate_logs() |
2803 | + self.output_file = open(self.cache_file, 'w') |
2804 | + else: |
2805 | + self.output_file = open(os.devnull, 'w') |
2806 | + |
2807 | + self.copied_stdout, self.stdout_fd = self._do_redirect(sys.stdout) |
2808 | + self.copied_stderr, self.stderr_fd = self._do_redirect(sys.stderr) |
2809 | + |
2810 | + def __exit__(self, type, value, tb): |
2811 | + if self.copied_stdout and self.stdout_fd: |
2812 | + self._undo_redirect(sys.stdout, self.stdout_fd, self.copied_stdout) |
2813 | + if self.copied_stderr and self.stderr_fd: |
2814 | + self._undo_redirect(sys.stderr, self.stderr_fd, self.copied_stderr) |
2815 | + if self.output_file: |
2816 | + self.output_file.close() |
2817 | + |
2818 | + |
2819 | +class Config(object): |
2820 | + def __init__(self): |
2821 | + self._arg_parser = argparse.ArgumentParser(description=u'Libertine Store service') |
2822 | + self._arg_parser.add_argument(u'-l', u"--use-local-cache", |
2823 | + action='store_true', |
2824 | + default=False, |
2825 | + help=u"use local cache instead of system cache") |
2826 | + self._arg_parser.add_argument(u'-d', u"--debug", |
2827 | + action='store_true', |
2828 | + default=False, |
2829 | + help=u"allow all output on stdout") |
2830 | + self._arg_parser.add_argument(u'-c', u"--cache-output", |
2831 | + action='store_true', |
2832 | + default=False, |
2833 | + help=u"Log to $HOME/.cache/libertined/ instead of stdout") |
2834 | + args = self._arg_parser.parse_args(namespace=Config) |
2835 | + |
2836 | + |
2837 | +class Loop(object): |
2838 | + def __init__(self): |
2839 | + GLib.unix_signal_add(GLib.PRIORITY_HIGH, |
2840 | + signal.SIGTERM, |
2841 | + self.sigterm, |
2842 | + None) |
2843 | + self.loop = GLib.MainLoop() |
2844 | + |
2845 | + def sigterm(self, code): |
2846 | + utils.get_logger().info("terminate ('%s') signal received" % code) |
2847 | + self.shutdown() |
2848 | + |
2849 | + def shutdown(self): |
2850 | + utils.get_logger().info("shutting service down") |
2851 | + self.loop.quit() |
2852 | + |
2853 | + def run(self): |
2854 | + utils.get_logger().debug("entering main loop") |
2855 | + self.loop.run() |
2856 | + |
2857 | + |
2858 | +def main(): |
2859 | + config = Config() |
2860 | + |
2861 | + with OutputRedirector(config): |
2862 | + utils.get_logger().info("Starting libertine service") |
2863 | + service = manager.Manager() |
2864 | + loop = Loop() |
2865 | + try: |
2866 | + loop.run() |
2867 | + except KeyboardInterrupt: |
2868 | + utils.get_logger().debug("keyboard interrupt received") |
2869 | + loop.shutdown() |
2870 | + |
2871 | + |
2872 | +if __name__ == '__main__': |
2873 | + main() |
FAILED: Continuous integration, rev:355 /jenkins. canonical. com/libertine/ job/lp- libertine- ci/197/ /jenkins. canonical. com/libertine/ job/build/ 442/console /jenkins. canonical. com/libertine/ job/build- 0-fetch/ 444 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= vivid+overlay/ 424/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= xenial+ overlay/ 424/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/424/ console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= vivid+overlay/ 424/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= xenial+ overlay/ 424/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= zesty/424/ console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/libertine/ job/lp- libertine- ci/197/ rebuild
https:/