summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--morphlib/localrepocache.py49
-rw-r--r--morphlib/localrepocache_tests.py26
2 files changed, 72 insertions, 3 deletions
diff --git a/morphlib/localrepocache.py b/morphlib/localrepocache.py
index 673f9854..9beba4af 100644
--- a/morphlib/localrepocache.py
+++ b/morphlib/localrepocache.py
@@ -66,11 +66,17 @@ class LocalRepoCache(object):
is prepended. The base urls are given to the class when it is
created.
+ Instead of cloning via a normal 'git clone' directly from the
+ git server, we first try to download a bundle from a url, and
+ if that works, we clone from the bundle.
+
'''
- def __init__(self, cachedir, baseurls):
+ def __init__(self, cachedir, baseurls, bundle_base_url):
+ assert bundle_base_url.endswith('/')
self._cachedir = cachedir
self._baseurls = baseurls
+ self._bundle_base_url = bundle_base_url
self._ex = morphlib.execute.Execute(cachedir, logging.debug)
def _exists(self, filename): # pragma: no cover
@@ -93,9 +99,34 @@ class LocalRepoCache(object):
self._ex.runv(['git'] + args)
+ def _fetch(self, url, filename): # pragma: no cover
+ '''Fetch contents of url into a file.
+
+ This method is meant to be overridden by unit tests.
+
+ '''
+
+ source_handle = urllib2.urlopen(url)
+ target_handle = open(filename, 'wb')
+
+ data = source_handle.read(4096)
+ while data:
+ target_handle.write(data)
+ data = source_handle.read(4096)
+
+ source_handle.close()
+ target_handle.close()
+
def _escape(self, url):
'''Escape a URL so it can be used as a basename in a file.'''
- return urllib.quote(url, safe='')
+
+ # FIXME: The following is a nicer way than what source manager does.
+ # However, for compatibility, we need to use the same as the source
+ # manager uses, since that's what the bundle server (set up by
+ # Lorry) uses.
+ # return urllib.quote(url, safe='')
+
+ return morphlib.sourcemanager.quote_url(url)
def _cache_name(self, url):
basename = self._escape(url)
@@ -115,6 +146,16 @@ class LocalRepoCache(object):
return True
return False
+ def _clone_with_bundle(self, repourl, path):
+ escaped = self._escape(repourl)
+ bundle_url = urlparse.urljoin(self._bundle_base_url, escaped)
+ bundle_path = path + '.bundle'
+ if self._fetch(bundle_url, bundle_path):
+ self._git(['clone', bundle_path, path])
+ return True
+ else:
+ return False
+
def cache_repo(self, reponame):
'''Clone the given repo into the cache.
@@ -125,6 +166,10 @@ class LocalRepoCache(object):
for repourl, path in self._base_iterate(reponame):
if self._exists(path):
break
+
+ if self._clone_with_bundle(repourl, path):
+ break
+
try:
self._git(['clone', reponame, path])
except morphlib.execute.CommandFailure:
diff --git a/morphlib/localrepocache_tests.py b/morphlib/localrepocache_tests.py
index 13a1c415..add0aa20 100644
--- a/morphlib/localrepocache_tests.py
+++ b/morphlib/localrepocache_tests.py
@@ -23,15 +23,22 @@ class LocalRepoCacheTests(unittest.TestCase):
def setUp(self):
baseurls = ['git://example.com/']
+ bundle_base_url = 'http://lorry.example.com/bundles/'
self.reponame = 'reponame'
self.repourl = 'git://example.com/reponame'
+ escaped_url = 'git___example_com_reponame'
+ self.bundle_url = '%s%s' % (bundle_base_url, escaped_url)
self.cachedir = '/cache/dir'
+ self.cache_path = '%s/%s' % (self.cachedir, escaped_url)
self.cache = set()
self.remotes = []
+ self.fetched = []
self.lrc = morphlib.localrepocache.LocalRepoCache(self.cachedir,
- baseurls)
+ baseurls,
+ bundle_base_url)
self.lrc._git = self.fake_git
self.lrc._exists = self.fake_exists
+ self.lrc._fetch = self.not_found
def fake_git(self, args):
if args[0] == 'clone':
@@ -48,6 +55,13 @@ class LocalRepoCacheTests(unittest.TestCase):
def fake_exists(self, filename):
return filename in self.cache
+ def not_found(self, url, path):
+ return False
+
+ def fake_fetch(self, url, path):
+ self.fetched.append(url)
+ return True
+
def test_has_not_got_relative_repo_initially(self):
self.assertFalse(self.lrc.has_repo(self.reponame))
@@ -75,6 +89,16 @@ class LocalRepoCacheTests(unittest.TestCase):
self.assertRaises(morphlib.localrepocache.NoRemote,
self.lrc.cache_repo, self.repourl)
+ def test_does_not_mind_a_missing_bundle(self):
+ self.lrc.cache_repo(self.repourl)
+ self.assertEqual(self.fetched, [])
+
+ def test_fetches_bundle_when_it_exists(self):
+ self.lrc._fetch = self.fake_fetch
+ self.lrc.cache_repo(self.repourl)
+ self.assertEqual(self.fetched, [self.bundle_url])
+ self.assertEqual(self.remotes, [self.cache_path + '.bundle'])
+
def test_gets_cached_relative_repo(self):
self.lrc.cache_repo(self.reponame)
cached = self.lrc.get_repo(self.reponame)