Merge lp:~diegosarmentero/ubuntuone-client/syncdaemon-q into lp:ubuntuone-client

Proposed by Diego Sarmentero on 2012-04-05
Status: Merged
Approved by: Natalia Bidart on 2012-04-18
Approved revision: 1228
Merged at revision: 1225
Proposed branch: lp:~diegosarmentero/ubuntuone-client/syncdaemon-q
Merge into: lp:ubuntuone-client
Diff against target: 96 lines (+35/-1)
4 files modified
tests/platform/windows/test_tools.py (+27/-0)
tests/syncdaemon/test_action_queue.py (+1/-0)
ubuntuone/platform/tools/windows.py (+3/-1)
ubuntuone/platform/windows/ipc_client.py (+4/-0)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntuone-client/syncdaemon-q
Reviewer Review Type Date Requested Status
Natalia Bidart 2012-04-05 Abstain on 2012-04-18
Alejandro J. Cura (community) Approve on 2012-04-18
Manuel de la Peña (community) Approve on 2012-04-16
dobey (community) Approve on 2012-04-05
Review via email: mp+100984@code.launchpad.net

Commit Message

- Avoid creating SyncDaemonTool if is not already running and the user wants to close it. (LP: #907479).

To post a comment you must log in.
dobey (dobey) wrote :

Do we really need to print the "not running" message here? I think just exiting cleanly would be fine, and it would change this block of code to just be "if running and options.quit:" which I think is better.

review: Needs Information
Diego Sarmentero (diegosarmentero) wrote :

We can't change the if to: "if running and options.quit:" because you will be leaving out a lot of options.
I can avoid printing the message and just exit if you think it's best, but i added the message to show some kind of feedback, so the user actually knows that something was executed.

dobey (dobey) wrote :

Approving, but we should probably fix that code to be more modular, so that all the options actually get handled separately in parsing. Right now, the logic is hard to separate when reading the diff/code.

review: Approve
Natalia Bidart (nataliabidart) wrote :

The creation of the sync_daemon_tool instance should not start the service, only the call to .start() should do it.

The changes applied to this branch are not incorrect, but I'm not comfortable with landing this, since we will be masquerading the real issue which is that the simple fact of creating an instance of SyncDaemonTool starts the service in windows (and it shouldn't).

review: Needs Fixing
Diego Sarmentero (diegosarmentero) wrote :

> The creation of the sync_daemon_tool instance should not start the service,
> only the call to .start() should do it.
>
> The changes applied to this branch are not incorrect, but I'm not comfortable
> with landing this, since we will be masquerading the real issue which is that
> the simple fact of creating an instance of SyncDaemonTool starts the service
> in windows (and it shouldn't).

The problem seems to be that in ubuntu_sso tcpactivation to get the active port, if sd is not running self._spawn_server() is called, and this opens sd but never close it.

    def _spawn_server(self):
        """Start running the server process."""
        # Without using close_fds=True, strange things happen
        # with logging on windows. More information at
        # http://bugs.python.org/issue4749
        with open(r"c:\Users\gatox\Desktop\output.txt", 'w') as f:
            f.write(repr(self.config.command_line))
        subprocess.Popen(self.config.command_line, close_fds=True)

    @defer.inlineCallbacks
    def _do_get_active_port(self):
        """Get the port for the running instance, starting it if needed."""
        is_running = yield self.is_already_running()
        if not is_running:
            self._spawn_server()
            yield self._wait_server_active()
        defer.returnValue(self.config.port)

I assume that this code (starting sd just to ask the port if running) is here for another reason, because it doesn't make sense with this issue, we wouldn't need to start sd to ask for the port, if we want to close it.

So, i'm not sure if the proper fix is the one already proposed, or we should close sd after asking for the port (which it doesn't have much sense to me).

Diego Sarmentero (diegosarmentero) wrote :
Download full text (3.5 KiB)

http://paste.ubuntu.com/921687/

These are the series of call that end up openning syncdaemon in the "_spawn_server" method.

----------------------------------------------------------
(u1sdtool)

@defer.inlineCallbacks
def main(options, args, stdout):
    """Entry point."""
    sync_daemon_tool = SyncDaemonTool(bus)

----------------------------------------------------------
(ubuntuone-client/ubuntuone/platform/tools/__init__.py)

