Cleanup chunks for deleted image if token expired

In patch I47229b366c25367ec1bd48aec684e0880f3dfe60 it was
introduced the logic that if image was deleted during file
upload when we want to update image status from 'saving'
to 'active' it's expected to get Duplicate error and delete
stale chunks after that. But if user's token is expired
there will be Unathorized exception and chunks will stay
in store and clog it.
And when, the upload operation for such an image is
completed the operator configured quota can be exceeded.

This patch fixes the issue of left over chunks for an image
which was deleted from saving status, by correctly handle
auth exceptions from registry server.

Partial-bug: #1498163

Change-Id: I17a66eca55bfb83107046910e69c4da01415deec
This commit is contained in:
Mike Fedosin 2015-09-20 17:01:22 +03:00 committed by Nikhil Komawar
parent 60f2986f5c
commit 301bf5108a
4 changed files with 63 additions and 1 deletions

View File

@ -171,6 +171,13 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier):
raise exception.ImageNotFound()
else:
raise
except exception.NotAuthenticated as e:
# Delete image data due to possible token expiration.
LOG.debug("Authentication error - the token may have "
"expired during file upload. Deleting image data for "
" %s " % image_id)
initiate_deletion(req, location_data, image_id)
raise webob.exc.HTTPUnauthorized(explanation=e.msg, request=req)
except exception.ImageNotFound:
msg = _LI("Image %s could not be found after upload. The image may"
" have been deleted during the upload.") % image_id

View File

@ -105,7 +105,19 @@ class ImageDataController(object):
raise webob.exc.HTTPGone(explanation=msg,
request=req,
content_type='text/plain')
except exception.NotAuthenticated:
msg = (_("Authentication error - the token may have "
"expired during file upload. Deleting image data for "
"%s.") % image_id)
LOG.debug(msg)
try:
image.delete()
except exception.NotAuthenticated:
# NOTE: Ignore this exception
pass
raise webob.exc.HTTPUnauthorized(explanation=msg,
request=req,
content_type='text/plain')
except ValueError as e:
LOG.debug("Cannot save data for image %(id)s: %(e)s",
{'id': image_id,

View File

@ -323,3 +323,29 @@ class TestUploadUtils(base.StoreClearingUnitTest):
'metadata': {}}, image_meta['id'])
mock_safe_kill.assert_called_once_with(
req, image_meta['id'], 'saving')
@mock.patch.object(registry, 'update_image_metadata',
side_effect=exception.NotAuthenticated)
@mock.patch.object(upload_utils, 'initiate_deletion')
def test_activate_image_with_expired_token(
self, mocked_delete, mocked_update):
"""Test token expiration during image upload.
If users token expired before image was uploaded then if auth error
was caught from registry during changing image status from 'saving'
to 'active' then it's required to delete all image data.
"""
context = mock.Mock()
req = mock.Mock()
req.context = context
with self._get_store_and_notifier() as (location, checksum, image_meta,
image_data, store, notifier,
update_data):
self.assertRaises(webob.exc.HTTPUnauthorized,
upload_utils.upload_data_to_store,
req, image_meta, image_data, store, notifier)
self.assertEqual(2, mocked_update.call_count)
mocked_delete.assert_called_once_with(
req,
{'url': 'file://foo/bar', 'status': 'active', 'metadata': {}},
'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d')

View File

@ -192,6 +192,23 @@ class TestImagesController(base.StoreClearingUnitTest):
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.upload,
request, unit_test_utils.UUID1, 'YYYY', 4)
def test_upload_with_expired_token(self):
def side_effect(image, from_state=None):
if from_state == 'saving':
raise exception.NotAuthenticated()
mocked_save = mock.Mock(side_effect=side_effect)
mocked_delete = mock.Mock()
request = unit_test_utils.get_fake_request()
image = FakeImage('abcd')
image.delete = mocked_delete
self.image_repo.result = image
self.image_repo.save = mocked_save
self.assertRaises(webob.exc.HTTPUnauthorized, self.controller.upload,
request, unit_test_utils.UUID1, 'YYYY', 4)
self.assertEqual(3, mocked_save.call_count)
mocked_delete.assert_called_once_with()
def test_upload_non_existent_image_during_save_initiates_deletion(self):
def fake_save_not_found(self):
raise exception.ImageNotFound()