Merge "Fix: Interrupted copy-image leaking data on subsequent operation"
This commit is contained in:
commit
b540281ced
@ -59,7 +59,26 @@ class _CopyImage(task.Task):
|
||||
self.image_id)
|
||||
|
||||
if os.path.exists(file_path):
|
||||
return file_path, 0
|
||||
# NOTE (abhishekk): If previous copy-image operation is failed
|
||||
# due to power failure, network failure or any other reason and
|
||||
# the image data here is partial then clear the staging area and
|
||||
# re-stage the fresh image data.
|
||||
# Ref: https://bugs.launchpad.net/glance/+bug/1885003
|
||||
size_in_staging = os.path.getsize(file_path)
|
||||
if image.size == size_in_staging:
|
||||
return file_path, 0
|
||||
else:
|
||||
LOG.debug(("Found partial image data in staging "
|
||||
"%(fn)s, deleting it to re-stage "
|
||||
"again"), {'fn': file_path})
|
||||
try:
|
||||
os.unlink(file_path)
|
||||
except OSError as e:
|
||||
LOG.error(_LE("Deletion of staged "
|
||||
"image data from %(fn)s has failed because "
|
||||
"[Errno %(en)d]"), {'fn': file_path,
|
||||
'en': e.errno})
|
||||
raise
|
||||
|
||||
# At first search image in default_backend
|
||||
default_store = CONF.glance_store.default_backend
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
import glance_store as store_api
|
||||
@ -139,6 +140,67 @@ class TestCopyImageTask(test_utils.BaseTestCase):
|
||||
mock_store_api.assert_called_once_with(
|
||||
"os_glance_staging_store")
|
||||
|
||||
@mock.patch.object(os, 'unlink')
|
||||
@mock.patch.object(os.path, 'getsize')
|
||||
@mock.patch.object(os.path, 'exists')
|
||||
@mock.patch.object(store_api, 'get_store_from_store_identifier')
|
||||
def test_copy_image_to_staging_store_partial_data_exists(
|
||||
self, mock_store_api, mock_exists, mock_getsize, mock_unlink):
|
||||
mock_store_api.return_value = self.staging_store
|
||||
mock_exists.return_value = True
|
||||
mock_getsize.return_value = 3
|
||||
|
||||
copy_image_task = copy_image._CopyImage(
|
||||
self.task.task_id, self.task_type, self.image_repo,
|
||||
self.image_id)
|
||||
with mock.patch.object(self.image_repo, 'get') as get_mock:
|
||||
get_mock.return_value = mock.MagicMock(
|
||||
image_id=self.images[0]['id'],
|
||||
locations=self.images[0]['locations'],
|
||||
status=self.images[0]['status'],
|
||||
size=4
|
||||
)
|
||||
with mock.patch.object(store_api, 'get') as get_data:
|
||||
get_data.return_value = (b"dddd", 4)
|
||||
copy_image_task.execute()
|
||||
mock_exists.assert_called_once()
|
||||
mock_getsize.assert_called_once()
|
||||
mock_unlink.assert_called_once()
|
||||
self.staging_store.add.assert_called_once()
|
||||
mock_store_api.assert_called_once_with(
|
||||
"os_glance_staging_store")
|
||||
|
||||
@mock.patch.object(os, 'unlink')
|
||||
@mock.patch.object(os.path, 'getsize')
|
||||
@mock.patch.object(os.path, 'exists')
|
||||
@mock.patch.object(store_api, 'get_store_from_store_identifier')
|
||||
def test_copy_image_to_staging_store_data_exists(
|
||||
self, mock_store_api, mock_exists, mock_getsize, mock_unlink):
|
||||
mock_store_api.return_value = self.staging_store
|
||||
mock_exists.return_value = True
|
||||
mock_getsize.return_value = 4
|
||||
|
||||
copy_image_task = copy_image._CopyImage(
|
||||
self.task.task_id, self.task_type, self.image_repo,
|
||||
self.image_id)
|
||||
with mock.patch.object(self.image_repo, 'get') as get_mock:
|
||||
get_mock.return_value = mock.MagicMock(
|
||||
image_id=self.images[0]['id'],
|
||||
locations=self.images[0]['locations'],
|
||||
status=self.images[0]['status'],
|
||||
size=4
|
||||
)
|
||||
copy_image_task.execute()
|
||||
mock_exists.assert_called_once()
|
||||
mock_store_api.assert_called_once_with(
|
||||
"os_glance_staging_store")
|
||||
mock_getsize.assert_called_once()
|
||||
# As valid image data already exists in staging area
|
||||
# it does not remove it and also does not download
|
||||
# it again to staging area
|
||||
mock_unlink.assert_not_called()
|
||||
self.staging_store.add.assert_not_called()
|
||||
|
||||
@mock.patch.object(store_api, 'get_store_from_store_identifier')
|
||||
def test_copy_non_existing_image_to_staging_store_(self, mock_store_api):
|
||||
mock_store_api.return_value = self.staging_store
|
||||
|
Loading…
x
Reference in New Issue
Block a user