Merge lp:~townsend/libertine/rebuild-running-app-state into lp:libertine
- rebuild-running-app-state
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Larry Price |
Approved revision: | 417 |
Merged at revision: | 412 |
Proposed branch: | lp:~townsend/libertine/rebuild-running-app-state |
Merge into: | lp:libertine |
Diff against target: |
513 lines (+178/-89) 8 files modified
python/libertine/Client.py (+19/-11) python/libertine/ContainersConfig.py (+12/-3) python/libertine/Libertine.py (+2/-0) python/libertine/LxcContainer.py (+23/-27) python/libertine/LxdContainer.py (+20/-22) python/libertine/launcher/session.py (+15/-7) python/libertine/service/manager.py (+12/-19) python/libertine/service/operations_state.py (+75/-0) |
To merge this branch: | bzr merge lp:~townsend/libertine/rebuild-running-app-state |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Larry Price | Approve | ||
Libertine CI Bot | continuous-integration | Approve | |
Review via email: mp+317548@code.launchpad.net |
Commit message
Fix libertined such that it can probe for currently running X apps and rebuild application running state.
Description of the change
Libertine CI Bot (libertine-ci-bot) wrote : | # |
Larry Price (larryprice) wrote : | # |
a few inlines - this is some complex stuff, and it'll be very nice to get it in.
- 409. By Christopher Townsend
-
Changes based on review.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:409
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: 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:409
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
- 410. By Christopher Townsend
-
Add invalidate method to the Client.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:410
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
- 411. By Christopher Townsend
-
A few more changes based on review.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:411
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
- 412. By Christopher Townsend
-
Merge lp:libertine.
- 413. By Christopher Townsend
-
Better handle container state when trying to destroy a container.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:413
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
Larry Price (larryprice) wrote : | # |
one inline - i'm about to load up the debs and do some verification
- 414. By Christopher Townsend
-
Only need to instantiate the ContainersConfig object once.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:414
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
- 415. By Christopher Townsend
-
Missed some variable inits.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:415
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
- 416. By Christopher Townsend
-
Used wrong init type.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:416
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
Larry Price (larryprice) wrote : | # |
So we don't forget, the bug I found EOD is that my bad pids aren't being cleaned up by libertined.
- 417. By Christopher Townsend
-
Fix removal of more than one invalid app.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:417
https:/
Executed test runs:
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:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'python/libertine/Client.py' | |||
2 | --- python/libertine/Client.py 2017-02-15 21:25:15 +0000 | |||
3 | +++ python/libertine/Client.py 2017-02-23 15:25:56 +0000 | |||
4 | @@ -20,6 +20,9 @@ | |||
5 | 20 | 20 | ||
6 | 21 | class Client(object): | 21 | class Client(object): |
7 | 22 | def __init__(self): | 22 | def __init__(self): |
8 | 23 | self._get_manager() | ||
9 | 24 | |||
10 | 25 | def _get_manager(self): | ||
11 | 23 | self._manager = None | 26 | self._manager = None |
12 | 24 | 27 | ||
13 | 25 | try: | 28 | try: |
14 | @@ -33,16 +36,25 @@ | |||
15 | 33 | except dbus.exceptions.DBusException as e: | 36 | except dbus.exceptions.DBusException as e: |
16 | 34 | utils.get_logger().warning("Exception raised while discovering d-bus service: {}".format(str(e))) | 37 | utils.get_logger().warning("Exception raised while discovering d-bus service: {}".format(str(e))) |
17 | 35 | 38 | ||
18 | 39 | def _do_operation(self, operation): | ||
19 | 40 | # It's possible that the service has gone down from when first getting the object. | ||
20 | 41 | # This catches the dbus excpetion if it did, and tries to reconnect to the service | ||
21 | 42 | # and then retry the dbus method. | ||
22 | 43 | while self.valid: | ||
23 | 44 | try: | ||
24 | 45 | return operation() | ||
25 | 46 | except dbus.exceptions.DBusException as e: | ||
26 | 47 | self._get_manager() | ||
27 | 48 | else: | ||
28 | 49 | return False | ||
29 | 50 | |||
30 | 36 | @property | 51 | @property |
31 | 37 | def valid(self): | 52 | def valid(self): |
32 | 38 | return self._manager is not None | 53 | return self._manager is not None |
33 | 39 | 54 | ||
34 | 40 | def container_operation_start(self, id): | 55 | def container_operation_start(self, id): |
35 | 41 | if not self.valid: | ||
36 | 42 | return False | ||
37 | 43 | |||
38 | 44 | retries = 0 | 56 | retries = 0 |
40 | 45 | while not self._manager.get_dbus_method('container_operation_start', self._interface)(id): | 57 | while not self._do_operation(lambda: self._manager.get_dbus_method('container_operation_start', self._interface)(id)): |
41 | 46 | retries += 1 | 58 | retries += 1 |
42 | 47 | if retries > 5: | 59 | if retries > 5: |
43 | 48 | return False | 60 | return False |
44 | @@ -50,12 +62,8 @@ | |||
45 | 50 | 62 | ||
46 | 51 | return True | 63 | return True |
47 | 52 | 64 | ||
50 | 53 | def container_operation_finished(self, id): | 65 | def container_operation_finished(self, id, app_name, pid): |
51 | 54 | return self.valid and self._manager.get_dbus_method("container_operation_finished", self._interface)(id) | 66 | return self._do_operation(lambda: self._manager.get_dbus_method("container_operation_finished", self._interface)(id, app_name, pid)) |
52 | 55 | 67 | ||
53 | 56 | def container_stopped(self, id): | 68 | def container_stopped(self, id): |
59 | 57 | if not self.valid: | 69 | return self._do_operation(lambda: self._manager.get_dbus_method('container_stopped', self._interface)(id)) |
55 | 58 | return False | ||
56 | 59 | |||
57 | 60 | self._manager.get_dbus_method('container_stopped', self._interface)(id) | ||
58 | 61 | return True | ||
60 | 62 | 70 | ||
61 | === modified file 'python/libertine/ContainersConfig.py' | |||
62 | --- python/libertine/ContainersConfig.py 2017-02-07 14:09:49 +0000 | |||
63 | +++ python/libertine/ContainersConfig.py 2017-02-23 15:25:56 +0000 | |||
64 | @@ -364,9 +364,18 @@ | |||
65 | 364 | app_obj = {'appExecName': app_exec_name, 'pid': pid} | 364 | app_obj = {'appExecName': app_exec_name, 'pid': pid} |
66 | 365 | self._set_value_by_key(container_id, 'runningApps', app_obj) | 365 | self._set_value_by_key(container_id, 'runningApps', app_obj) |
67 | 366 | 366 | ||
71 | 367 | def delete_running_app(self, container_id, app_exec_name): | 367 | def delete_running_app(self, container_id, app_obj): |
72 | 368 | self._delete_array_object_by_key_value(container_id, 'runningApps', | 368 | self._delete_array_object_by_value(container_id, 'runningApps', app_obj) |
73 | 369 | 'appExecName', app_exec_name) | 369 | |
74 | 370 | def find_running_app_by_name_and_pid(self, container_id, app_exec_name, pid): | ||
75 | 371 | for app in self.get_running_apps(container_id): | ||
76 | 372 | if app['appExecName'] == app_exec_name and app['pid'] == pid: | ||
77 | 373 | return app | ||
78 | 374 | |||
79 | 375 | return None | ||
80 | 376 | |||
81 | 377 | def get_running_apps(self, container_id): | ||
82 | 378 | return self._get_value_by_key(container_id, 'runningApps') or [] | ||
83 | 370 | 379 | ||
84 | 371 | """ | 380 | """ |
85 | 372 | Operations for bind-mount maintenance in a Libertine container. | 381 | Operations for bind-mount maintenance in a Libertine container. |
86 | 373 | 382 | ||
87 | === modified file 'python/libertine/Libertine.py' | |||
88 | --- python/libertine/Libertine.py 2017-02-09 15:40:37 +0000 | |||
89 | +++ python/libertine/Libertine.py 2017-02-23 15:25:56 +0000 | |||
90 | @@ -83,6 +83,8 @@ | |||
91 | 83 | self.container_type = container_type | 83 | self.container_type = container_type |
92 | 84 | self.container_id = container_id | 84 | self.container_id = container_id |
93 | 85 | self._config = config | 85 | self._config = config |
94 | 86 | self._app_name = '' | ||
95 | 87 | self._pid = 0 | ||
96 | 86 | self.root_path = utils.get_libertine_container_rootfs_path(self.container_id) | 88 | self.root_path = utils.get_libertine_container_rootfs_path(self.container_id) |
97 | 87 | self.locale = self._config.get_container_locale(container_id) | 89 | self.locale = self._config.get_container_locale(container_id) |
98 | 88 | self.language = self._get_language_from_locale() | 90 | self.language = self._get_language_from_locale() |
99 | 89 | 91 | ||
100 | === modified file 'python/libertine/LxcContainer.py' | |||
101 | --- python/libertine/LxcContainer.py 2017-02-15 21:12:37 +0000 | |||
102 | +++ python/libertine/LxcContainer.py 2017-02-23 15:25:56 +0000 | |||
103 | @@ -14,7 +14,6 @@ | |||
104 | 14 | 14 | ||
105 | 15 | import contextlib | 15 | import contextlib |
106 | 16 | import crypt | 16 | import crypt |
107 | 17 | import dbus | ||
108 | 18 | import lxc | 17 | import lxc |
109 | 19 | import os | 18 | import os |
110 | 20 | import psutil | 19 | import psutil |
111 | @@ -22,7 +21,6 @@ | |||
112 | 22 | import subprocess | 21 | import subprocess |
113 | 23 | import sys | 22 | import sys |
114 | 24 | import tempfile | 23 | import tempfile |
115 | 25 | import time | ||
116 | 26 | 24 | ||
117 | 27 | from .Libertine import BaseContainer | 25 | from .Libertine import BaseContainer |
118 | 28 | from . import utils, HostInfo, Client | 26 | from . import utils, HostInfo, Client |
119 | @@ -215,7 +213,8 @@ | |||
120 | 215 | return fd.read().strip('\n') != self.host_info.get_host_timezone() | 213 | return fd.read().strip('\n') != self.host_info.get_host_timezone() |
121 | 216 | 214 | ||
122 | 217 | def start_container(self): | 215 | def start_container(self): |
124 | 218 | self._manager.container_operation_start(self.container_id) | 216 | if not self._manager.container_operation_start(self.container_id): |
125 | 217 | return False | ||
126 | 219 | 218 | ||
127 | 220 | if self.container.state == 'RUNNING': | 219 | if self.container.state == 'RUNNING': |
128 | 221 | return True | 220 | return True |
129 | @@ -231,33 +230,21 @@ | |||
130 | 231 | return True | 230 | return True |
131 | 232 | 231 | ||
132 | 233 | def stop_container(self): | 232 | def stop_container(self): |
140 | 234 | if self._manager.valid: | 233 | if (self._manager.container_operation_finished(self.container_id, self._app_name, self._pid) and |
141 | 235 | if not self._manager.container_operation_finished(self.container_id): | 234 | lxc_stop(self.container, self._freeze_on_stop)): |
135 | 236 | return False | ||
136 | 237 | |||
137 | 238 | if not lxc_stop(self.container, self._freeze_on_stop): | ||
138 | 239 | return False | ||
139 | 240 | |||
142 | 241 | return self._manager.container_stopped(self.container_id) | 235 | return self._manager.container_stopped(self.container_id) |
145 | 242 | else: | 236 | |
146 | 243 | return lxc_stop(self.container, self._freeze_on_stop) | 237 | return False |
147 | 244 | 238 | ||
148 | 245 | def restart_container(self): | 239 | def restart_container(self): |
149 | 246 | if self.container.state != 'FROZEN': | 240 | if self.container.state != 'FROZEN': |
150 | 247 | utils.get_logger().warning("Container {} is not frozen. Cannot restart.".format(self.container_id)) | 241 | utils.get_logger().warning("Container {} is not frozen. Cannot restart.".format(self.container_id)) |
151 | 248 | return False | 242 | return False |
152 | 249 | 243 | ||
160 | 250 | orig_freeze = self._freeze_on_stop | 244 | if not (lxc_stop(self.container) and lxc_start(self.container)): |
154 | 251 | self._freeze_on_stop = False | ||
155 | 252 | |||
156 | 253 | # We never want to use the manager when restarting. | ||
157 | 254 | self._manager = None | ||
158 | 255 | |||
159 | 256 | if not (self.stop_container() and self.start_container()): | ||
161 | 257 | return False | 245 | return False |
162 | 258 | 246 | ||
165 | 259 | self._freeze_on_stop = orig_freeze | 247 | return lxc_stop(self.container, self._freeze_on_stop) |
164 | 260 | return self.stop_container() | ||
166 | 261 | 248 | ||
167 | 262 | def run_in_container(self, command_string): | 249 | def run_in_container(self, command_string): |
168 | 263 | cmd_args = shlex.split(command_string) | 250 | cmd_args = shlex.split(command_string) |
169 | @@ -276,7 +263,14 @@ | |||
170 | 276 | if not self.container.defined: | 263 | if not self.container.defined: |
171 | 277 | return False | 264 | return False |
172 | 278 | 265 | ||
174 | 279 | self.container.stop() | 266 | if self.container.state == 'RUNNING': |
175 | 267 | utils.get_logger().error("Canceling destruction due to running container") | ||
176 | 268 | return False | ||
177 | 269 | |||
178 | 270 | if self.container.state == 'FROZEN' and not lxc_stop(self.container): | ||
179 | 271 | utils.get_logger().error("Canceling destruction due to container not stopped") | ||
180 | 272 | return False | ||
181 | 273 | |||
182 | 280 | self.container.destroy() | 274 | self.container.destroy() |
183 | 281 | return True | 275 | return True |
184 | 282 | 276 | ||
185 | @@ -389,10 +383,6 @@ | |||
186 | 389 | self.container.save_config() | 383 | self.container.save_config() |
187 | 390 | 384 | ||
188 | 391 | def start_application(self, app_exec_line, environ): | 385 | def start_application(self, app_exec_line, environ): |
189 | 392 | if not self._manager.valid: | ||
190 | 393 | utils.get_logger().error("No interface to libertine-lxc-manager. Failing application launch.") | ||
191 | 394 | return | ||
192 | 395 | |||
193 | 396 | os.environ.clear() | 386 | os.environ.clear() |
194 | 397 | os.environ.update(environ) | 387 | os.environ.update(environ) |
195 | 398 | 388 | ||
196 | @@ -400,11 +390,17 @@ | |||
197 | 400 | self._manager.container_stopped(self.container_id) | 390 | self._manager.container_stopped(self.container_id) |
198 | 401 | return | 391 | return |
199 | 402 | 392 | ||
200 | 393 | self._app_name = app_exec_line[0] | ||
201 | 394 | |||
202 | 403 | app_launch_cmd = "sudo -E -u " + os.environ['USER'] + " env PATH=" + os.environ['PATH'] | 395 | app_launch_cmd = "sudo -E -u " + os.environ['USER'] + " env PATH=" + os.environ['PATH'] |
203 | 404 | cmd = shlex.split(app_launch_cmd) | 396 | cmd = shlex.split(app_launch_cmd) |
204 | 405 | app = self.container.attach(lxc.attach_run_command, | 397 | app = self.container.attach(lxc.attach_run_command, |
205 | 406 | cmd + app_exec_line) | 398 | cmd + app_exec_line) |
207 | 407 | return psutil.Process(app) | 399 | |
208 | 400 | proc = psutil.Process(app) | ||
209 | 401 | self._pid = proc.pid | ||
210 | 402 | |||
211 | 403 | return proc | ||
212 | 408 | 404 | ||
213 | 409 | def finish_application(self, app): | 405 | def finish_application(self, app): |
214 | 410 | os.waitpid(app.pid, 0) | 406 | os.waitpid(app.pid, 0) |
215 | 411 | 407 | ||
216 | === modified file 'python/libertine/LxdContainer.py' | |||
217 | --- python/libertine/LxdContainer.py 2017-02-22 15:35:46 +0000 | |||
218 | +++ python/libertine/LxdContainer.py 2017-02-23 15:25:56 +0000 | |||
219 | @@ -14,7 +14,6 @@ | |||
220 | 14 | 14 | ||
221 | 15 | import contextlib | 15 | import contextlib |
222 | 16 | import crypt | 16 | import crypt |
223 | 17 | import dbus | ||
224 | 18 | import os | 17 | import os |
225 | 19 | import psutil | 18 | import psutil |
226 | 20 | import pylxd | 19 | import pylxd |
227 | @@ -368,10 +367,14 @@ | |||
228 | 368 | utils.get_logger().error("No such container '%s'" % self.container_id) | 367 | utils.get_logger().error("No such container '%s'" % self.container_id) |
229 | 369 | return False | 368 | return False |
230 | 370 | 369 | ||
232 | 371 | if not (self.stop_container(wait=True) or self._container.status == 'Stopped'): | 370 | if self._container.status == 'Running': |
233 | 372 | utils.get_logger().error("Canceling destruction due to running container") | 371 | utils.get_logger().error("Canceling destruction due to running container") |
234 | 373 | return False | 372 | return False |
235 | 374 | 373 | ||
236 | 374 | if self._container.status == 'Frozen' and not lxd_stop(self._container): | ||
237 | 375 | utils.get_logger().error("Canceling destruction due to container not stopped") | ||
238 | 376 | return False | ||
239 | 377 | |||
240 | 375 | self._container.delete() | 378 | self._container.delete() |
241 | 376 | 379 | ||
242 | 377 | return self._delete_rootfs() | 380 | return self._delete_rootfs() |
243 | @@ -398,7 +401,8 @@ | |||
244 | 398 | if not self._try_get_container(): | 401 | if not self._try_get_container(): |
245 | 399 | return False | 402 | return False |
246 | 400 | 403 | ||
248 | 401 | self._manager.container_operation_start(self.container_id) | 404 | if not self._manager.container_operation_start(self.container_id): |
249 | 405 | return False | ||
250 | 402 | 406 | ||
251 | 403 | if self._container.status == 'Running': | 407 | if self._container.status == 'Running': |
252 | 404 | return True | 408 | return True |
253 | @@ -425,16 +429,11 @@ | |||
254 | 425 | if not self._try_get_container(): | 429 | if not self._try_get_container(): |
255 | 426 | return False | 430 | return False |
256 | 427 | 431 | ||
264 | 428 | if self._manager.valid: | 432 | if (self._manager.container_operation_finished(self.container_id, self._app_name, self._pid) and |
265 | 429 | if not self._manager.container_operation_finished(self.container_id): | 433 | lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop)): |
259 | 430 | return False | ||
260 | 431 | |||
261 | 432 | if not lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop): | ||
262 | 433 | return False | ||
263 | 434 | |||
266 | 435 | return self._manager.container_stopped(self.container_id) | 434 | return self._manager.container_stopped(self.container_id) |
269 | 436 | else: | 435 | |
270 | 437 | return lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop) | 436 | return False |
271 | 438 | 437 | ||
272 | 439 | def restart_container(self, wait=True): | 438 | def restart_container(self, wait=True): |
273 | 440 | if not self._try_get_container(): | 439 | if not self._try_get_container(): |
274 | @@ -444,17 +443,10 @@ | |||
275 | 444 | utils.get_logger().warning("Container {} is not frozen. Cannot restart.".format(self._container.name)) | 443 | utils.get_logger().warning("Container {} is not frozen. Cannot restart.".format(self._container.name)) |
276 | 445 | return False | 444 | return False |
277 | 446 | 445 | ||
285 | 447 | orig_freeze = self._freeze_on_stop | 446 | if not (lxd_stop(self._container) and lxd_start(self._container)): |
279 | 448 | self._freeze_on_stop = False | ||
280 | 449 | |||
281 | 450 | # We never want to use the manager when restarting. | ||
282 | 451 | self._manager = None | ||
283 | 452 | |||
284 | 453 | if not (self.stop_container(wait=True) and self.start_container()): | ||
286 | 454 | return False | 447 | return False |
287 | 455 | 448 | ||
290 | 456 | self._freeze_on_stop = orig_freeze | 449 | return lxd_stop(self._container, freeze_on_stop=self._freeze_on_stop) |
289 | 457 | return self.stop_container(wait=True) | ||
291 | 458 | 450 | ||
292 | 459 | def start_application(self, app_exec_line, environ): | 451 | def start_application(self, app_exec_line, environ): |
293 | 460 | if not self._try_get_container(): | 452 | if not self._try_get_container(): |
294 | @@ -467,10 +459,16 @@ | |||
295 | 467 | if not self.start_container(home=environ['HOME']): | 459 | if not self.start_container(home=environ['HOME']): |
296 | 468 | return False | 460 | return False |
297 | 469 | 461 | ||
298 | 462 | self._app_name = app_exec_line[0] | ||
299 | 463 | |||
300 | 470 | args = self._lxc_args("sudo -E -u {} env PATH={}".format(environ['USER'], environ['PATH']), environ) | 464 | args = self._lxc_args("sudo -E -u {} env PATH={}".format(environ['USER'], environ['PATH']), environ) |
301 | 471 | 465 | ||
302 | 472 | args.extend(app_exec_line) | 466 | args.extend(app_exec_line) |
304 | 473 | return psutil.Popen(args) | 467 | |
305 | 468 | proc = psutil.Popen(args) | ||
306 | 469 | self._pid = proc.pid | ||
307 | 470 | |||
308 | 471 | return proc | ||
309 | 474 | 472 | ||
310 | 475 | def finish_application(self, app): | 473 | def finish_application(self, app): |
311 | 476 | app.wait() | 474 | app.wait() |
312 | 477 | 475 | ||
313 | === modified file 'python/libertine/launcher/session.py' | |||
314 | --- python/libertine/launcher/session.py 2016-12-12 19:59:14 +0000 | |||
315 | +++ python/libertine/launcher/session.py 2017-02-23 15:25:56 +0000 | |||
316 | @@ -1,4 +1,4 @@ | |||
318 | 1 | # Copyright 2016 Canonical Ltd. | 1 | # Copyright 2016-2017 Canonical Ltd. |
319 | 2 | # | 2 | # |
320 | 3 | # This program is free software: you can redistribute it and/or modify it | 3 | # This program is free software: you can redistribute it and/or modify it |
321 | 4 | # under the terms of the GNU General Public License version 3, as published | 4 | # under the terms of the GNU General Public License version 3, as published |
322 | @@ -234,25 +234,36 @@ | |||
323 | 234 | handler, datum = key.data | 234 | handler, datum = key.data |
324 | 235 | handler(key.fd, datum) | 235 | handler(key.fd, datum) |
325 | 236 | self._container.finish_application(self._app) | 236 | self._container.finish_application(self._app) |
326 | 237 | |||
327 | 238 | if self._config.container_id: | ||
328 | 239 | self._remove_running_app() | ||
329 | 240 | |||
330 | 237 | self._stop_services() | 241 | self._stop_services() |
331 | 238 | 242 | ||
332 | 239 | def start_application(self): | 243 | def start_application(self): |
333 | 240 | """Connect to the container and start the application running.""" | 244 | """Connect to the container and start the application running.""" |
334 | 241 | self._container.connect() | 245 | self._container.connect() |
335 | 242 | self.callback(self._container.disconnect) | 246 | self.callback(self._container.disconnect) |
336 | 243 | self._add_running_app() | ||
337 | 244 | self._app = self._container.start_application(self._config.exec_line, | 247 | self._app = self._container.start_application(self._config.exec_line, |
338 | 245 | self._config.session_environ) | 248 | self._config.session_environ) |
339 | 249 | if self._app: | ||
340 | 250 | self._add_running_app() | ||
341 | 246 | 251 | ||
342 | 247 | def _add_running_app(self): | 252 | def _add_running_app(self): |
343 | 248 | """Add a running app entry to ContainersConfig.json.""" | 253 | """Add a running app entry to ContainersConfig.json.""" |
344 | 249 | if self._config.container_id: | 254 | if self._config.container_id: |
346 | 250 | ContainersConfig().add_running_app(self._config.container_id, self._config.exec_line[0]) | 255 | ContainersConfig().add_running_app(self._config.container_id, self._config.exec_line[0], self._app.pid) |
347 | 251 | 256 | ||
348 | 252 | def _remove_running_app(self): | 257 | def _remove_running_app(self): |
349 | 253 | """Remove a running app entry from ContainersConfig.json.""" | 258 | """Remove a running app entry from ContainersConfig.json.""" |
350 | 254 | if self._config.container_id: | 259 | if self._config.container_id: |
352 | 255 | ContainersConfig().delete_running_app(self._config.container_id, self._config.exec_line[0]) | 260 | containers_config = ContainersConfig() |
353 | 261 | running_app = containers_config.find_running_app_by_name_and_pid(self._config.container_id, | ||
354 | 262 | self._config.exec_line[0], | ||
355 | 263 | self._app.pid) | ||
356 | 264 | |||
357 | 265 | if running_app: | ||
358 | 266 | containers_config.delete_running_app(self._config.container_id, running_app) | ||
359 | 256 | 267 | ||
360 | 257 | def _create_bridge_listener(self, bridge_config): | 268 | def _create_bridge_listener(self, bridge_config): |
361 | 258 | """Create a socket bridge listener for a socket bridge configuration. | 269 | """Create a socket bridge listener for a socket bridge configuration. |
362 | @@ -343,9 +354,6 @@ | |||
363 | 343 | signal.signal(signal.SIGINT, self._sigint_handler) | 354 | signal.signal(signal.SIGINT, self._sigint_handler) |
364 | 344 | signal.signal(signal.SIGTERM, self._sigterm_handler) | 355 | signal.signal(signal.SIGTERM, self._sigterm_handler) |
365 | 345 | 356 | ||
366 | 346 | if self._config.container_id: | ||
367 | 347 | self._remove_running_app() | ||
368 | 348 | |||
369 | 349 | for bridge_pair in self._config.socket_bridges: | 357 | for bridge_pair in self._config.socket_bridges: |
370 | 350 | os.remove(translate_to_real_address(bridge_pair.session_address)) | 358 | os.remove(translate_to_real_address(bridge_pair.session_address)) |
371 | 351 | 359 | ||
372 | 352 | 360 | ||
373 | === modified file 'python/libertine/service/manager.py' | |||
374 | --- python/libertine/service/manager.py 2017-02-15 21:12:37 +0000 | |||
375 | +++ python/libertine/service/manager.py 2017-02-23 15:25:56 +0000 | |||
376 | @@ -15,7 +15,7 @@ | |||
377 | 15 | import dbus | 15 | import dbus |
378 | 16 | import dbus.service | 16 | import dbus.service |
379 | 17 | import libertine.service.task_dispatcher | 17 | import libertine.service.task_dispatcher |
381 | 18 | from collections import Counter | 18 | import libertine.service.operations_state |
382 | 19 | from dbus.mainloop.glib import DBusGMainLoop | 19 | from dbus.mainloop.glib import DBusGMainLoop |
383 | 20 | from libertine.service import container | 20 | from libertine.service import container |
384 | 21 | from libertine import utils | 21 | from libertine import utils |
385 | @@ -29,8 +29,9 @@ | |||
386 | 29 | class Manager(dbus.service.Object): | 29 | class Manager(dbus.service.Object): |
387 | 30 | def __init__(self): | 30 | def __init__(self): |
388 | 31 | utils.get_logger().debug("creating service") | 31 | utils.get_logger().debug("creating service") |
389 | 32 | self._operations = Counter() | ||
390 | 33 | DBusGMainLoop(set_as_default=True) | 32 | DBusGMainLoop(set_as_default=True) |
391 | 33 | self._operations_state = libertine.service.operations_state.OperationsState() | ||
392 | 34 | |||
393 | 34 | try: | 35 | try: |
394 | 35 | bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME, | 36 | bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME, |
395 | 36 | bus=dbus.SessionBus(), | 37 | bus=dbus.SessionBus(), |
396 | @@ -124,28 +125,20 @@ | |||
397 | 124 | def container_operation_start(self, container): | 125 | def container_operation_start(self, container): |
398 | 125 | utils.get_logger().debug("container_operation_start({})".format(container)) | 126 | utils.get_logger().debug("container_operation_start({})".format(container)) |
399 | 126 | 127 | ||
405 | 127 | if self._operations[container] == -1: | 128 | return self._operations_state.operation_start(container) |
401 | 128 | return False | ||
402 | 129 | |||
403 | 130 | self._operations[container] += 1 | ||
404 | 131 | return True | ||
406 | 132 | 129 | ||
407 | 133 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, | 130 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
409 | 134 | in_signature='s', | 131 | in_signature='ssi', |
410 | 135 | out_signature='b') | 132 | out_signature='b') |
412 | 136 | def container_operation_finished(self, container): | 133 | def container_operation_finished(self, container, app_name='', pid=0): |
413 | 137 | utils.get_logger().debug("container_operation_finished({})".format(container)) | 134 | utils.get_logger().debug("container_operation_finished({})".format(container)) |
422 | 138 | stop = False | 135 | |
423 | 139 | self._operations[container] -= 1 | 136 | return self._operations_state.operation_finished(container, app_name, pid) |
416 | 140 | |||
417 | 141 | if self._operations[container] == 0: | ||
418 | 142 | self._operations[container] -= 1 | ||
419 | 143 | stop = True | ||
420 | 144 | |||
421 | 145 | return stop | ||
424 | 146 | 137 | ||
425 | 147 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, | 138 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
427 | 148 | in_signature='s') | 139 | in_signature='s', |
428 | 140 | out_signature='b') | ||
429 | 149 | def container_stopped(self, container): | 141 | def container_stopped(self, container): |
430 | 150 | utils.get_logger().debug("container_stopped({})".format(container)) | 142 | utils.get_logger().debug("container_stopped({})".format(container)) |
432 | 151 | del self._operations[container] | 143 | |
433 | 144 | return self._operations_state.operation_stopped(container) | ||
434 | 152 | 145 | ||
435 | === added file 'python/libertine/service/operations_state.py' | |||
436 | --- python/libertine/service/operations_state.py 1970-01-01 00:00:00 +0000 | |||
437 | +++ python/libertine/service/operations_state.py 2017-02-23 15:25:56 +0000 | |||
438 | @@ -0,0 +1,75 @@ | |||
439 | 1 | # Copyright 2017 Canonical Ltd. | ||
440 | 2 | # | ||
441 | 3 | # This program is free software: you can redistribute it and/or modify | ||
442 | 4 | # it under the terms of the GNU General Public License as published by | ||
443 | 5 | # the Free Software Foundation; version 3 of the License. | ||
444 | 6 | # | ||
445 | 7 | # This program is distributed in the hope that it will be useful, | ||
446 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
447 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
448 | 10 | # GNU General Public License for more details. | ||
449 | 11 | # | ||
450 | 12 | # You should have received a copy of the GNU General Public License | ||
451 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
452 | 14 | |||
453 | 15 | import libertine.ContainersConfig | ||
454 | 16 | import psutil | ||
455 | 17 | |||
456 | 18 | from collections import Counter | ||
457 | 19 | from libertine import utils | ||
458 | 20 | |||
459 | 21 | |||
460 | 22 | class OperationsState(object): | ||
461 | 23 | def __init__(self): | ||
462 | 24 | self._get_running_apps_per_container() | ||
463 | 25 | |||
464 | 26 | def _get_running_apps_per_container(self): | ||
465 | 27 | self._invalid_apps = dict() | ||
466 | 28 | self._operations = Counter() | ||
467 | 29 | config = libertine.ContainersConfig.ContainersConfig() | ||
468 | 30 | |||
469 | 31 | for container in config.get_containers(): | ||
470 | 32 | running_apps = config.get_running_apps(container).copy() | ||
471 | 33 | |||
472 | 34 | for app in running_apps: | ||
473 | 35 | try: | ||
474 | 36 | proc = psutil.Process(app['pid']) | ||
475 | 37 | if app['appExecName'] in proc.cmdline(): | ||
476 | 38 | self._operations[container] += 1 | ||
477 | 39 | else: | ||
478 | 40 | raise | ||
479 | 41 | except: | ||
480 | 42 | utils.get_logger().error("Container app {} is not valid.".format(app['appExecName'])) | ||
481 | 43 | if container not in self._invalid_apps: | ||
482 | 44 | self._invalid_apps[container] = [{app['appExecName'], app['pid']}] | ||
483 | 45 | else: | ||
484 | 46 | self._invalid_apps[container].append({app['appExecName'], app['pid']}) | ||
485 | 47 | config.delete_running_app(container, app) | ||
486 | 48 | continue | ||
487 | 49 | |||
488 | 50 | def operation_start(self, container): | ||
489 | 51 | if self._operations[container] == -1: | ||
490 | 52 | return False | ||
491 | 53 | |||
492 | 54 | self._operations[container] += 1 | ||
493 | 55 | |||
494 | 56 | return True | ||
495 | 57 | |||
496 | 58 | def operation_finished(self, container, app_name, pid): | ||
497 | 59 | if container in self._invalid_apps and {app_name, pid} in self._invalid_apps[container]: | ||
498 | 60 | self._invalid_apps[container].remove({app_name, pid}) | ||
499 | 61 | if not self._invalid_apps[container]: | ||
500 | 62 | del self._invalid_apps[container] | ||
501 | 63 | else: | ||
502 | 64 | self._operations[container] -= 1 | ||
503 | 65 | |||
504 | 66 | if self._operations[container] == 0: | ||
505 | 67 | self._operations[container] = -1 | ||
506 | 68 | return True | ||
507 | 69 | |||
508 | 70 | return False | ||
509 | 71 | |||
510 | 72 | def operation_stopped(self, container): | ||
511 | 73 | del self._operations[container] | ||
512 | 74 | |||
513 | 75 | return True |
PASSED: Continuous integration, rev:408 /jenkins. canonical. com/libertine/ job/lp- libertine- ci/390/ /jenkins. canonical. com/libertine/ job/build/ 744 /jenkins. canonical. com/libertine/ job/test- 0-autopkgtest/ label=amd64, release= xenial+ overlay, testname= default/ 612 /jenkins. canonical. com/libertine/ job/test- 0-autopkgtest/ label=amd64, release= zesty,testname= default/ 612 /jenkins. canonical. com/libertine/ job/test- 0-autopkgtest/ label=i386, release= xenial+ overlay, testname= default/ 612 /jenkins. canonical. com/libertine/ job/test- 0-autopkgtest/ label=i386, release= zesty,testname= default/ 612 /jenkins. canonical. com/libertine/ job/build- 0-fetch/ 754 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= xenial+ overlay/ 735 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= xenial+ overlay/ 735/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/735 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/735/ artifact/ output/ *zip*/output. zip /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= xenial+ overlay/ 735 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= xenial+ overlay/ 735/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= zesty/735 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= zesty/735/ artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
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:/
Click here to trigger a rebuild: /jenkins. canonical. com/libertine/ job/lp- libertine- ci/390/ rebuild
https:/