Permalink
Browse files

Merge "Expose 'protected' image attribute in v2 API"

  • Loading branch information...
Jenkins authored and openstack-gerrit committed Aug 13, 2012
2 parents 42ebabb + 3557b97 commit 963128349209539ff22ec4431d82f1322e7e3723
View
@@ -112,11 +112,14 @@ def index(self, req, marker=None, limit=None, sort_key='created_at',
for image in images]
return result
- def show(self, req, image_id):
+ def _get_image(self, context, image_id):
try:
- image = self.db_api.image_get(req.context, image_id)
+ return self.db_api.image_get(context, image_id)
except (exception.NotFound, exception.Forbidden):
raise webob.exc.HTTPNotFound()
+
+ def show(self, req, image_id):
+ image = self._get_image(req.context, image_id)
image = self._normalize_properties(dict(image))
return self._append_tags(req.context, image)
@@ -143,6 +146,12 @@ def update(self, req, image_id, image):
@utils.mutating
def delete(self, req, image_id):
+ image = self._get_image(req.context, image_id)
+
+ if image['protected']:
+ msg = _("Unable to delete as image is protected.")
+ raise webob.exc.HTTPForbidden(explanation=msg)
+
try:
self.db_api.image_destroy(req.context, image_id)
except (exception.NotFound, exception.Forbidden):
@@ -171,7 +180,7 @@ def _parse_image(self, request):
image = {'properties': body}
for key in ['checksum', 'created_at', 'container_format',
'disk_format', 'id', 'min_disk', 'min_ram', 'name', 'size',
- 'status', 'tags', 'updated_at', 'visibility']:
+ 'status', 'tags', 'updated_at', 'visibility', 'protected']:
try:
image[key] = image['properties'].pop(key)
except KeyError:
@@ -192,8 +201,7 @@ def _check_readonly(image):
@staticmethod
def _check_reserved(image):
- for key in ['owner', 'protected', 'is_public', 'location',
- 'deleted', 'deleted_at']:
+ for key in ['owner', 'is_public', 'location', 'deleted', 'deleted_at']:
if key in image:
msg = "Attribute \'%s\' is reserved." % key
raise webob.exc.HTTPForbidden(explanation=unicode(msg))
@@ -278,7 +286,7 @@ def _format_image(self, image):
# top-level image object
image_view = image['properties']
attributes = ['id', 'name', 'disk_format', 'container_format',
- 'size', 'status', 'checksum', 'tags',
+ 'size', 'status', 'checksum', 'tags', 'protected',
'created_at', 'updated_at', 'min_ram', 'min_disk']
for key in attributes:
image_view[key] = image[key]
@@ -362,6 +370,10 @@ def delete(self, response, result):
'description': 'Scope of image accessibility',
'enum': ['public', 'private'],
},
+ 'protected': {
+ 'type': 'boolean',
+ 'description': 'If true, image will not be deletable.',
+ },
'checksum': {
'type': 'string',
'description': 'md5 hash of image contents.',
@@ -88,14 +88,15 @@ def test_image_lifecycle(self):
self.assertFalse('checksum' in image)
self.assertFalse('size' in image)
self.assertEqual('bar', image['foo'])
+ self.assertEqual(False, image['protected'])
self.assertTrue(image['created_at'])
self.assertTrue(image['updated_at'])
self.assertEqual(image['updated_at'], image['created_at'])
# The image should be mutable, including adding new properties
path = self._url('/v2/images/%s' % image_id)
data = json.dumps({'name': 'image-2', 'format': 'vhd',
- 'foo': 'baz', 'ping': 'pong'})
+ 'foo': 'baz', 'ping': 'pong', 'protected': True})
response = requests.put(path, headers=self._headers(), data=data)
self.assertEqual(200, response.status_code)
@@ -105,6 +106,7 @@ def test_image_lifecycle(self):
self.assertEqual('vhd', image['format'])
self.assertEqual('baz', image['foo'])
self.assertEqual('pong', image['ping'])
+ self.assertEqual(True, image['protected'])
# Updates should persist across requests
path = self._url('/v2/images/%s' % image_id)
@@ -115,6 +117,7 @@ def test_image_lifecycle(self):
self.assertEqual('image-2', image['name'])
self.assertEqual('baz', image['foo'])
self.assertEqual('pong', image['ping'])
+ self.assertEqual(True, image['protected'])
# Try to download data before its uploaded
path = self._url('/v2/images/%s/file' % image_id)
@@ -157,6 +160,17 @@ def test_image_lifecycle(self):
self.assertEqual(200, response.status_code)
self.assertEqual(5, json.loads(response.text)['size'])
+ # Deletion should not work on protected images
+ path = self._url('/v2/images/%s' % image_id)
+ response = requests.delete(path, headers=self._headers())
+ self.assertEqual(403, response.status_code)
+
+ # Unprotect image for deletion
+ path = self._url('/v2/images/%s' % image_id)
+ data = json.dumps({'protected': False})
+ response = requests.put(path, headers=self._headers(), data=data)
+ self.assertEqual(200, response.status_code)
+
# Deletion should work
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
@@ -53,6 +53,7 @@ def test_resource(self):
'direct_url',
'min_ram',
'min_disk',
+ 'protected',
])
self.assertEqual(expected, set(image_schema['properties'].keys()))
@@ -357,6 +357,7 @@ def test_create_full(self):
'min_ram': 128,
'min_disk': 10,
'foo': 'bar',
+ 'protected': True,
})
output = self.deserializer.create(request)
expected = {'image': {
@@ -369,6 +370,7 @@ def test_create_full(self):
'min_ram': 128,
'min_disk': 10,
'properties': {'foo': 'bar'},
+ 'protected': True,
}}
self.assertEqual(expected, output)
@@ -403,6 +405,7 @@ def test_update(self):
'min_ram': 128,
'min_disk': 10,
'foo': 'bar',
+ 'protected': True,
})
output = self.deserializer.update(request)
expected = {'image': {
@@ -415,6 +418,7 @@ def test_update(self):
'min_ram': 128,
'min_disk': 10,
'properties': {'foo': 'bar'},
+ 'protected': True,
}}
self.assertEqual(expected, output)
@@ -680,6 +684,7 @@ def test_index(self):
'name': 'image-1',
'status': 'queued',
'visibility': 'public',
+ 'protected': False,
'tags': ['one', 'two'],
'size': 1024,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
@@ -697,6 +702,7 @@ def test_index(self):
'id': UUID2,
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'tags': [],
'created_at': ISOTIME,
'updated_at': ISOTIME,
@@ -741,6 +747,7 @@ def test_show_full_fixture(self):
'name': 'image-1',
'status': 'queued',
'visibility': 'public',
+ 'protected': False,
'tags': ['one', 'two'],
'size': 1024,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
@@ -764,6 +771,7 @@ def test_show_minimal_fixture(self):
'id': UUID2,
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'tags': [],
'created_at': ISOTIME,
'updated_at': ISOTIME,
@@ -781,6 +789,7 @@ def test_create(self):
'name': 'image-1',
'status': 'queued',
'visibility': 'public',
+ 'protected': False,
'tags': ['one', 'two'],
'size': 1024,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
@@ -806,6 +815,7 @@ def test_update(self):
'name': 'image-1',
'status': 'queued',
'visibility': 'public',
+ 'protected': False,
'tags': ['one', 'two'],
'size': 1024,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
@@ -851,6 +861,7 @@ def test_show(self):
'name': 'image-2',
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
'tags': [],
'size': 1024,
@@ -872,6 +883,7 @@ def test_show_reports_invalid_data(self):
'name': 'image-2',
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
'tags': [],
'size': 1024,
@@ -904,6 +916,7 @@ def test_show(self):
'name': 'image-2',
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
'marx': 'groucho',
'tags': [],
@@ -929,6 +942,7 @@ def test_show_invalid_additional_property(self):
'name': 'image-2',
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
'marx': 123,
'tags': [],
@@ -951,6 +965,7 @@ def test_show_with_additional_properties_disabled(self):
'name': 'image-2',
'status': 'queued',
'visibility': 'private',
+ 'protected': False,
'checksum': 'ca425b88f047ce8ec45ee90e813ada91',
'tags': [],
'size': 1024,
@@ -31,7 +31,7 @@ def test_image(self):
expected = set(['status', 'name', 'tags', 'checksum', 'created_at',
'disk_format', 'updated_at', 'visibility', 'self',
'file', 'container_format', 'schema', 'id', 'size',
- 'direct_url', 'min_ram', 'min_disk'])
+ 'direct_url', 'min_ram', 'min_disk', 'protected'])
self.assertEqual(set(output['properties'].keys()), expected)
def test_images(self):

0 comments on commit 9631283

Please sign in to comment.