summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarja Shakhray <dshakhray@mirantis.com>2015-07-02 16:12:50 +0300
committerMike Fedosin <mfedosin@mirantis.com>2015-08-26 20:02:25 +0300
commit2506953b7fdd9ee899cd0589b8995ae6f4db30ca (patch)
tree36fe16d63fc67cfad94f61e1bdee8ab8ad4ba40e
parentc71a87241e15d819b08dd182b7cc295bf6b3ab62 (diff)
downloadpython-glanceclient-2506953b7fdd9ee899cd0589b8995ae6f4db30ca.tar.gz
Glance v3 client methods for work with blobs
This commit adds methods for work with BLOB and related tests to the client. Currently implemented methods: 'delete_blob', 'download_blob', 'upload_blob' Co-Authored-By: Mike Fedosin <mfedosin@mirantis.com> FastTrack Implements-blueprint: artifact-repository Change-Id: I29357ee117d7dbdaed58ab12ccb10ba575820b2a
-rw-r--r--glanceclient/tests/unit/v3/test_artifacts.py179
-rw-r--r--glanceclient/v3/artifacts.py60
2 files changed, 235 insertions, 4 deletions
diff --git a/glanceclient/tests/unit/v3/test_artifacts.py b/glanceclient/tests/unit/v3/test_artifacts.py
index 91597e0..d40528f 100644
--- a/glanceclient/tests/unit/v3/test_artifacts.py
+++ b/glanceclient/tests/unit/v3/test_artifacts.py
@@ -11,7 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-
+import errno
import testtools
from glanceclient import exc
@@ -124,6 +124,63 @@ data_fixtures = {
finn='human', **type_fixture)
),
},
+ '/v3/artifacts/adventure_time/v1.0.2/'
+ '18f67e12-88e0-4b13-86fb-5adc36d884b6/image_file': {
+ 'PUT': (
+ {},
+ '',
+ ),
+
+ 'DELETE': (
+ {},
+ '',
+ ),
+ },
+ '/v3/artifacts/adventure_time/v1.0.2/'
+ '18f67e12-88e0-4b13-86fb-5adc36d884b6/image_file/download': {
+ 'GET': (
+ {},
+ 'Princess Bubblegum rocks!!!',
+ ),
+ },
+ '/v3/artifacts/adventure_time/v1.0.2/'
+ '5cc4bebc-db27-11e1-a1eb-080027cbe205/image_file/download': {
+ 'GET': (
+ {
+ 'content-md5': '5a3c872ee92e2c58efd0d47862eb9c85'
+ },
+ 'Princess Bubblegum rocks!!!',
+ ),
+ },
+ '/v3/artifacts/adventure_time/v1.0.2/'
+ '66fb18d6-db27-11e1-a1eb-080027cbe205/image_file/download': {
+ 'GET': (
+ {
+ 'content-md5': 'Lich was here!!!'
+ },
+ 'Princess Bubblegum rocks!!!',
+ ),
+ },
+ '/v3/artifacts/adventure_time/v1.0.2/'
+ '18f67e12-88e0-4b13-86fb-5adc36d884b6/screenshots/1': {
+ 'PUT': (
+ {},
+ '',
+ ),
+
+ 'DELETE': (
+ {},
+ '',
+ ),
+ },
+ '/v3/artifacts/adventure_time/v1.0.2/'
+ '18f67e12-88e0-4b13-86fb-5adc36d884b6/screenshots/1/download': {
+ 'GET': (
+ {},
+ 'What time is it?',
+ ),
+ },
+
}
@@ -300,3 +357,123 @@ class TestController(testtools.TestCase):
self.assertEqual(artifact_id, artifact.id)
self.assertEqual('Gunter The Penguin', artifact.name)
self.assertEqual('human', artifact.type_specific_properties['finn'])
+
+ def test_upload_blob(self):
+ image_data = 'Adventure Time with Finn & Jake'
+
+ artifact_id = '18f67e12-88e0-4b13-86fb-5adc36d884b6'
+
+ params = {'blob_property': 'image_file', 'artifact_id': artifact_id,
+ 'data': image_data}
+ params.update(type_fixture)
+
+ self.controller.upload_blob(**params)
+ expect_hdrs = {'Content-Type': 'application/octet-stream'}
+ expect = [('PUT', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/image_file' % artifact_id, expect_hdrs, image_data)]
+
+ self.assertEqual(expect, self.api.calls)
+
+ def test_delete_blob(self):
+ artifact_id = '18f67e12-88e0-4b13-86fb-5adc36d884b6'
+
+ params = {'blob_property': 'image_file', 'artifact_id': artifact_id}
+ params.update(type_fixture)
+ self.controller.delete_blob(**params)
+ expect = [('DELETE', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/image_file' % artifact_id, {}, None)]
+
+ self.assertEqual(expect, self.api.calls)
+
+ def test_download_blob(self):
+ artifact_id = '18f67e12-88e0-4b13-86fb-5adc36d884b6'
+
+ params = {'blob_property': 'image_file', 'artifact_id': artifact_id}
+ params.update(type_fixture)
+ body = ''.join([b for b in self.controller.download_blob(**params)])
+
+ expect = [('GET', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/image_file/download' % artifact_id, {}, None)]
+
+ self.assertEqual(expect, self.api.calls)
+ self.assertEqual('Princess Bubblegum rocks!!!', body)
+
+ def test_download_blob_with_checksum(self):
+ artifact_id = '5cc4bebc-db27-11e1-a1eb-080027cbe205'
+ params = {'blob_property': 'image_file', 'artifact_id': artifact_id}
+ params.update(type_fixture)
+ body = ''.join([b for b in self.controller.download_blob(**params)])
+
+ self.assertEqual('Princess Bubblegum rocks!!!', body)
+
+ params['do_checksum'] = False
+ body = ''.join([b for b in self.controller.download_blob(**params)])
+
+ expect = [('GET', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/image_file/download' % artifact_id, {}, None)] * 2
+
+ self.assertEqual(expect, self.api.calls)
+ self.assertEqual('Princess Bubblegum rocks!!!', body)
+
+ def test_download_blob_with_wrong_checksum(self):
+ artifact_id = '66fb18d6-db27-11e1-a1eb-080027cbe205'
+
+ params = {'blob_property': 'image_file', 'artifact_id': artifact_id}
+ params.update(type_fixture)
+ try:
+ ''.join([b for b in self.controller.download_blob(**params)])
+ self.fail('data did not raise an error.')
+ except IOError as e:
+ self.assertEqual(errno.EPIPE, e.errno)
+ msg = 'was 5a3c872ee92e2c58efd0d47862eb9c85 expected Lich'
+ self.assertIn(msg, str(e))
+
+ params['do_checksum'] = False
+ body = ''.join([b for b in self.controller.download_blob(**params)])
+
+ expect = [('GET', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/image_file/download' % artifact_id, {}, None)] * 2
+
+ self.assertEqual(expect, self.api.calls)
+ self.assertEqual('Princess Bubblegum rocks!!!', body)
+
+ def test_data_upload_blob_with_position(self):
+ image_data = 'Adventure Time with Finn & Jake'
+
+ artifact_id = '18f67e12-88e0-4b13-86fb-5adc36d884b6'
+
+ params = {'blob_property': 'screenshots', 'artifact_id': artifact_id,
+ 'position': 1, 'data': image_data}
+ params.update(type_fixture)
+
+ self.controller.upload_blob(**params)
+ expect_hdrs = {'Content-Type': 'application/octet-stream'}
+ expect = [('PUT', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/screenshots/1' % artifact_id, expect_hdrs, image_data)]
+
+ self.assertEqual(expect, self.api.calls)
+
+ def test_delete_blob_with_position(self):
+ artifact_id = '18f67e12-88e0-4b13-86fb-5adc36d884b6'
+
+ params = {'blob_property': 'screenshots', 'artifact_id': artifact_id,
+ 'position': 1}
+ params.update(type_fixture)
+ self.controller.delete_blob(**params)
+ expect = [('DELETE', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/screenshots/1' % artifact_id, {}, None)]
+
+ self.assertEqual(expect, self.api.calls)
+
+ def test_download_blob_with_position(self):
+ artifact_id = '18f67e12-88e0-4b13-86fb-5adc36d884b6'
+
+ params = {'blob_property': 'screenshots', 'artifact_id': artifact_id,
+ 'position': 1}
+ params.update(type_fixture)
+ body = ''.join([b for b in self.controller.download_blob(**params)])
+ expect = [('GET', '/v3/artifacts/adventure_time/v1.0.2/'
+ '%s/screenshots/1/download' % artifact_id, {}, None)]
+
+ self.assertEqual(expect, self.api.calls)
+ self.assertEqual('What time is it?', body)
diff --git a/glanceclient/v3/artifacts.py b/glanceclient/v3/artifacts.py
index 2440a36..971be98 100644
--- a/glanceclient/v3/artifacts.py
+++ b/glanceclient/v3/artifacts.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from glanceclient.common import utils
from glanceclient import exc
from glanceclient.v3 import ArtifactType
@@ -140,15 +141,68 @@ class Controller(object):
def upload_blob(self, artifact_id, blob_property, data, position=None,
type_name=None, type_version=None):
- raise NotImplementedError()
+ """Upload blob data.
+
+ :param artifact_id: ID of the artifact to download a blob
+ :param blob_property: blob property name
+ :param position: if blob_property is a list then the
+ position must be specified
+ """
+ type_name, type_version = self._check_type_params(type_name,
+ type_version)
+ hdrs = {'Content-Type': 'application/octet-stream'}
+
+ url = '/v3/artifacts/%s/v%s/%s/%s' % (type_name, type_version,
+ artifact_id, blob_property)
+ if position:
+ url += "/%s" % position
+
+ self.http_client.put(url, headers=hdrs, data=data)
def download_blob(self, artifact_id, blob_property, position=None,
type_name=None, type_version=None, do_checksum=True):
- raise NotImplementedError()
+ """Get blob data.
+
+ :param artifact_id: ID of the artifact to download a blob
+ :param blob_property: blob property name
+ :param position: if blob_property is a list then the
+ position must be specified
+ :param do_checksum: Enable/disable checksum validation.
+ """
+ type_name, type_version = self._check_type_params(type_name,
+ type_version)
+ url = '/v3/artifacts/%s/v%s/%s/%s' % (type_name, type_version,
+ artifact_id, blob_property)
+ if position:
+ url += '/%s' % position
+
+ url += '/download'
+
+ resp, body = self.http_client.get(url)
+ checksum = resp.headers.get('content-md5', None)
+ content_length = int(resp.headers.get('content-length', 0))
+
+ if checksum is not None and do_checksum:
+ body = utils.integrity_iter(body, checksum)
+
+ return utils.IterableWithLength(body, content_length)
def delete_blob(self, artifact_id, blob_property, position=None,
type_name=None, type_version=None):
- raise NotImplementedError()
+ """Delete blob and related data.
+
+ :param artifact_id: ID of the artifact to delete a blob
+ :param blob_property: blob property name
+ :param position: if blob_property is a list then the
+ position must be specified
+ """
+ type_name, type_version = self._check_type_params(type_name,
+ type_version)
+ url = '/v3/artifacts/%s/v%s/%s/%s' % (type_name, type_version,
+ artifact_id, blob_property)
+ if position:
+ url += '/%s' % position
+ self.http_client.delete(url)
def add_property(self, artifact_id, dependency_id, position=None,
type_name=None, type_version=None):