class SyncDaemonTool(object):
    """Various utility methods to test/play with the SyncDaemon."""

    def __init__(self, bus=None):
        self.bus = bus
        self.last_event = 0
        self.delayed_call = None
        self.log = logger
        self.proxy = source.SyncDaemonToolProxy(bus=bus)

----------------------------------------------------------
(ubuntuone-client/ubuntuone/platform/tools/windows.py)

class SyncDaemonToolProxy(object):
...
    def __init__(self, bus=None):
        self.client = UbuntuOneClient()
        self.connected = self.client.connect()

----------------------------------------------------------
(ubuntuone-client/ubuntuone/platform/windows/ipc_client.py)

class UbuntuOneClient(object):
    """Root object that provides access to all the remote objects."""

    def __init__(self):
        """Create a new instance."""
        ...

    @defer.inlineCallbacks
    def connect(self):
        """Connect to the syncdaemon service."""
        # pylint: disable=W0702
        try:
            # connect to the remote objects
            self.factory = PBClientFactory()
            self.client = yield ipc_client_connect(self.factory)
            ...

----------------------------------------------------------
(ubuntuone-client/ubuntuone/platform/windows/ipc.py)

@defer.inlineCallbacks
def ipc_client_connect(client_factory):
    """Connect the IPC client factory."""
    ac = ActivationClient(get_activation_config())
    port = yield ac.get_active_port()

----------------------------------------------------------
(ubuntu-sso-client/ubuntu_sso/utils/tcpactivation.py)

class ActivationClient(ActivationDetector):
    """A client for tcp activation."""

    # a classwide lock, so the server is started only once
    lock = defer.DeferredLock()

    @defer.inlineCallbacks
    def _wait_server_active(self):
        """Wait till the server is active."""
        for _ in xrange(NUMBER_OF_CHECKS):
            is_running = yield self.is_already_running()
            if is_running:
                defer.returnValue(None)
            yield async_sleep(DELAY_BETWEEN_CHECKS)
        raise ActivationTimeoutError()

    def _spawn_server(self):
        """Start running the server process."""
        # Without using close_fds=True, strange things happen
        # with logging on windows. More information at
        # http://bugs.python.org/issue4749
        subprocess.Popen(self.config.command_line, close_fds=True)

    @defer.inlineCallbacks
    def _do_get_active_port(self):
        """Get the port for the running instance, starting it if needed."""
        is_running = yield self.is_already_running()
        if not is_running:
            self._spawn_server()
            yield self._wait_server_active()
        de...

Read more...

Diego Sarmentero (diegosarmentero) wrote :

