Merge lp:~mandel/ubuntu-sso-client/fix-stale-broker into lp:ubuntu-sso-client

Proposed by Manuel de la Peña on 2012-08-30
Status: Merged
Approved by: Mike McCracken on 2012-08-30
Approved revision: 998
Merged at revision: 995
Proposed branch: lp:~mandel/ubuntu-sso-client/fix-stale-broker
Merge into: lp:ubuntu-sso-client
Diff against target: 169 lines (+111/-6)
2 files modified
ubuntu_sso/utils/ipc.py (+34/-6)
ubuntu_sso/utils/tests/test_ipc.py (+77/-0)
To merge this branch: bzr merge lp:~mandel/ubuntu-sso-client/fix-stale-broker
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve on 2012-08-30
Roberto Alsina (community) 2012-08-30 Approve on 2012-08-30
Review via email: mp+121973@code.launchpad.net

Commit Message

- Re-start sso if we get a stale broker amd re-request all the remote objects (LP: #1043367).

Description of the Change

- Re-start sso if we get a stale broker amd re-request all the remote objects (LP: #1043367).

If you want to do IRL simply start sso in one terminal, launch control panel and kill sso in the middle of the work.

To post a comment you must log in.
Roberto Alsina (ralsina) wrote :

Looks good to me

review: Approve
Mike McCracken (mikemc) wrote :

For posterity's sake: how to IRL test, so we know when this might happen in real life.

start with no creds
start sso-login
start u1-cp
cp will find no creds, show the initial screen asking you to log in or create a new account
** here, kill sso-login
then try to continue with CP, it should spawn a new sso-login and continue on its way.

if you already have creds, CP gets them once and caches them, so you shouldn't get a stale broker error if you've got creds successfully once, since it shouldn't call into sso again.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/utils/ipc.py'
2--- ubuntu_sso/utils/ipc.py 2012-08-09 21:17:19 +0000
3+++ ubuntu_sso/utils/ipc.py 2012-08-30 01:47:20 +0000
4@@ -237,10 +237,11 @@
5 call_remote_functions = [] # methods that can be called on the remote obj
6 signal_handlers = [] # signals that are of interest of this client
7
8- def __init__(self, remote_object):
9+ def __init__(self, base_client, remote_object):
10 """Create instance."""
11 super(RemoteClient, self).__init__()
12 self._mapping = defaultdict(list)
13+ self.base_client = base_client
14 self.remote = remote_object
15
16 # for each function name in self.call_remote_functions,
17@@ -284,6 +285,7 @@
18
19 return callback_wrapper
20
21+ @defer.inlineCallbacks
22 def call_method(self, method_name, *args, **kwargs):
23 """Call asynchronously 'method_name(*args)'.
24
25@@ -293,13 +295,23 @@
26 """
27 logger.debug('Performing %r as a remote call (%r, %r).',
28 method_name, args, kwargs)
29- result = self.remote.callRemote(method_name, *args)
30- return result
31+ try:
32+ result = yield self.remote.callRemote(method_name, *args)
33+ except DeadReferenceError:
34+ yield self.base_client.reconnect()
35+ result = yield self.call_method(method_name, *args, **kwargs)
36+ defer.returnValue(result)
37
38+ @defer.inlineCallbacks
39 def register_to_signals(self):
40 """Register to the signals."""
41- return self.remote.callRemote('register_to_signals', self,
42- self.signal_handlers)
43+ try:
44+ result = yield self.remote.callRemote('register_to_signals', self,
45+ self.signal_handlers)
46+ except DeadReferenceError:
47+ yield self.base_client.reconnect()
48+ result = yield self.register_to_signals()
49+ defer.returnValue(result)
50
51 def unregister_to_signals(self):
52 """Register to the signals."""
53@@ -352,7 +364,7 @@
54 self.clients.keys(), self.__class__.__name__)
55 for name, client_class in self.clients.items():
56 remote = yield root.callRemote('get_%s' % name)
57- setattr(self, name, client_class(remote))
58+ setattr(self, name, client_class(self, remote))
59
60 @defer.inlineCallbacks
61 def connect(self):
62@@ -367,6 +379,22 @@
63 yield self.register_to_signals()
64
65 @defer.inlineCallbacks
66+ def reconnect(self):
67+ """Reconnect with the server."""
68+ self.factory = PBClientFactory()
69+ self.client = yield client_connect(self.factory,
70+ self.service_name,
71+ self.service_cmdline,
72+ self.service_description)
73+ root = yield self.factory.getRootObject()
74+ # loop over the already present remote clients and reset their remotes
75+ for name in self.clients:
76+ remote = yield root.callRemote('get_%s' % name)
77+ remote_client = getattr(self, name)
78+ remote_client.remote = remote
79+ yield self.register_to_signals()
80+
81+ @defer.inlineCallbacks
82 def register_to_signals(self):
83 """Register all the clients to their signals."""
84 for name in self.clients:
85
86=== modified file 'ubuntu_sso/utils/tests/test_ipc.py'
87--- ubuntu_sso/utils/tests/test_ipc.py 2012-08-14 20:37:42 +0000
88+++ ubuntu_sso/utils/tests/test_ipc.py 2012-08-30 01:47:20 +0000
89@@ -619,3 +619,80 @@
90 fake_remote_client.random_exception,
91 )
92 self.assertTrue(self.memento.check_warning(*expected))
93+
94+
95+class FakeRootObject(object):
96+ """A fake root object."""
97+
98+ def __init__(self, called, remote_obj):
99+ """Create a new instance."""
100+ self.called = called
101+ self.remote_obj = remote_obj
102+
103+ # pylint: disable=C0103
104+ def callRemote(self, method_name):
105+ """A fake call remove method."""
106+ self.called.append(method_name)
107+ return defer.succeed(self.remote_obj)
108+ # pylint: enable=C0103
109+
110+
111+class FakeWorkingRemoteClient(object):
112+ """A fake remote client."""
113+
114+ def __init__(self, called):
115+ """Create a new instance."""
116+ self.remote = None
117+ self.called = called
118+
119+ def register_to_signals(self):
120+ """Register to signals."""
121+ self.called.append('register_to_signals')
122+ return defer.succeed(True)
123+
124+
125+class ReconnectTestCase(TestCase):
126+ """Test the reconnection when sso is dead."""
127+
128+ @defer.inlineCallbacks
129+ def setUp(self):
130+ """Set the different tests."""
131+ yield super(ReconnectTestCase, self).setUp()
132+ self.called = []
133+ self.remote_obj = 'remote'
134+ self.root_obj = FakeRootObject(self.called, self.remote_obj)
135+
136+ def fake_get_root_object():
137+ """Fake getting the root object."""
138+ self.called.append('getRootObject')
139+ return defer.succeed(self.root_obj)
140+
141+ def fake_client_connect(factory, service_name, cmd, description):
142+ """Fake the client connect."""
143+ self.called.append('client_connect')
144+ self.patch(factory, 'getRootObject', fake_get_root_object)
145+ return defer.succeed(True)
146+
147+ self.patch(ipc, 'client_connect', fake_client_connect)
148+
149+ @defer.inlineCallbacks
150+ def test_reconnect_method(self):
151+ """Test the execcution of the reconnect method."""
152+ clients = dict(first=FakeWorkingRemoteClient(self.called),
153+ second=FakeWorkingRemoteClient(self.called))
154+
155+ base_client = ipc.BaseClient()
156+ base_client.clients = clients
157+ for name, client in clients.items():
158+ setattr(base_client, name, client)
159+
160+ yield base_client.reconnect()
161+ # assert that we did call the correct methods
162+ self.assertIn('client_connect', self.called)
163+ self.assertIn('getRootObject', self.called)
164+
165+ for name in clients:
166+ self.assertIn('get_%s' % name, self.called)
167+
168+ self.assertEqual(len(clients),
169+ self.called.count('register_to_signals'))

Subscribers

People subscribed via source and target branches