Merge lp:~townsend/libertine/remove-unused-code into lp:libertine

Proposed by Christopher Townsend
Status: Merged
Approved by: Larry Price
Approved revision: 326
Merged at revision: 322
Proposed branch: lp:~townsend/libertine/remove-unused-code
Merge into: lp:libertine
Diff against target: 813 lines (+45/-555)
7 files modified
python/libertine/Libertine.py (+0/-298)
python/libertine/__init__.py (+0/-11)
python/libertine/launcher/session.py (+21/-3)
python/libertine/launcher/task.py (+4/-1)
tests/unit/test_launcher.py (+20/-62)
tests/unit/test_session_bridge.py (+0/-108)
tests/unit/test_sockets.py (+0/-72)
To merge this branch: bzr merge lp:~townsend/libertine/remove-unused-code
Reviewer Review Type Date Requested Status
Larry Price Approve
Libertine CI Bot continuous-integration Approve
Review via email: mp+309502@code.launchpad.net

Commit message

Remove unused code deprecated by the refactor-app-wrapper branch recently merged.
Move the code that adds and removes running apps to the ContainersConfig.json file.

To post a comment you must log in.
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

FAILED: Continuous integration, rev:323
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/191/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/libertine/job/build/434/console
    None: https://jenkins.canonical.com/libertine/job/lp-generic-update-mp/332/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/436
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=vivid+overlay/417
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=xenial+overlay/417
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=yakkety/417
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/416/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/416/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=yakkety/416/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/416/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/416/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=yakkety/416/console

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/191/rebuild

review: Needs Fixing (continuous-integration)
324. By Christopher Townsend

Catch ProcessLookupError when trying to kill service processes.

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

FAILED: Continuous integration, rev:324
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/192/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/libertine/job/build/435/console
    None: https://jenkins.canonical.com/libertine/job/lp-generic-update-mp/333/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/437
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=vivid+overlay/418
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=xenial+overlay/418
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=yakkety/418
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/417/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/417/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=yakkety/417/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/417/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/417/console
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=yakkety/417/console

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/192/rebuild

review: Needs Fixing (continuous-integration)
325. By Christopher Townsend

Add a base class for test_launcher.py for set up.

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:325
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/193/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/436
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/344
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/344
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=yakkety,testname=default/344
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/344
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/344
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=yakkety,testname=default/344
    None: https://jenkins.canonical.com/libertine/job/lp-generic-update-mp/334/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/438
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=vivid+overlay/419
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=xenial+overlay/419
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=yakkety/419
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/418
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/418/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/418
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/418/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=yakkety/418
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=yakkety/418/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/418
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/418/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/418
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/418/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=yakkety/418
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=yakkety/418/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/193/rebuild

review: Approve (continuous-integration)
326. By Christopher Townsend

Some more unused imports.

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:326
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/194/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/437
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/345
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/345
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=yakkety,testname=default/345
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/345
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/345
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=yakkety,testname=default/345
    None: https://jenkins.canonical.com/libertine/job/lp-generic-update-mp/335/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/439
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=vivid+overlay/420
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=xenial+overlay/420
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-1-sourcepkg/release=yakkety/420
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/419
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/419/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/419
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/419/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=yakkety/419
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=yakkety/419/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/419
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/419/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/419
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/419/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=yakkety/419
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=yakkety/419/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/194/rebuild

review: Approve (continuous-integration)
Revision history for this message
Larry Price (larryprice) wrote :

