From 2690d6f18333c4b4eee21b296e7ffbde614ee89f Mon Sep 17 00:00:00 2001 From: Abhijeet Malawade Date: Tue, 16 Dec 2014 03:00:41 -0800 Subject: [PATCH] Fix copy-from when user_storage_quota is enabled If we enable 'user_storage_quota' and try to upload image then it throws 'AttributeError' error. If 'user_storage_quota' parameter is enabled then we need to send image_data as LimitingReader class object to 'store_add_to_backend' method. But currently we are getting image_data as CooperativeReader class object every time. Change-Id: I301d4007c9a4bea8836ee98a9e9685de2104a28e Closes-Bug: 1398903 --- glance/api/v1/upload_utils.py | 8 +++- glance/tests/unit/v1/test_upload_utils.py | 52 +++++++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/glance/api/v1/upload_utils.py b/glance/api/v1/upload_utils.py index 80370937d3..5671cf20b6 100644 --- a/glance/api/v1/upload_utils.py +++ b/glance/api/v1/upload_utils.py @@ -91,6 +91,12 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier): image_size = image_meta.get('size') try: + # By default image_data will be passed as CooperativeReader object. + # But if 'user_storage_quota' is enabled and 'remaining' is not None + # then it will be passed as object of LimitingReader to + # 'store_add_to_backend' method. + image_data = utils.CooperativeReader(image_data) + remaining = glance.api.common.check_quota( req.context, image_size, db_api, image_id=image_id) if remaining is not None: @@ -101,7 +107,7 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier): checksum, location_metadata) = store_api.store_add_to_backend( image_meta['id'], - utils.CooperativeReader(image_data), + image_data, image_meta['size'], store, context=req.context) diff --git a/glance/tests/unit/v1/test_upload_utils.py b/glance/tests/unit/v1/test_upload_utils.py index 85e1229394..1afaf001a2 100644 --- a/glance/tests/unit/v1/test_upload_utils.py +++ b/glance/tests/unit/v1/test_upload_utils.py @@ -23,6 +23,7 @@ import webob.exc from glance.api.v1 import upload_utils from glance.common import exception from glance.common import store_utils +from glance.common import utils import glance.registry.client.v1.api as registry from glance.tests.unit import base import glance.tests.unit.utils as unit_test_utils @@ -113,11 +114,19 @@ class TestUploadUtils(base.StoreClearingUnitTest): image_meta['size'], context=mock.ANY) def test_upload_data_to_store(self): + # 'user_storage_quota' is not set + def store_add(image_id, data, size, **kwargs): + # Check if 'data' is instance of 'CooperativeReader' when + # 'user_storage_quota' is disabled. + self.assertIsInstance(data, utils.CooperativeReader) + return location, 10, "checksum", {} + req = unit_test_utils.get_fake_request() with self._get_store_and_notifier( - ext_update_data={'size': 10}) as (location, checksum, image_meta, - image_data, store, notifier, - update_data): + ext_update_data={'size': 10}, + exc_class=store_add) as (location, checksum, image_meta, + image_data, store, notifier, + update_data): ret = image_meta.update(update_data) with patch.object(registry, 'update_image_metadata', return_value=ret) as mock_update_image_metadata: @@ -130,6 +139,43 @@ class TestUploadUtils(base.StoreClearingUnitTest): req.context, image_meta['id'], update_data, from_state='saving') + def test_upload_data_to_store_user_storage_quota_enabled(self): + # Enable user_storage_quota + self.config(user_storage_quota='100B') + + def store_add(image_id, data, size, **kwargs): + # Check if 'data' is instance of 'LimitingReader' when + # 'user_storage_quota' is enabled. + self.assertIsInstance(data, utils.LimitingReader) + return location, 10, "checksum", {} + + req = unit_test_utils.get_fake_request() + with self._get_store_and_notifier( + ext_update_data={'size': 10}, + exc_class=store_add) as (location, checksum, image_meta, + image_data, store, notifier, + update_data): + ret = image_meta.update(update_data) + # mock 'check_quota' + mock_check_quota = patch('glance.api.common.check_quota', + return_value=100) + mock_check_quota.start() + self.addCleanup(mock_check_quota.stop) + with patch.object(registry, 'update_image_metadata', + return_value=ret) as mock_update_image_metadata: + actual_meta, location_data = upload_utils.upload_data_to_store( + req, image_meta, image_data, store, notifier) + + self.assertEqual(location, location_data['url']) + self.assertEqual(image_meta.update(update_data), actual_meta) + mock_update_image_metadata.assert_called_once_with( + req.context, image_meta['id'], update_data, + from_state='saving') + # 'check_quota' is called two times + check_quota_call_count =\ + mock_check_quota.target.check_quota.call_count + self.assertEqual(2, check_quota_call_count) + def test_upload_data_to_store_mismatch_size(self): req = unit_test_utils.get_fake_request()