diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-05-28 09:59:58 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-05-28 09:59:58 +0000 |
| commit | d7e613dabc2dbc28d9405a5b450dc2b4dfa9d47b (patch) | |
| tree | 8cdd40cd47da365242aef96d5ff97f192a6723e8 | |
| parent | 0add75a7ff674cd650a962bfbc1f24121313bfde (diff) | |
| parent | 7b89917ece552b6b3dfe92ecc878531d9bfd9125 (diff) | |
| download | nova-d7e613dabc2dbc28d9405a5b450dc2b4dfa9d47b.tar.gz nova-d7e613dabc2dbc28d9405a5b450dc2b4dfa9d47b.tar.xz nova-d7e613dabc2dbc28d9405a5b450dc2b4dfa9d47b.zip | |
Merge "_s3_create update only pertinent metadata"
| -rw-r--r-- | nova/image/fake.py | 18 | ||||
| -rw-r--r-- | nova/image/glance.py | 5 | ||||
| -rw-r--r-- | nova/image/s3.py | 47 | ||||
| -rw-r--r-- | nova/tests/glance/stubs.py | 2 | ||||
| -rw-r--r-- | nova/tests/image/test_s3.py | 59 |
5 files changed, 105 insertions, 26 deletions
diff --git a/nova/image/fake.py b/nova/image/fake.py index 4f29cdb6e..41a68269a 100644 --- a/nova/image/fake.py +++ b/nova/image/fake.py @@ -210,7 +210,8 @@ class _FakeImageService(object): self._imagedata[image_id] = data.read() return self.images[image_id] - def update(self, context, image_id, metadata, data=None): + def update(self, context, image_id, metadata, data=None, + headers=None): """Replace the contents of the given image with the new data. :raises: ImageNotFound if the image does not exist. @@ -218,7 +219,20 @@ class _FakeImageService(object): """ if not self.images.get(image_id): raise exception.ImageNotFound(image_id=image_id) - self.images[image_id] = copy.deepcopy(metadata) + try: + purge = headers['x-glance-registry-purge-props'] + except Exception: + purge = True + if purge: + self.images[image_id] = copy.deepcopy(metadata) + else: + image = self.images[image_id] + try: + image['properties'].update(metadata.pop('properties')) + except Exception: + pass + image.update(metadata) + return self.images[image_id] def delete(self, context, image_id): """Delete the given image. diff --git a/nova/image/glance.py b/nova/image/glance.py index 1e98cbe11..dc7ae89ad 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -290,7 +290,7 @@ class GlanceImageService(object): base_image_meta) return base_image_meta - def update(self, context, image_id, image_meta, data=None): + def update(self, context, image_id, image_meta, data=None, features=None): """Replace the contents of the given image with the new data. :raises: ImageNotFound if the image does not exist. @@ -301,7 +301,8 @@ class GlanceImageService(object): image_meta = self._translate_to_glance(image_meta) client = self._get_client(context) try: - image_meta = client.update_image(image_id, image_meta, data) + image_meta = client.update_image(image_id, image_meta, data, + features) except Exception: _reraise_translated_image_exception(image_id) diff --git a/nova/image/s3.py b/nova/image/s3.py index 9ed060464..d0746922f 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -288,8 +288,20 @@ class S3ImageService(object): context.update_store() log_vars = {'image_location': image_location, 'image_path': image_path} - metadata['properties']['image_state'] = 'downloading' - self.service.update(context, image_uuid, metadata) + + def _update_image_state(context, image_uuid, image_state): + metadata = {'properties': {'image_state': image_state}} + headers = {'x-glance-registry-purge-props': False} + self.service.update(context, image_uuid, metadata, None, + headers) + + def _update_image_data(context, image_uuid, image_data): + metadata = {} + headers = {'x-glance-registry-purge-props': False} + self.service.update(context, image_uuid, metadata, image_data, + headers) + + _update_image_state(context, image_uuid, 'downloading') try: parts = [] @@ -310,12 +322,10 @@ class S3ImageService(object): except Exception: LOG.exception(_("Failed to download %(image_location)s " "to %(image_path)s"), log_vars) - metadata['properties']['image_state'] = 'failed_download' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'failed_download') return - metadata['properties']['image_state'] = 'decrypting' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'decrypting') try: hex_key = manifest.find('image/ec2_encrypted_key').text @@ -329,38 +339,33 @@ class S3ImageService(object): except Exception: LOG.exception(_("Failed to decrypt %(image_location)s " "to %(image_path)s"), log_vars) - metadata['properties']['image_state'] = 'failed_decrypt' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'failed_decrypt') return - metadata['properties']['image_state'] = 'untarring' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'untarring') try: unz_filename = self._untarzip_image(image_path, dec_filename) except Exception: LOG.exception(_("Failed to untar %(image_location)s " "to %(image_path)s"), log_vars) - metadata['properties']['image_state'] = 'failed_untar' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'failed_untar') return - metadata['properties']['image_state'] = 'uploading' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'uploading') try: with open(unz_filename) as image_file: - self.service.update(context, image_uuid, - metadata, image_file) + _update_image_data(context, image_uuid, image_file) except Exception: LOG.exception(_("Failed to upload %(image_location)s " "to %(image_path)s"), log_vars) - metadata['properties']['image_state'] = 'failed_upload' - self.service.update(context, image_uuid, metadata) + _update_image_state(context, image_uuid, 'failed_upload') return - metadata['properties']['image_state'] = 'available' - metadata['status'] = 'active' - self.service.update(context, image_uuid, metadata) + metadata = {'status': 'active', + 'properties': {'image_state': 'available'}} + headers = {'x-glance-registry-purge-props': False} + self.service.update(context, image_uuid, metadata, None, headers) shutil.rmtree(image_path) diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index ccf431b69..e3bdf9dab 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -140,7 +140,7 @@ class StubGlanceClient(object): return self.images[-1] - def update_image(self, image_id, metadata, data): + def update_image(self, image_id, metadata, data, features): for i, image in enumerate(self.images): if image['id'] == str(image_id): if 'id' in metadata: diff --git a/nova/tests/image/test_s3.py b/nova/tests/image/test_s3.py index 124666df9..9f078fc49 100644 --- a/nova/tests/image/test_s3.py +++ b/nova/tests/image/test_s3.py @@ -15,7 +15,11 @@ # License for the specific language governing permissions and limitations # under the License. +import binascii +import eventlet +import mox import os +import tempfile from nova import context import nova.db.api @@ -58,6 +62,21 @@ ami_manifest_xml = """<?xml version="1.0" ?> </manifest> """ +file_manifest_xml = """<?xml version="1.0" ?> +<manifest> + <image> + <ec2_encrypted_key>foo</ec2_encrypted_key> + <user_encrypted_key>foo</user_encrypted_key> + <ec2_encrypted_iv>foo</ec2_encrypted_iv> + <parts count="1"> + <part index="0"> + <filename>foo</filename> + </part> + </parts> + </image> +</manifest> +""" + class TestS3ImageService(test.TestCase): def setUp(self): @@ -133,6 +152,46 @@ class TestS3ImageService(test.TestCase): 'no_device': True}] self.assertEqual(block_device_mapping, expected_bdm) + def test_s3_create_is_public(self): + metadata = {'properties': { + 'image_location': 'mybucket/my.img.manifest.xml'}, + 'name': 'mybucket/my.img'} + handle, tempf = tempfile.mkstemp(dir='/tmp') + + ignore = mox.IgnoreArg() + mockobj = self.mox.CreateMockAnything() + self.stubs.Set(self.image_service, '_conn', mockobj) + mockobj(ignore).AndReturn(mockobj) + self.stubs.Set(mockobj, 'get_bucket', mockobj) + mockobj(ignore).AndReturn(mockobj) + self.stubs.Set(mockobj, 'get_key', mockobj) + mockobj(ignore).AndReturn(mockobj) + self.stubs.Set(mockobj, 'get_contents_as_string', mockobj) + mockobj().AndReturn(file_manifest_xml) + self.stubs.Set(self.image_service, '_download_file', mockobj) + mockobj(ignore, ignore, ignore).AndReturn(tempf) + self.stubs.Set(binascii, 'a2b_hex', mockobj) + mockobj(ignore).AndReturn('foo') + mockobj(ignore).AndReturn('foo') + self.stubs.Set(self.image_service, '_decrypt_image', mockobj) + mockobj(ignore, ignore, ignore, ignore, ignore).AndReturn(mockobj) + self.stubs.Set(self.image_service, '_untarzip_image', mockobj) + mockobj(ignore, ignore).AndReturn(tempf) + self.mox.ReplayAll() + + img = self.image_service._s3_create(self.context, metadata) + eventlet.sleep() + translated = self.image_service._translate_id_to_uuid(context, img) + uuid = translated['id'] + self.glance_service = nova.image.get_default_image_service() + updated_image = self.glance_service.update(self.context, uuid, + {'is_public': True}, None, + {'x-glance-registry-purge-props': False}) + self.assertTrue(updated_image['is_public']) + self.assertEqual(updated_image['status'], 'active') + self.assertEqual(updated_image['properties']['image_state'], + 'available') + def test_s3_malicious_tarballs(self): self.assertRaises(exception.NovaException, self.image_service._test_for_malicious_tarball, |