> The creation of the sync_daemon_tool instance should not start the service,
> only the call to .start() should do it.
>
> The changes applied to this branch are not incorrect, but I'm not comfortable
> with landing this, since we will be masquerading the real issue which is that
> the simple fact of creating an instance of SyncDaemonTool starts the service
> in windows (and it shouldn't).

Done!

Alejandro J. Cura (alecu) wrote :

self.wait_connected() returns a deferred that is callbacked when the connection is done, so this branch won't work as expected.

review: Needs Fixing
Diego Sarmentero (diegosarmentero) wrote :

> self.wait_connected() returns a deferred that is callbacked when the
> connection is done, so this branch won't work as expected.

Fixed.
The call to wait_connection(), was wrong, and unnecessary, because inside of the proper function call "call_after_connection_inner", we are already doing: yield self.connected

Alejandro J. Cura (alecu) wrote :

Looks great now.

review: Approve
Alejandro J. Cura (alecu) wrote :

After some chat on IRC with nessita, it became clear that these areas of the branch need improvement:

<nessita> alecu: so, I'm not that happy with the definition of record_calls, since it also dismissed the arguments passed to the patched functions
<alecu> nessita, the point about record_calls is very valid: we should also be checking the arguments for those calls.
<nessita> alecu: also, I would advice using functools.partial for the pacth, and not defining a new record_calls

review: Needs Fixing
Manuel de la Peña (mandel) wrote :

+1

review: Approve
Ubuntu One Auto Pilot (otto-pilot) wrote :

Voting does not meet specified criteria. Required: Approve >= 1, Disapprove == 0, Needs Fixing == 0, Needs Information == 0, Resubmit == 0, Pending == 0. Got: 2 Approve, 2 Needs Fixing.

Alejandro J. Cura (alecu) wrote :

Looks good now.

review: Approve
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/windows/test_tools.py'
2--- tests/platform/windows/test_tools.py 2012-04-09 20:07:05 +0000
3+++ tests/platform/windows/test_tools.py 2012-04-16 12:24:17 +0000
4@@ -29,6 +29,7 @@
5 """Tests for some tools for talking to the syncdaemon."""
6
7 import sys
8+import operator
9
10 from twisted.internet import defer
11 from twisted.trial.unittest import TestCase
12@@ -50,6 +51,7 @@
13 yield super(TestSyncDaemonTool, self).setUp()
14 self.patch(windows.UbuntuOneClient, "connect", lambda _: defer.Deferred())
15 self.sdtool = windows.SyncDaemonToolProxy()
16+ self.calls = {}
17
18 def test_call_after_connection(self):
19 """Test the _call_after_connection method."""
20@@ -61,6 +63,31 @@
21 self.sdtool.connected.callback("got connected!")
22 self.assertEqual(len(collected_calls), 1)
23
24+ def test_call_after_connection_connect(self):
25+ """Test execute connect in _call_after_connection method."""
26+ self.patch(self.sdtool.client, "is_connected", lambda: False)
27+ my_connect = lambda *args, **kwargs: operator.setitem(
28+ self.calls, "connect", (args, kwargs))
29+ self.patch(self.sdtool.client, "connect", my_connect)
30+ collected_calls = []
31+ test_method = lambda *args: collected_calls.append(args)
32+ test_method = self.sdtool._call_after_connection(test_method)
33+ test_method(123)
34+ self.assertIn("connect", self.calls)
35+ self.assertEqual(self.calls["connect"], ((), {}))
36+
37+ def test_call_after_connection_not_connect(self):
38+ """Test execute connect in _call_after_connection method."""
39+ self.patch(self.sdtool.client, "is_connected", lambda: True)
40+ my_connect = lambda *args, **kwargs: operator.setitem(
41+ self.calls, "connect", (args, kwargs))
42+ self.patch(self.sdtool.client, "connect", my_connect)
43+ collected_calls = []
44+ test_method = lambda *args: collected_calls.append(args)
45+ test_method = self.sdtool._call_after_connection(test_method)
46+ test_method(123)
47+ self.assertNotIn("connect", self.calls)
48+
49 def test_should_wrap(self):
50 """Only some attributes should be wrapped."""
51 test_function = "sample_function"
52
53=== modified file 'tests/syncdaemon/test_action_queue.py'
54--- tests/syncdaemon/test_action_queue.py 2012-04-09 20:07:05 +0000
55+++ tests/syncdaemon/test_action_queue.py 2012-04-16 12:24:17 +0000
56@@ -1288,6 +1288,7 @@
57 orig = self.action_queue.clientConnectionLost
58
59 d = defer.Deferred()
60+
61 def faked_connectionLost(*args, **kwargs):
62 """Receive connection lost and check."""
63 orig(*args, **kwargs)
64
65=== modified file 'ubuntuone/platform/tools/windows.py'
66--- ubuntuone/platform/tools/windows.py 2012-04-09 20:07:05 +0000
67+++ ubuntuone/platform/tools/windows.py 2012-04-16 12:24:17 +0000
68@@ -126,10 +126,12 @@
69
70 def __init__(self, bus=None):
71 self.client = UbuntuOneClient()
72- self.connected = self.client.connect()
73+ self.connected = None
74
75 def _call_after_connection(self, method):
76 """Make sure Perspective Broker is connected before calling."""
77+ if not self.client.is_connected():
78+ self.connected = self.client.connect()
79
80 @defer.inlineCallbacks
81 def call_after_connection_inner(*args, **kwargs):
82
83=== modified file 'ubuntuone/platform/windows/ipc_client.py'
84--- ubuntuone/platform/windows/ipc_client.py 2012-04-09 20:07:05 +0000
85+++ ubuntuone/platform/windows/ipc_client.py 2012-04-16 12:24:17 +0000
86@@ -759,6 +759,10 @@
87 'Could not connect to the syncdaemon ipc.', e)
88 # pylint: disable=W0702
89
90+ def is_connected(self):
91+ """Return if the client is connected."""
92+ return (self.client is not None)
93+
94 @defer.inlineCallbacks
95 def register_to_signals(self):
96 """Register the different clients to the signals."""

Subscribers

People subscribed via source and target branches