[MERGE][0.91] Make RemoteRepository.sprout cope gracefully with servers that don't support the 'Repository.tarball' request.
Andrew Bennetts
andrew at canonical.com
Fri Sep 14 04:11:04 BST 2007
This fixes a problem we've had since we introduced the tarball hack for
initial branching via the smart protocol: if the server doesn't support the new
method, the client breaks rather than falling back to file operations (which are
slow but always available).
This is a regression (although one we've had for a few releases now), so I'm
proposing it for 0.91. The risk is quite low I think.
-Andrew.
-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: andrew.bennetts at canonical.com-20070914025541-\
# xymskacw08apuoq5
# target_branch: http://bazaar-vcs.org/bzr/bzr.0.91/
# testament_sha1: d16fc54c5b608a97fdc9cb0d5dfe4778f70e44aa
# timestamp: 2007-09-14 13:02:40 +1000
# source_branch: http://people.ubuntu.com/~andrew/bzr/tarball-back-\
# compat-for-0.91
# base_revision_id: pqm at pqm.ubuntu.com-20070912051907-80aaitfew8esgq23
#
# Begin patch
=== modified file 'bzrlib/remote.py'
--- bzrlib/remote.py 2007-09-11 08:03:10 +0000
+++ 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'
--- bzrlib/tests/test_remote.py 2007-07-19 06:34:09 +0000
+++ 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"""
# Begin bundle
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
More information about the bazaar
mailing list