lgtm

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'python/libertine/Libertine.py'
2--- python/libertine/Libertine.py 2016-10-14 13:09:16 +0000
3+++ python/libertine/Libertine.py 2016-10-27 20:51:59 +0000
4@@ -14,19 +14,11 @@
5
6 from .AppDiscovery import AppLauncherCache
7 from gi.repository import Libertine
8-from multiprocessing import Process, active_children
9-from socket import *
10 import abc
11 import contextlib
12 import libertine.utils
13 import os
14-import psutil
15-import select
16 import shutil
17-import shlex
18-import signal
19-import sys
20-import time
21
22 from libertine.ContainersConfig import ContainersConfig
23 from libertine.HostInfo import HostInfo
24@@ -413,23 +405,6 @@
25 """
26 self.container.finish_application(app)
27
28- def launch_application(self, app_exec_line):
29- """
30- Launches an application in the container.
31-
32- :param app_exec_line: the application exec line as passed in by
33- ubuntu-app-launch
34- """
35- if self.containers_config.container_exists(self.container.container_id):
36- # Update $PATH as necessary
37- if '/usr/games' not in os.environ['PATH']:
38- os.environ['PATH'] = os.environ['PATH'] + ":/usr/games"
39-
40- app = self.start_application(app_exec_line, environ=os.environ)
41- self.finish_application(app)
42- else:
43- raise RuntimeError("Container with id %s does not exist." % self.container.container_id)
44-
45 def list_app_launchers(self, use_json=False):
46 """
47 Enumerates all application launchers (based on .desktop files) available
48@@ -482,276 +457,3 @@
49 return self.container.configure_remove_archive(archive, verbosity)
50 except RuntimeError as e:
51 return handle_runtime_error(e)
52-
53-
54-class Socket(object):
55- """
56- A simple socket wrapper class. This will wrap a socket
57-
58- :param socket: A python socket to be wrapped
59- """
60- def __init__(self, sock):
61- if not isinstance(sock, socket):
62- raise TypeError("expected socket to be a python socket class instead found: '{}'".format(sock.__class__))
63-
64- self.socket = sock
65-
66- """
67- Implement equality checking for other instances of this class or ints only.
68-
69- :param other: Either a Socket class or an int
70- """
71- def __eq__(self, other):
72- if isinstance(other, Socket):
73- return self.socket == other.socket
74- elif isinstance(other, socket):
75- return self.socket == other
76- elif isinstance(other, int):
77- return self.socket.fileno() == other
78- else:
79- raise TypeError("unsupported operand type(s) for ==: '{}' and '{}'".format(self.__class__, type(other)))
80-
81- def __ne__(self, other):
82- return not self == other
83-
84- def __hash__(self):
85- return self.socket.fileno()
86-
87- def socket(self):
88- return self.socket
89-
90-class SessionSocket(Socket):
91- """
92- Creates a AF_UNIX socket from a path to be used as the session socket.
93- This is used for RAII and to take ownership of the socket
94-
95- :param path: The path the socket will be binded with
96- """
97- def __init__(self, session_path):
98- try:
99- sock = socket(AF_UNIX, SOCK_STREAM)
100- except:
101- sock = None
102- raise
103- else:
104- try:
105- sock.bind(session_path)
106- sock.listen(5)
107- except:
108- sock.close()
109- sock = None
110- raise
111- else:
112- super().__init__(sock)
113- self.session_path = session_path
114-
115- def __del__(self):
116- self.socket.shutdown(SHUT_RDWR)
117- self.socket.close()
118- os.remove(self.session_path)
119-
120-
121-class HostSessionSocketPair():
122- def __init__(self, host_socket_path, session_socket_path):
123- self.host_socket_path = host_socket_path
124- self.session_socket_path = session_socket_path
125-
126-
127-class LibertineSessionBridge(object):
128- """
129- Creates a session bridge which will pair host and session sockets to proxy the info
130- from the session to the host and vice versa.
131-
132- :param host_session_socket_paths: A list of pairs container valid sockets to proxy {host:session}
133- """
134- def __init__(self, host_session_socket_paths):
135- self.descriptors = []
136- self.host_session_socket_path_map = {}
137- self.socket_pairs = {}
138-
139- for host_session_socket_pair in host_session_socket_paths:
140- host_path = host_session_socket_pair.host_socket_path
141- session_path = host_session_socket_pair.session_socket_path
142-
143- socket = SessionSocket(session_path)
144-
145- self.descriptors.append(socket)
146- self.host_session_socket_path_map.update({socket:host_path})
147-
148- """
149- If we end up going out of scope/error let's make sure we clean up sockets and paths.
150- """
151- def __del__(self):
152- self.socket_pairs = None
153- self.descriptors = None
154- self.host_session_socket_path_map = None
155-
156- """
157- When a new connection is made on one of the main sockets we have to create a new
158- socket pairing with the container socket.
159-
160- :param host_path: The raw path to the container socket
161- :param container_sock: The socket which received a new connection
162- """
163- def accept_new_connection(self, host_path, container_sock):
164- newconn = Socket(container_sock.accept()[0])
165- self.descriptors.append(newconn)
166-
167- host_sock = Socket(socket(AF_UNIX, SOCK_STREAM))
168- host_sock.socket.connect(host_path)
169- self.descriptors.append(host_sock)
170-
171- self.socket_pairs.update({newconn:host_sock})
172- self.socket_pairs.update({host_sock:newconn})
173-
174- """
175- Cleans up a socket that has had its connection closed.
176-
177- :param socket_to_remove: The socket that had its connection closed
178- """
179- def close_connections(self, socket_to_remove):
180- partner_socket = self.socket_pairs[socket_to_remove.fileno()]
181-
182- self.socket_pairs.pop(socket_to_remove.fileno())
183- self.socket_pairs.pop(partner_socket.socket.fileno())
184-
185- self.descriptors.remove(socket_to_remove)
186- self.descriptors.remove(partner_socket)
187-
188- if socket_to_remove in self.host_session_socket_path_map:
189- self.host_session_socket_path_map.pop(socket_to_remove)
190-
191- """
192- The main loop which uses select to block until one of the sockets we are listening to becomes readable.
193- It is advised this be started in its own process or thread, as this function blocks!
194- """
195- def main_loop(self):
196- while 1:
197- try:
198- raw_sockets = list(map(lambda x : x.socket, self.descriptors))
199- rlist, wlist, elist = select.select(raw_sockets, [], [])
200- except InterruptedError as e:
201- continue
202- except Exception as e:
203- libertine.utils.get_logger().exception(e)
204- break
205-
206- for sock in rlist:
207- if sock.fileno() == -1:
208- continue
209-
210- # Its possible to have multiple socket reads that are pairs. If this happens and we remove a pair we
211- # need to ignore the other pair since it no longer has a complete pair
212- if sock.fileno() not in self.host_session_socket_path_map and sock.fileno() not in self.socket_pairs:
213- continue
214-
215- if sock.fileno() in self.host_session_socket_path_map:
216- self.accept_new_connection(self.host_session_socket_path_map[sock.fileno()], sock)
217-
218- else:
219- try:
220- data = sock.recv(4096)
221- except Exception as e:
222- libertine.utils.get_logger().exception(e)
223- self.close_connections(sock)
224- continue
225- else:
226- if len(data) == 0:
227- self.close_connections(sock)
228- continue
229-
230- send_sock = self.socket_pairs[sock.fileno()].socket
231-
232- if send_sock.fileno() < 0:
233- continue
234-
235- totalsent = 0
236- while totalsent < len(data):
237- try:
238- sent = send_sock.send(data)
239- except BrokenPipeError as e:
240- libertine.utils.get_logger().exception(e)
241- self.close_connections(sock)
242- break
243- else:
244- if sent == 0:
245- close_connections(sock)
246- break
247- totalsent = totalsent + sent
248-
249-
250-class LibertineApplication(object):
251- """
252- Launches a libertine container with a session bridge for sockets such as dbus
253-
254- :param container_id: The container id.
255- :param app_exec_line: The exec line used to start the app in the container.
256- """
257- def __init__(self, container_id, app_exec_line):
258- signal.signal(signal.SIGTERM, self.cleanup)
259- signal.signal(signal.SIGINT, self.cleanup)
260-
261- self.container_id = container_id
262- self.app_exec_line = app_exec_line
263- self.lsb = None
264- self.pasted = None
265-
266- def cleanup(self, signum, frame):
267- self.cleanup_helpers()
268-
269- def cleanup_helpers(self):
270- self._remove_running_app()
271- self._close_lsb()
272- self._close_pasted()
273-
274- def _close_lsb(self):
275- if self.lsb is not None:
276- self.lsb_process.terminate()
277-
278- while active_children():
279- time.sleep(1)
280-
281- def _close_pasted(self):
282- if self.pasted is not None:
283- self.pasted.terminate()
284-
285- def _add_running_app(self):
286- ContainersConfig().add_running_app(self.container_id, self.app_exec_line[0])
287-
288- def _remove_running_app(self):
289- ContainersConfig().delete_running_app(self.container_id, self.app_exec_line[0])
290-
291- """
292- Launches the libertine session bridge. This creates a proxy socket to read to and from
293- for abstract sockets such as dbus.
294-
295- :param session_socket_paths: A list of socket paths the session will create.
296- """
297- def launch_session_bridge(self, session_socket_paths):
298- self.lsb = LibertineSessionBridge(session_socket_paths)
299- self.lsb_process = Process(target=self.lsb.main_loop)
300- self.lsb_process.start()
301-
302- """
303- Launches the pasted process for allowing copy and paste to work with X apps and
304- content-hub.
305- """
306- def launch_pasted(self):
307- self.pasted = psutil.Popen("pasted")
308-
309- """
310- Launches the container from the id and attempts to run the application exec.
311- """
312- def launch_application(self):
313- if not ContainersConfig().container_exists(self.container_id):
314- raise RuntimeError("Container ID %s does not exist." % self.container_id)
315-
316- container = LibertineContainer(self.container_id)
317- self._add_running_app()
318-
319- try:
320- container.launch_application(self.app_exec_line)
321- except:
322- raise
323- finally:
324- self.cleanup_helpers()
325
326=== modified file 'python/libertine/__init__.py'
327--- python/libertine/__init__.py 2016-08-04 23:21:52 +0000
328+++ python/libertine/__init__.py 2016-10-27 20:51:59 +0000
329@@ -22,21 +22,10 @@
330
331 __all__ = [
332 # from Libertine
333- 'LibertineApplication',
334 'LibertineContainer',
335- 'LibertineSessionBridge',
336- 'LibertineSessionBridge',
337- 'HostSessionSocketPair',
338- 'SessionSocket',
339- 'Socket',
340 'utils',
341 ]
342
343 __docformat__ = "restructuredtext en"
344
345-from libertine.Libertine import LibertineApplication
346 from libertine.Libertine import LibertineContainer
347-from libertine.Libertine import LibertineSessionBridge
348-from libertine.Libertine import HostSessionSocketPair
349-from libertine.Libertine import SessionSocket
350-from libertine.Libertine import Socket
351
352=== modified file 'python/libertine/launcher/session.py'
353--- python/libertine/launcher/session.py 2016-10-19 12:41:12 +0000
354+++ python/libertine/launcher/session.py 2016-10-27 20:51:59 +0000
355@@ -23,8 +23,9 @@
356 from .config import Config
357 from .. import utils
358 from contextlib import ExitStack, suppress
359+from libertine.ContainersConfig import ContainersConfig
360 from psutil import STATUS_ZOMBIE
361-from socket import socket, AF_UNIX, SOCK_STREAM
362+from socket import socket, AF_UNIX, SOCK_STREAM, SHUT_RDWR
363 from .task import LaunchServiceTask, TaskType
364
365
366@@ -94,7 +95,7 @@
367 be a tuneable parameter).
368 Also, it's a non-blocking write and that may affect things. Honestly, it
369 should be a non-blocking write and logic should be added to track
370- write-available evnts and unsent bytes and all that stuff but not today.
371+ write-available events and unsent bytes and all that stuff but not today.
372 """
373 try:
374 b = from_socket.recv(4096)
375@@ -105,7 +106,7 @@
376 log.info('close detected on {}'.format(from_socket))
377 return len(b)
378 except Exception as e:
379- log.exception(e)
380+ log.debug(e)
381 return 0
382
383 def _close_up_shop(self, session):
384@@ -118,7 +119,10 @@
385 this object from its watch list.
386 """
387 session.remove_bridge_pair(self)
388+ self.session_socket.shutdown(SHUT_RDWR)
389 self.session_socket.close()
390+
391+ self.host_socket.shutdown(SHUT_RDWR)
392 self.host_socket.close()
393
394
395@@ -239,9 +243,20 @@
396 """Connect to the container and start the application running."""
397 self._container.connect()
398 self.callback(self._container.disconnect)
399+ self._add_running_app()
400 self._app = self._container.start_application(self._config.exec_line,
401 self._config.session_environ)
402
403+ def _add_running_app(self):
404+ """Add a running app entry to ContainersConfig.json."""
405+ if self._config.container_id:
406+ ContainersConfig().add_running_app(self._config.container_id, self._config.exec_line[0])
407+
408+ def _remove_running_app(self):
409+ """Remove a running app entry from ContainersConfig.json."""
410+ if self._config.container_id:
411+ ContainersConfig().delete_running_app(self._config.container_id, self._config.exec_line[0])
412+
413 def _create_bridge_listener(self, bridge_config):
414 """Create a socket bridge listener for a socket bridge configuration.
415
416@@ -331,6 +346,9 @@
417 signal.signal(signal.SIGINT, self._sigint_handler)
418 signal.signal(signal.SIGTERM, self._sigterm_handler)
419
420+ if self._config.container_id:
421+ self._remove_running_app()
422+
423 for bridge_pair in self._config.socket_bridges:
424 os.remove(translate_to_real_address(bridge_pair.session_address))
425
426
427=== modified file 'python/libertine/launcher/task.py'
428--- python/libertine/launcher/task.py 2016-10-13 19:32:18 +0000
429+++ python/libertine/launcher/task.py 2016-10-27 20:51:59 +0000
430@@ -61,7 +61,10 @@
431
432 def stop(self):
433 """Shuts the service down. """
434- self._process.terminate()
435+ try:
436+ self._process.terminate()
437+ except ProcessLookupError:
438+ pass
439
440 def wait(self):
441 """Wait for service shutdown to complete.
442
443=== modified file 'tests/unit/test_launcher.py'
444--- tests/unit/test_launcher.py 2016-10-25 16:55:51 +0000
445+++ tests/unit/test_launcher.py 2016-10-27 20:51:59 +0000
446@@ -14,15 +14,11 @@
447
448 import os
449 import random
450-import shlex
451 import shutil
452 import signal
453 import string
454-import subprocess
455-import sys
456 import tempfile
457
458-from libertine import LibertineApplication
459 from libertine import LibertineContainer
460 from libertine import launcher
461
462@@ -41,61 +37,16 @@
463 """Generates a (hopefully) unique string."""
464 return prefix + ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(8))
465
466-
467-class TestLibertineLaunch(TestCase):
468+class TestLauncher(TestCase):
469 def setUp(self):
470- super(TestLibertineLaunch, self).setUp()
471- self.cmake_source_dir = os.environ['CMAKE_SOURCE_DIR']
472- self.cmake_binary_dir = os.environ['CMAKE_BINARY_DIR']
473-
474- container_config_path = os.path.join(self.cmake_binary_dir, 'tests', 'unit', 'libertine-config')
475-
476- # Set necessary enviroment variables
477- os.environ['XDG_DATA_HOME'] = container_config_path
478+ super(TestLauncher, self).setUp()
479 os.environ['XDG_RUNTIME_DIR'] = tempfile.mkdtemp()
480- os.environ['PATH'] = (self.cmake_source_dir + '/tests/mocks:' +
481- self.cmake_source_dir + '/tools:' + os.environ['PATH'])
482-
483 self.addCleanup(self.cleanup)
484
485- # Lets figure out how to really mock this....
486- cli_cmd = self.cmake_source_dir + '/tools/libertine-container-manager create -i test -n Test -t mock'
487- args = shlex.split(cli_cmd)
488- subprocess.Popen(args).wait()
489-
490 def cleanup(self):
491 shutil.rmtree(os.environ['XDG_RUNTIME_DIR'])
492
493- def test_launch_app_existing_container(self):
494- '''
495- Base line test to ensure launching an app in an existing container works.
496- '''
497- la = LibertineApplication('test', 'true')
498- la.launch_application()
499-
500- def test_launch_app_nonexistent_container(self):
501- '''
502- Test to make sure that things gracefully handle a non-existing container.
503- '''
504- la = LibertineApplication('test1', 'true')
505- self.assertRaises(RuntimeError, la.launch_application)
506-
507- def test_launch_good_app(self):
508- '''
509- Test to make sure that launching an app actually works.
510- '''
511- la = LibertineApplication('test', 'mock_app')
512- la.launch_application()
513-
514- def test_launch_bad_app(self):
515- '''
516- Test to make sure launching an app that doesn't exist doesn't break things
517- '''
518- la = LibertineApplication('test', 'foo')
519- self.assertRaises(FileNotFoundError, la.launch_application)
520-
521-
522-class TestLauncherTaskConfig(TestCase):
523+class TestLauncherTaskConfig(TestLauncher):
524 """Unit tests for libertine.launcher.task module."""
525
526 def test_task_config_ctor(self):
527@@ -108,7 +59,7 @@
528 self.assertThat(task.datum, Equals(fake_datum))
529
530
531-class TestLauncherConfig(TestCase):
532+class TestLauncherConfig(TestLauncher):
533 """
534 Verifies the defined behaviour of the Libertine Launcher Config class.
535 """
536@@ -230,7 +181,7 @@
537 MatchesPredicate(pasted_is_in_list, "pasted is not in task list"))
538
539
540-class TestLauncherSession(TestCase):
541+class TestLauncherSession(TestLauncher):
542 """
543 Verifies the defined bahaviour of a Libertine Lunacher session.
544
545@@ -246,7 +197,8 @@
546 fake_session_socket = 'unix:path=/tmp/garbage'
547 fake_bridge_config = launcher.SocketBridge('FAKE_SOCKET', 'dummy', fake_session_socket)
548 self._mock_config = MagicMock(spec=launcher.Config,
549- socket_bridges=[fake_bridge_config])
550+ socket_bridges=[fake_bridge_config],
551+ container_id=None)
552
553 self._fake_session_address = launcher.translate_to_real_address(fake_session_socket)
554
555@@ -328,7 +280,7 @@
556 self.assertThat(session_event_loop.is_alive(), Equals(False))
557
558
559-class TestLauncherServiceTask(TestCase):
560+class TestLauncherServiceTask(TestLauncher):
561 """Verify the expected bahaviour of launch tasks."""
562
563 def setUp(self):
564@@ -461,7 +413,7 @@
565 s.close()
566
567
568-class TestLauncherSessionSocketBridge(TestCase):
569+class TestLauncherSessionSocketBridge(TestLauncher):
570 """Verify the Launcher Session socket bridge functionality."""
571
572 _fake_session_socket = 'unix:path=/tmp/session-' + _generate_unique_string()
573@@ -478,7 +430,8 @@
574 config = MagicMock(spec=launcher.Config,
575 socket_bridges=[launcher.SocketBridge('FAKE_SOCKET',
576 host_address=EchoServer.socket_address,
577- session_address=self._fake_session_socket)])
578+ session_address=self._fake_session_socket)],
579+ container_id=None)
580 self._session = launcher.Session(config, mock_container)
581
582 def test_abstract_socket_is_used(self):
583@@ -536,7 +489,7 @@
584 self.assertThat(response, Contains('ping'))
585
586
587-class TestLauncherSessionTask(TestCase):
588+class TestLauncherSessionTask(TestLauncher):
589 """Verify how a Session handles Tasks."""
590
591 @patch('test_launcher.LibertineContainer')
592@@ -554,7 +507,11 @@
593 fake_datum = [self.getUniqueString()]
594 task = launcher.TaskConfig(launcher.TaskType.LAUNCH_SERVICE, fake_datum)
595
596- config = MagicMock(spec=launcher.Config, socket_bridges=[], prelaunch_tasks=[task], host_environ={})
597+ config = MagicMock(spec=launcher.Config,
598+ socket_bridges=[],
599+ prelaunch_tasks=[task],
600+ host_environ={},
601+ container_id=None)
602 self._session = launcher.Session(config, mock_container)
603
604 def test_session_starts_prelaunch_task(self):
605@@ -577,7 +534,7 @@
606 self.assertThat(self._mock_service_task.wait.called, Equals(True))
607
608
609-class TestLauncherContainerBehavior(TestCase):
610+class TestLauncherContainerBehavior(TestLauncher):
611 """Verify some expected behaviour when it comes to running the contained application."""
612
613 def setUp(self):
614@@ -585,7 +542,8 @@
615
616 self._mock_config = MagicMock(spec=launcher.Config,
617 socket_bridges=[],
618- session_environ={})
619+ session_environ={},
620+ container_id=None)
621
622 # Need to fake the ContainersConfig used internally by
623 # LibertineContainer... that whole outfit needs to be refactored for
624
625=== removed file 'tests/unit/test_session_bridge.py'
626--- tests/unit/test_session_bridge.py 2016-08-28 00:17:54 +0000
627+++ tests/unit/test_session_bridge.py 1970-01-01 00:00:00 +0000
628@@ -1,108 +0,0 @@
629-# Copyright 2016 Canonical Ltd.
630-#
631-# This program is free software: you can redistribute it and/or modify it
632-# under the terms of the GNU General Public License version 3, as published
633-# by the Free Software Foundation.
634-#
635-# This program is distributed in the hope that it will be useful, but
636-# WITHOUT ANY WARRANTY; without even the implied warranties of
637-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
638-# PURPOSE. See the GNU General Public License for more details.
639-#
640-# You should have received a copy of the GNU General Public License along
641-# with this program. If not, see <http://www.gnu.org/licenses/>.
642-
643-import os
644-import tempfile
645-from socket import *
646-import shutil
647-
648-from libertine import LibertineSessionBridge, HostSessionSocketPair, SessionSocket, LibertineApplication
649-from libertine import Socket
650-from multiprocessing import Process
651-
652-from testtools import TestCase
653-from testtools.matchers import Equals, NotEquals
654-from unittest import skip
655-
656-import time
657-import threading
658-
659-class TestLibertineSessionBridge(TestCase):
660- def setUp(self):
661- super(TestLibertineSessionBridge, self).setUp()
662-
663- xdg_runtime_path = tempfile.mkdtemp()
664-
665- self.addCleanup(self.cleanup)
666-
667- # Set necessary enviroment variables
668- os.environ['XDG_RUNTIME_DIR'] = xdg_runtime_path
669-
670- # Create r/w fake temp sockets, the session will be created by the lsb
671- self.host_path = os.path.join(xdg_runtime_path, 'HOST')
672- self.session_path = os.path.join(xdg_runtime_path, 'SESSION')
673-
674- self.host_socket = SessionSocket(self.host_path)
675- self.assertTrue(os.path.exists(self.host_path))
676-
677- """
678- Make sure we assert out socket is cleaned up. If we are failing here RAII
679- is broken.
680- """
681- def cleanup(self):
682- self.host_socket = None
683-
684- self.assertFalse(os.path.exists(self.session_path))
685- self.assertFalse(os.path.exists(self.host_path))
686- shutil.rmtree(os.environ['XDG_RUNTIME_DIR'])
687-
688- """
689- A function used to just read from the host socket. In a different thread
690- """
691- def host_read(self, expected_bytes):
692- conn, addr = self.host_socket.socket.accept()
693- data = conn.recv(1024)
694- conn.close()
695- self.assertThat(data, Equals(expected_bytes))
696-
697- def test_creates_socket_file(self):
698- """
699- We assert when we create a lsb we create the session socket
700- """
701- lsb = LibertineSessionBridge([HostSessionSocketPair(self.host_path, self.session_path)])
702- self.assertTrue(os.path.exists(self.session_path))
703-
704-
705- @skip("to be replaced by launcher.Session tests")
706- def test_data_read_from_host_to_session(self):
707- """
708- This test shows we are able to proxy data from a host socket to a session client.
709- We do this by:
710- 1) Create a valid host socket
711- 2) Start a thread which waits to asserts out host socket recv the expected data
712- 3) Start the lsb thread to create a proxy session socket
713- 4) We create a fake session client (socket) and connect to the session path
714- 5) We send the expected bytes to the fake session client socket
715- 6) We join on the host sock loop, if we never get the expected bytes in the host socket loop we fail
716- 7) Clean up, and assert all our threads are not alive and have been cleaned up
717- """
718- expected_bytes = b'Five exclamation marks, the sure sign of an insane mind.'
719- host_sock_loop = threading.Thread(target=self.host_read, args=(expected_bytes,))
720- host_sock_loop.start()
721-
722- lsb = LibertineSessionBridge([HostSessionSocketPair(self.host_path, self.session_path)])
723- lsb_process = Process(target=lsb.main_loop)
724- lsb_process.start()
725-
726- fake_session_client = socket(AF_UNIX, SOCK_STREAM)
727- fake_session_client.connect(self.session_path)
728- fake_session_client.sendall(expected_bytes)
729-
730- host_sock_loop.join(timeout=1)
731- fake_session_client.close()
732- lsb_process.terminate()
733- lsb_process.join(timeout=1)
734-
735- self.assertFalse(host_sock_loop.is_alive())
736- self.assertFalse(lsb_process.is_alive())
737
738=== removed file 'tests/unit/test_sockets.py'
739--- tests/unit/test_sockets.py 2016-08-25 01:49:07 +0000
740+++ tests/unit/test_sockets.py 1970-01-01 00:00:00 +0000
741@@ -1,72 +0,0 @@
742-# Copyright 2016 Canonical Ltd.
743-#
744-# This program is free software: you can redistribute it and/or modify it
745-# under the terms of the GNU General Public License version 3, as published
746-# by the Free Software Foundation.
747-#
748-# This program is distributed in the hope that it will be useful, but
749-# WITHOUT ANY WARRANTY; without even the implied warranties of
750-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
751-# PURPOSE. See the GNU General Public License for more details.
752-#
753-# You should have received a copy of the GNU General Public License along
754-# with this program. If not, see <http://www.gnu.org/licenses/>.
755-
756-from libertine import Socket
757-
758-from socket import socket
759-
760-from testtools import TestCase
761-from testtools.matchers import Equals, NotEquals
762-
763-class FakeSocket(Socket):
764- """
765- We are making things up here... soo lets not attempt to remove anything!
766- """
767- def __del__(self):
768- pass
769-
770-
771-class TestSocket(TestCase):
772- def setUp(self):
773- super(TestSocket, self).setUp()
774- self.socket1 = socket()
775- self.socket2 = socket()
776-
777- """
778- Test we can wrap a python socket.socket in our Socket class
779- """
780- def test_socket_warp(self):
781- s = FakeSocket(self.socket1)
782- self.assertThat(s, Equals(self.socket1))
783-
784- """
785- Test our equivalence operator works on three types:
786- 1: Other Socket classes
787- 2: Python socket classes
788- 3: The fd/socket raw Int value
789- """
790- def test_socket_eq_op(self):
791- s1 = FakeSocket(self.socket1)
792- s2 = FakeSocket(self.socket1)
793- self.assertThat(s1, Equals(s2))
794- self.assertThat(s2, Equals(self.socket1))
795- self.assertThat(s2, Equals(self.socket1.fileno()))
796-
797- """
798- Test our not equivalence works on the same three types
799- """
800- def test_socket_not_eq_op(self):
801- s1 = FakeSocket(self.socket1)
802- s2 = FakeSocket(self.socket2)
803- self.assertThat(s1, NotEquals(s2))
804- self.assertThat(s2, NotEquals(self.socket1))
805- self.assertThat(s2, NotEquals(self.socket1.fileno()))
806-
807- """
808- Test our Socket wrapper class is also hashable
809- """
810- def test_socket_is_hashable(self):
811- s = FakeSocket(self.socket1)
812- dic = {s: 17}
813- self.assertThat(dic[s], Equals(17))

Subscribers

People subscribed via source and target branches