Skip to content

Commit 922c2ed

Browse files
alistarleyebinamakonan-abhi
committed
Fix cleaning of web-download image import
If import flow fail before reaching the end it never execute the _DeleteFromFS task and the node_staging_uri is never cleaned up. Implement the revert() function of the _WebDownload task to remove the temporary file. Change-Id: I6dd6a6e2a95a5bd17a80b6256852bb9fac5fa339 Co-Authored-By: Grégoire Unbekandt <[email protected]> Co-Authored-By: Abhishek Kekane <[email protected]> Closes-Bug: #1795950
1 parent 68c202d commit 922c2ed

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

glance/async_/flows/_internal_plugins/web_download.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,20 @@ def revert(self, result, **kwargs):
144144
image.status = 'queued'
145145
self.image_repo.save(image)
146146

147+
# NOTE(abhishekk): Deleting partial image data from staging area
148+
if self._path is not None:
149+
LOG.debug(('Deleting image %(image_id)s from staging '
150+
'area.'), {'image_id': self.image_id})
151+
try:
152+
if CONF.enabled_backends:
153+
store_api.delete(self._path, None)
154+
else:
155+
store_api.delete_from_backend(self._path)
156+
except Exception:
157+
LOG.exception(_LE("Error reverting web-download "
158+
"task: %(task_id)s"), {
159+
'task_id': self.task_id})
160+
147161

148162
def get_flow(**kwargs):
149163
"""Return task flow for web-download.

glance/tests/unit/async_/flows/test_web_download.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from glance_store._drivers import filesystem
2020
from glance_store import backend
2121
from oslo_config import cfg
22+
from taskflow.types import failure
2223

2324
from glance.async_.flows._internal_plugins import web_download
2425
from glance.async_.flows import api_image_import
@@ -175,3 +176,74 @@ def test_web_download_delete_staging_image_succeed(self, mock_exists):
175176
delete_from_fs_task.execute(staging_path)
176177
self.assertEqual(1, mock_exists.call_count)
177178
self.assertEqual(1, mock_unlik.call_count)
179+
180+
@mock.patch.object(filesystem.Store, 'add')
181+
@mock.patch("glance.async_.flows._internal_plugins.web_download.store_api")
182+
def test_web_download_revert_with_failure(self, mock_store_api,
183+
mock_add):
184+
web_download_task = web_download._WebDownload(
185+
self.task.task_id, self.task_type, self.task_repo,
186+
self.image_id, self.uri)
187+
with mock.patch.object(script_utils,
188+
'get_image_data_iter') as mock_iter:
189+
mock_iter.return_value.headers = {'content-length': '4'}
190+
mock_add.return_value = "/path/to_downloaded_data", 3
191+
self.assertRaises(
192+
glance.common.exception.ImportTaskError,
193+
web_download_task.execute)
194+
195+
web_download_task.revert(None)
196+
mock_store_api.delete_from_backend.assert_called_once_with(
197+
"/path/to_downloaded_data")
198+
199+
@mock.patch("glance.async_.flows._internal_plugins.web_download.store_api")
200+
def test_web_download_revert_without_failure_multi_store(self,
201+
mock_store_api):
202+
enabled_backends = {
203+
'fast': 'file',
204+
'cheap': 'file'
205+
}
206+
self.config(enabled_backends=enabled_backends)
207+
web_download_task = web_download._WebDownload(
208+
self.task.task_id, self.task_type, self.task_repo,
209+
self.image_id, self.uri)
210+
web_download_task._path = "/path/to_downloaded_data"
211+
web_download_task.revert("/path/to_downloaded_data")
212+
mock_store_api.delete.assert_called_once_with(
213+
"/path/to_downloaded_data", None)
214+
215+
@mock.patch("glance.async_.flows._internal_plugins.web_download.store_api")
216+
def test_web_download_revert_with_failure_without_path(self,
217+
mock_store_api):
218+
result = failure.Failure.from_exception(
219+
glance.common.exception.ImportTaskError())
220+
web_download_task = web_download._WebDownload(
221+
self.task.task_id, self.task_type, self.task_repo,
222+
self.image_id, self.uri)
223+
web_download_task.revert(result)
224+
mock_store_api.delete_from_backend.assert_not_called()
225+
226+
@mock.patch("glance.async_.flows._internal_plugins.web_download.store_api")
227+
def test_web_download_revert_with_failure_with_path(self, mock_store_api):
228+
result = failure.Failure.from_exception(
229+
glance.common.exception.ImportTaskError())
230+
web_download_task = web_download._WebDownload(
231+
self.task.task_id, self.task_type, self.task_repo,
232+
self.image_id, self.uri)
233+
web_download_task._path = "/path/to_downloaded_data"
234+
web_download_task.revert(result)
235+
mock_store_api.delete_from_backend.assert_called_once_with(
236+
"/path/to_downloaded_data")
237+
238+
@mock.patch("glance.async_.flows._internal_plugins.web_download.store_api")
239+
def test_web_download_delete_fails_on_revert(self, mock_store_api):
240+
result = failure.Failure.from_exception(
241+
glance.common.exception.ImportTaskError())
242+
mock_store_api.delete_from_backend.side_effect = Exception
243+
web_download_task = web_download._WebDownload(
244+
self.task.task_id, self.task_type, self.task_repo,
245+
self.image_id, self.uri)
246+
web_download_task._path = "/path/to_downloaded_data"
247+
# this will verify that revert does not break because of failure
248+
# while deleting data in staging area
249+
web_download_task.revert(result)

0 commit comments

Comments
 (0)