Merge lp:~jameinel/bzr/2.3-bzr-connect-ssh into lp:bzr
Status: | Merged |
---|---|
Approved by: | Vincent Ladeuil on 2010-09-02 |
Approved revision: | 5406 |
Merged at revision: | 5405 |
Proposed branch: | lp:~jameinel/bzr/2.3-bzr-connect-ssh |
Merge into: | lp:bzr |
Diff against target: |
112 lines (+31/-24) 3 files modified
NEWS (+3/-0) bzrlib/tests/stub_sftp.py (+28/-22) bzrlib/tests/test_transport.py (+0/-2) |
To merge this branch: | bzr merge lp:~jameinel/bzr/2.3-bzr-connect-ssh |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vincent Ladeuil | 2010-09-01 | Approve on 2010-09-02 | |
Review via email:
|
Commit message
Fix bug #626876 by properly waiting for the paramiko.Transport thread to finish its work.
Description of the change
This should fix the failing test that we started skipping.
Specifically, the code was expecting "ssh_server.
However, it turns out that it returns as soon as "negotiation" has completed, not when it is done with the conversation.
It turns out that paramiko will set the "self.completio
As such, I went ahead and created a different event for us to wait on, that just triggers when the thread is actually done running. Maybe we could just use "ssh_server.
Part of the confusion is that:
a) TestServerInAThread is run in a thread, when it gets a connection it
b) Starts a new thread and calls process_request, which ends up calling
finish_request which is where the TestSFTPConnect
in __init__ it calls self.setup()
c) self.setup() spawns a new thread in paramiko.Transport. Though it isn't obvious at
first that paramiko.Transport is actually a subclass of Thread.
Anyway, it makes sense to have the test 'process_request' thread just wait for the paramiko.Transport thread to finish. Arguably we shouldn't be threaded at that level at all, and SftpServer shouldn't be spawning threads per request. (Instead we should just have the main SftpServer thread which spawns and waits for paramiko.Transport threads directly.)
This code works, the test passes reliably, and it doesn't seem to have affected any other tests negatively.
bzr selftest -s bt.test_transport bzr_ssh
bzr selftest "(?i)sftp"
John A Meinel (jameinel) wrote : | # |
- 5406. By John A Meinel on 2010-09-01
-
Use the simpler form for the test server
Vincent Ladeuil (vila) wrote : | # |
Well done !
A couple of tweaks and some comments.
8 +* Wait for the SSH server to actually finish, rather than just waiting for
9 + it to negotiate the key exchange. (John Arbash Meinel, #626876)
10 +
You commented on the abuse of the 'server' word but you use it here in a way that will
add to the confusion.
You're waiting for the *connection* to actually finish here and the way paramiko
presents it is misleading (using ssh_server as a variable name wasn't a good idea
in the first place either).
The SFTPServer uses TestingTCPServe
TestingThreadin
TestingTCPServe
TestingThreadin
This connection should not start processing the request (the ssh command here)
before the key exchange. I.e TestingSFTPConn
called after setup() should not start before the key exchange is done). The sleep
was enough... in some cases. But the join() *ensures* the synchronisation.
So:
28 server = tcs._server_
29 + # This blocks until the key exchange has been done
30 ssh_server.
is misleading too, that's the join() that guarantees that the key exchange
has occurred and that it's safe to process the request. This comment applies
to the .join() indeed.
I think that one confusing point is that ssh presents an encrypted medium
(paramiko.
can happen (paramiko.Channel which can be used a socket).
We don't really care about the distinction in our case and view the Transport + Channel
as a single socket that appears unencrypted to us.
What this means is that we shouldn't touch the socket before the key exchange has occurred
and your fix ensures that.
Anyway, thank you for finding this nasty one, we may still want to avoid spawning
a subprocess here though and clean up some of the tearDown done here which I'm pretty
sure is now useless.
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
...
>
> This connection should not start processing the request (the ssh command here)
> before the key exchange. I.e TestingSFTPConn
> called after setup() should not start before the key exchange is done). The sleep
> was enough... in some cases. But the join() *ensures* the synchronisation.
>
> So:
>
> 28 server = tcs._server_
> 29 + # This blocks until the key exchange has been done
> 30 ssh_server.
>
> is misleading too, that's the join() that guarantees that the key exchange
> has occurred and that it's safe to process the request. This comment applies
> to the .join() indeed.
No. The 'start_server' waits until the key negotiation has occurred.
After that point it does the actual communication on the channel.
The 'ssh_server.join()' waits all the way until the remote end
disconnects and everything has completed.
The reason this is 'okay' is because the .handle() method is *completely
empty*, and is just called immediately after .setup().
The BaseResponseHandler __init__ code is:
self.setup()
self.handle()
self.close() # or finish or whatever
>
> I think that one confusing point is that ssh presents an encrypted medium
> (paramiko.
> can happen (paramiko.Channel which can be used a socket).
> We don't really care about the distinction in our case and view the Transport + Channel
> as a single socket that appears unencrypted to us.
>
> What this means is that we shouldn't touch the socket before the key exchange has occurred
> and your fix ensures that.
That is true, but still what I said above is correct.
>
> Anyway, thank you for finding this nasty one, we may still want to avoid spawning
> a subprocess here though and clean up some of the tearDown done here which I'm pretty
> sure is now useless.
>
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAkx
pkoAoIrqn59BYHg
=UPuM
-----END PGP SIGNATURE-----
Vincent Ladeuil (vila) wrote : | # |
>
> No. The 'start_server' waits until the key negotiation has occurred.
start_server() internally calls Thread.start(), are you sure there is only one
other thread that can set self.completion
> After that point it does the actual communication on the channel.
>
> The 'ssh_server.join()' waits all the way until the remote end
> disconnects and everything has completed.
That's what I said :)
> The reason this is 'okay' is because the .handle() method is *completely
> empty*, and is just called immediately after .setup().
That also matches what I said, it shouldn't be empty.
It will be clearer to separate between setup, handle and finish, and put the join
into finish.
> The BaseResponseHandler __init__ code is:
>
> self.setup()
> self.handle()
> self.close() # or finish or whatever
Yes, finish() not whatever :)
> That is true, but still what I said above is correct.
Confusingly correct, that's my point.
- 5407. By John A Meinel on 2010-09-02
-
refactor a little bit, so it is clearer what is going on.
John A Meinel (jameinel) wrote : | # |
sent to pqm by email
- 5408. By John A Meinel on 2010-09-02
-
Add a .finish method to the sftp version as well.
John A Meinel (jameinel) wrote : | # |
sent to pqm by email
An even simpler one that works is to just change the line:
time.sleep(0.2)
to
ssh_server.join()
I've gone ahead and switched to that one, as I think it is easier to understand.