Rev 2820: [MERGE][0.91] Make RemoteRepository.sprout cope gracefully with servers that don't support the 'Repository.tarball' request. in http://sourcefrog.net/bzr/0.91-integration
Martin Pool
mbp at sourcefrog.net
Wed Sep 26 01:53:10 BST 2007
At http://sourcefrog.net/bzr/0.91-integration
------------------------------------------------------------
revno: 2820
revision-id: mbp at sourcefrog.net-20070926005308-30n4v1t2lejhkjap
parent: mbp at sourcefrog.net-20070926005005-xbsj9nwk6b3jgw48
parent: andrew.bennetts at canonical.com-20070914025541-xymskacw08apuoq5
committer: Martin Pool <mbp at sourcefrog.net>
branch nick: 0.91-integration
timestamp: Wed 2007-09-26 10:53:08 +1000
message:
[MERGE][0.91] Make RemoteRepository.sprout cope gracefully with servers that don't support the 'Repository.tarball' request.
modified:
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
------------------------------------------------------------
revno: 2816.2.3
revision-id: andrew.bennetts at canonical.com-20070914025541-xymskacw08apuoq5
parent: andrew.bennetts at canonical.com-20070914024300-om8lbs7nag4n44u4
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: tarball-back-compat-for-0.91
timestamp: Fri 2007-09-14 12:55:41 +1000
message:
Improve the test.
modified:
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
------------------------------------------------------------
revno: 2816.2.2
revision-id: andrew.bennetts at canonical.com-20070914024300-om8lbs7nag4n44u4
parent: andrew.bennetts at canonical.com-20070914013818-yvv1e53dsc6gagss
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: tarball-back-compat-for-0.91
timestamp: Fri 2007-09-14 12:43:00 +1000
message:
Make the fallback a little tidier.
modified:
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
------------------------------------------------------------
revno: 2816.2.1
revision-id: andrew.bennetts at canonical.com-20070914013818-yvv1e53dsc6gagss
parent: pqm at pqm.ubuntu.com-20070912051907-80aaitfew8esgq23
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: tarball-back-compat-for-0.91
timestamp: Fri 2007-09-14 11:38:18 +1000
message:
Cope gracefully if the server doesn't support the Repository.tarball smart request.
modified:
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py 2007-09-11 08:03:10 +0000
+++ b/bzrlib/remote.py 2007-09-14 02:43:00 +0000
@@ -500,13 +500,14 @@
return self._real_repository.break_lock()
def _get_tarball(self, compression):
- """Return a TemporaryFile containing a repository tarball"""
+ """Return a TemporaryFile containing a repository tarball.
+
+ Returns None if the server does not support sending tarballs.
+ """
import tempfile
path = self.bzrdir._path_for_remote_call(self._client)
response, protocol = self._client.call_expecting_body(
'Repository.tarball', path, compression)
- assert response[0] in ('ok', 'failure'), \
- 'unexpected response code %s' % (response,)
if response[0] == 'ok':
# Extract the tarball and return it
t = tempfile.NamedTemporaryFile()
@@ -514,14 +515,23 @@
t.write(protocol.read_body_bytes())
t.seek(0)
return t
- else:
- raise errors.SmartServerError(error_code=response)
+ if (response == ('error', "Generic bzr smart protocol error: "
+ "bad request 'Repository.tarball'") or
+ response == ('error', "Generic bzr smart protocol error: "
+ "bad request u'Repository.tarball'")):
+ protocol.cancel_read_body()
+ return None
+ raise errors.UnexpectedSmartServerResponse(response)
def sprout(self, to_bzrdir, revision_id=None):
# TODO: Option to control what format is created?
- to_repo = to_bzrdir.create_repository()
- self._copy_repository_tarball(to_repo, revision_id)
- return to_repo
+ to_repo = self._copy_repository_tarball(to_bzrdir, revision_id)
+ if to_repo is None:
+ self._ensure_real()
+ return self._real_repository.sprout(
+ to_bzrdir, revision_id=revision_id)
+ else:
+ return to_repo
### These methods are just thin shims to the VFS object for now.
@@ -677,7 +687,7 @@
return self._real_repository.copy_content_into(
destination, revision_id=revision_id)
- def _copy_repository_tarball(self, destination, revision_id=None):
+ def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
# get a tarball of the remote repository, and copy from that into the
# destination
from bzrlib import osutils
@@ -687,6 +697,9 @@
# TODO: Maybe a progress bar while streaming the tarball?
note("Copying repository content as tarball...")
tar_file = self._get_tarball('bz2')
+ if tar_file is None:
+ return None
+ destination = to_bzrdir.create_repository()
try:
tar = tarfile.open('repository', fileobj=tar_file,
mode='r|bz2')
@@ -700,9 +713,7 @@
osutils.rmtree(tmpdir)
finally:
tar_file.close()
- # TODO: if the server doesn't support this operation, maybe do it the
- # slow way using the _real_repository?
- #
+ return destination
# TODO: Suggestion from john: using external tar is much faster than
# python's tarfile library, but it may not work on windows.
=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py 2007-07-19 06:34:09 +0000
+++ b/bzrlib/tests/test_remote.py 2007-09-14 02:55:41 +0000
@@ -106,11 +106,18 @@
class FakeProtocol(object):
"""Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
- def __init__(self, body):
+ def __init__(self, body, fake_client):
self._body_buffer = StringIO(body)
+ self._fake_client = fake_client
def read_body_bytes(self, count=-1):
- return self._body_buffer.read(count)
+ bytes = self._body_buffer.read(count)
+ if self._body_buffer.tell() == len(self._body_buffer.getvalue()):
+ self._fake_client.expecting_body = False
+ return bytes
+
+ def cancel_read_body(self):
+ self._fake_client.expecting_body = False
class FakeClient(_SmartClient):
@@ -118,13 +125,14 @@
def __init__(self, responses):
# We don't call the super init because there is no medium.
- """create a FakeClient.
+ """Create a FakeClient.
:param respones: A list of response-tuple, body-data pairs to be sent
back to callers.
"""
self.responses = responses
self._calls = []
+ self.expecting_body = False
def call(self, method, *args):
self._calls.append(('call', method, args))
@@ -133,7 +141,8 @@
def call_expecting_body(self, method, *args):
self._calls.append(('call_expecting_body', method, args))
result = self.responses.pop(0)
- return result[0], FakeProtocol(result[1])
+ self.expecting_body = True
+ return result[0], FakeProtocol(result[1], self)
class TestBzrDirOpenBranch(tests.TestCase):
@@ -751,6 +760,49 @@
# try to copy...
remote_repo.sprout(dest_bzrdir)
+ def test_backwards_compatibility(self):
+ """If the server doesn't recognise this request, fallback to VFS.
+
+ This happens when a current client talks to an older server that
+ doesn't implement 'Repository.tarball'.
+ """
+ # Make a regular local repository to receive the results
+ dest_transport = MemoryTransport()
+ dest_transport.mkdir('destrepo')
+ bzrdir_format = bzrdir.format_registry.make_bzrdir('default')
+ dest_bzrdir = bzrdir_format.initialize_on_transport(dest_transport)
+
+ error_msg = (
+ "Generic bzr smart protocol error: "
+ "bad request 'Repository.tarball'")
+ responses = [(('error', error_msg), '')]
+ remote_repo, client = self.setup_fake_client_and_repository(
+ responses, 'path')
+ mock_real_repo = MockRealRepository()
+ remote_repo._real_repository = mock_real_repo
+
+ # try to copy...
+ remote_repo.sprout(dest_bzrdir)
+
+ self.assertEqual([('sprout', dest_bzrdir, None)], mock_real_repo.calls,
+ "RemoteRepository didn't fallback to the real repository correctly")
+ self.failIf(client.expecting_body,
+ "The protocol has been left in an unclean state that will cause "
+ "TooManyConcurrentRequests errors.")
+
+
+class MockRealRepository(object):
+ """Mock of a RemoteRepository's '_real_repository' attribute.
+
+ Used by TestRepositoryTarball.test_backwards_compatibility.
+ """
+
+ def __init__(self):
+ self.calls = []
+
+ def sprout(self, to_bzrdir, revision_id=None):
+ self.calls.append(('sprout', to_bzrdir, revision_id))
+
class TestRemoteRepositoryCopyContent(tests.TestCaseWithTransport):
"""RemoteRepository.copy_content_into optimizations"""
More information about the bazaar-commits
mailing list