summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJannis Pohlmann <jannis.pohlmann@codethink.co.uk>2012-04-19 16:49:55 +0100
committerJannis Pohlmann <jannis.pohlmann@codethink.co.uk>2012-04-19 16:51:15 +0100
commit9dcffcaa08dfc40fd49969719fa3b984802fafe5 (patch)
treefc266ceb7f7b3e853814da0ad35196ecf2df4da3
parentac0a1bb2491147951e000ce8d4d439b8a2fac7dd (diff)
downloadmorph-9dcffcaa08dfc40fd49969719fa3b984802fafe5.tar.gz
Add RemoteArtifactCache. Move metadata basename code into Artifact.
-rw-r--r--morphlib/__init__.py1
-rw-r--r--morphlib/artifact.py6
-rw-r--r--morphlib/localartifactcache.py3
-rw-r--r--morphlib/remoteartifactcache.py98
-rw-r--r--morphlib/remoteartifactcache_tests.py156
5 files changed, 262 insertions, 2 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py
index da08ac32..942ef741 100644
--- a/morphlib/__init__.py
+++ b/morphlib/__init__.py
@@ -40,6 +40,7 @@ import localrepocache
import morph2
import morphology
import morphologyloader
+import remoteartifactcache
import remoterepocache
import savefile
import source
diff --git a/morphlib/artifact.py b/morphlib/artifact.py
index 3a438747..cea2f2f3 100644
--- a/morphlib/artifact.py
+++ b/morphlib/artifact.py
@@ -53,5 +53,11 @@ class Artifact(object):
self.source.morphology['kind'],
self.name)
+ def metadata_basename(self, metadata_name): # pragma: no cover
+ return '%s.%s.%s.%s' % (self.cache_key,
+ self.source.morphology['kind'],
+ self.name,
+ metadata_name)
+
def __str__(self): # pragma: no cover
return '%s|%s' % (self.source, self.name)
diff --git a/morphlib/localartifactcache.py b/morphlib/localartifactcache.py
index cf025210..83a668e4 100644
--- a/morphlib/localartifactcache.py
+++ b/morphlib/localartifactcache.py
@@ -65,10 +65,9 @@ class LocalArtifactCache(object):
return os.path.join(self.cachedir, basename)
def _artifact_metadata_filename(self, artifact, name):
- basename = '%s.%s' % (artifact.basename(), name)
+ basename = artifact.metadata_basename(name)
return os.path.join(self.cachedir, basename)
-
def _source_metadata_filename(self, source, cachekey, name):
basename = '%s.%s' % (cachekey, name)
return os.path.join(self.cachedir, basename)
diff --git a/morphlib/remoteartifactcache.py b/morphlib/remoteartifactcache.py
new file mode 100644
index 00000000..6df91a4e
--- /dev/null
+++ b/morphlib/remoteartifactcache.py
@@ -0,0 +1,98 @@
+# Copyright (C) 2012 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+import cliapp
+import httplib2
+import urllib2
+import urlparse
+
+
+class GetError(cliapp.AppException):
+
+ def __init__(self, cache, artifact):
+ cliapp.AppException.__init__(
+ self, 'Failed to get the artifact %s from the '
+ 'artifact cache %s' % (artifact, cache))
+
+
+class GetArtifactMetadataError(cliapp.AppException):
+
+ def __init__(self, cache, artifact, name):
+ cliapp.AppException.__init__(
+ self, 'Failed to get metadata %s for the artifact %s '
+ 'from the artifact cache %s' % (name, artifact, cache))
+
+
+class GetSourceMetadataError(cliapp.AppException):
+
+ def __init__(self, cache, source, cache_key, name):
+ cliapp.AppException.__init__(
+ self, 'Failed to get metadata %s for source %s '
+ 'and cache key %s from the artifact cache %s' %
+ (name, source, cache_key, cache))
+
+
+class RemoteArtifactCache(object):
+
+ def __init__(self, server_url):
+ self.server_url = server_url
+
+ def has(self, artifact):
+ return self._has_file(artifact.basename())
+
+ def has_artifact_metadata(self, artifact, name):
+ return self._has_file(artifact.metadata_basename(name))
+
+ def has_source_metadata(self, source, cachekey, name):
+ filename = '%s.%s' % (cachekey, name)
+ return self._has_file(filename)
+
+ def get(self, artifact):
+ try:
+ return self._get_file(artifact.basename())
+ except:
+ raise GetError(self, artifact)
+
+ def get_artifact_metadata(self, artifact, name):
+ try:
+ return self._get_file(artifact.metadata_basename(name))
+ except:
+ raise GetArtifactMetadataError(self, artifact, name)
+
+ def get_source_metadata(self, source, cachekey, name):
+ filename = '%s.%s' % (cachekey, name)
+ try:
+ return self._get_file(filename)
+ except:
+ raise GetSourceMetadataError(self, source, cachekey, name)
+
+ def _has_file(self, filename): # pragma: no cover
+ url = self._request_url(filename)
+ http = httplib2.Http()
+ response = http.request(url, 'HEAD')
+ status = response[0]['status']
+ return status >= 200 and status < 400
+
+ def _get_file(self, filename): # pragma: no cover
+ url = self._request_url(filename)
+ return urllib2.urlopen(url)
+
+ def _request_url(self, filename): # pragma: no cover
+ server_url = self.server_url
+ if not server_url.endswith('/'):
+ server_url += '/'
+ return urlparse.urljoin(
+ server_url, '/1.0/artifacts/filename=%s' % filename)
diff --git a/morphlib/remoteartifactcache_tests.py b/morphlib/remoteartifactcache_tests.py
new file mode 100644
index 00000000..b7f10450
--- /dev/null
+++ b/morphlib/remoteartifactcache_tests.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2012 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+import StringIO
+import unittest
+
+import morphlib
+
+
+class RemoteArtifactCacheTests(unittest.TestCase):
+
+ def setUp(self):
+ morph = morphlib.morph2.Morphology(
+ '''
+ {
+ "chunk": "chunk",
+ "kind": "chunk",
+ "artifacts": {
+ "chunk-runtime": [
+ "usr/bin",
+ "usr/sbin",
+ "usr/lib",
+ "usr/libexec"
+ ],
+ "chunk-devel": [
+ "usr/include"
+ ],
+ "chunk-doc": [
+ "usr/share/doc"
+ ]
+ }
+ }
+ ''')
+ self.source = morphlib.source.Source(
+ 'repo', 'ref', 'sha1', morph, 'chunk.morph')
+ self.runtime_artifact = morphlib.artifact.Artifact(
+ self.source, 'chunk-runtime')
+ self.runtime_artifact.cache_key = 'CHUNK-RUNTIME'
+ self.devel_artifact = morphlib.artifact.Artifact(
+ self.source, 'chunk-devel')
+ self.devel_artifact.cache_key = 'CHUNK-DEVEL'
+ self.doc_artifact = morphlib.artifact.Artifact(
+ self.source, 'chunk-doc')
+ self.doc_artifact.cache_key = 'CHUNK-DOC'
+
+ self.existing_files = set([
+ self.runtime_artifact.basename(),
+ self.devel_artifact.basename(),
+ self.runtime_artifact.metadata_basename('meta'),
+ '%s.%s' % (self.runtime_artifact.cache_key, 'meta'),
+ ])
+
+ self.server_url = 'http://foo.bar:8080'
+ self.cache = morphlib.remoteartifactcache.RemoteArtifactCache(
+ self.server_url)
+ self.cache._has_file = self._has_file
+ self.cache._get_file = self._get_file
+
+ def _has_file(self, filename):
+ return filename in self.existing_files
+
+ def _get_file(self, filename):
+ if filename in self.existing_files:
+ return StringIO.StringIO('%s' % filename)
+ else:
+ raise Exception('foo')
+
+ def test_sets_server_url(self):
+ self.assertEqual(self.cache.server_url, self.server_url)
+
+ def test_has_existing_artifact(self):
+ self.assertTrue(self.cache.has(self.runtime_artifact))
+
+ def test_has_a_different_existing_artifact(self):
+ self.assertTrue(self.cache.has(self.devel_artifact))
+
+ def test_does_not_have_a_non_existent_artifact(self):
+ self.assertFalse(self.cache.has(self.doc_artifact))
+
+ def test_has_existing_artifact_metadata(self):
+ self.assertTrue(self.cache.has_artifact_metadata(
+ self.runtime_artifact, 'meta'))
+
+ def test_does_not_have_non_existent_artifact_metadata(self):
+ self.assertFalse(self.cache.has_artifact_metadata(
+ self.runtime_artifact, 'non-existent-meta'))
+
+ def test_has_existing_source_metadata(self):
+ self.assertTrue(self.cache.has_source_metadata(
+ self.runtime_artifact.source,
+ self.runtime_artifact.cache_key,
+ 'meta'))
+
+ def test_does_not_have_non_existent_source_metadata(self):
+ self.assertFalse(self.cache.has_source_metadata(
+ self.runtime_artifact.source,
+ self.runtime_artifact.cache_key,
+ 'non-existent-meta'))
+
+ def test_get_existing_artifact(self):
+ handle = self.cache.get(self.runtime_artifact)
+ data = handle.read()
+ self.assertEqual(data, self.runtime_artifact.basename())
+
+ def test_get_a_different_existing_artifact(self):
+ handle = self.cache.get(self.devel_artifact)
+ data = handle.read()
+ self.assertEqual(data, self.devel_artifact.basename())
+
+ def test_fails_to_get_a_non_existent_artifact(self):
+ self.assertRaises(morphlib.remoteartifactcache.GetError,
+ self.cache.get, self.doc_artifact)
+
+ def test_get_existing_artifact_metadata(self):
+ handle = self.cache.get_artifact_metadata(
+ self.runtime_artifact, 'meta')
+ data = handle.read()
+ self.assertEqual(
+ data, '%s.%s' % (self.runtime_artifact.basename(), 'meta'))
+
+ def test_fails_to_get_non_existent_artifact_metadata(self):
+ self.assertRaises(
+ morphlib.remoteartifactcache.GetArtifactMetadataError,
+ self.cache.get_artifact_metadata,
+ self.runtime_artifact,
+ 'non-existent-meta')
+
+ def test_get_existing_source_metadata(self):
+ handle = self.cache.get_source_metadata(
+ self.runtime_artifact.source,
+ self.runtime_artifact.cache_key,
+ 'meta')
+ data = handle.read()
+ self.assertEqual(
+ data, '%s.%s' % (self.runtime_artifact.cache_key, 'meta'))
+
+ def test_fails_to_get_non_existent_source_metadata(self):
+ self.assertRaises(
+ morphlib.remoteartifactcache.GetSourceMetadataError,
+ self.cache.get_source_metadata,
+ self.runtime_artifact.source,
+ self.runtime_artifact.cache_key,
+ 'non-existent-meta')