diff --git a/glance/store/swift.py b/glance/store/swift.py index 3c6c276eae..cbb564a66f 100644 --- a/glance/store/swift.py +++ b/glance/store/swift.py @@ -22,6 +22,7 @@ from __future__ import absolute_import import hashlib import httplib import math +import sys import urllib import urlparse @@ -309,6 +310,7 @@ class BaseStore(glance.store.base.Store): total_chunks = '?' checksum = hashlib.md5() + written_chunks = [] combined_chunks_size = 0 while True: chunk_size = self.large_object_chunk_size @@ -324,9 +326,27 @@ class BaseStore(glance.store.base.Store): chunk_name = "%s-%05d" % (location.obj, chunk_id) reader = ChunkReader(image_file, checksum, chunk_size) - chunk_etag = connection.put_object( - location.container, chunk_name, reader, - content_length=content_length) + try: + chunk_etag = connection.put_object( + location.container, chunk_name, reader, + content_length=content_length) + written_chunks.append(chunk_name) + except Exception: + # Save original traceback + exc_type, exc_val, exc_tb = sys.exc_info() + + # Delete now stale segments from swift backend + for chunk in written_chunks: + LOG.debug(_("Deleting chunk %s" % chunk)) + try: + connection.delete_object(location.container, + chunk) + except Exception: + msg = _("Failed to delete orphan chunk %s/%s") + LOG.exception(msg, location.container, chunk) + + # reraise original exception with traceback intact + raise exc_type, exc_val, exc_tb bytes_read = reader.bytes_read msg = _("Wrote chunk %(chunk_name)s (%(chunk_id)d/" "%(total_chunks)s) of length %(bytes_read)d " diff --git a/glance/tests/functional/store/test_swift.py b/glance/tests/functional/store/test_swift.py index 22a523bc0f..923c94e810 100644 --- a/glance/tests/functional/store/test_swift.py +++ b/glance/tests/functional/store/test_swift.py @@ -33,6 +33,8 @@ import urlparse import oslo.config.cfg import testtools +from glance.common import exception +import glance.common.utils as common_utils from glance.openstack.common import uuidutils import glance.store.swift import glance.tests.functional.store as store_tests @@ -283,6 +285,23 @@ class TestSwiftStore(store_tests.BaseTestCase, testtools.TestCase): self.swift_client.delete_object(manifest_container, non_image_obj) + # Simulate exceeding 'image_size_cap' setting + image_data = StringIO.StringIO('X' * image_size) + image_data = common_utils.LimitingReader(image_data, image_size - 1) + image_id = uuidutils.generate_uuid() + self.assertRaises(exception.ImageSizeLimitExceeded, + store.add, + image_id, + image_data, + image_size) + + # Verify written segments have been deleted + container = swift_get_container(self.swift_client, + manifest_container, + prefix=image_id) + segments = [segment['name'] for segment in container[1]] + self.assertEqual(0, len(segments), 'Got segments %s' % segments) + def stash_image(self, image_id, image_data): container_name = self.swift_config['swift_store_container'] swift_put_object(self.swift_client,