Handle case when a glance image contains no data

In my experience, it happens when Swift storage backing Glance was
purged, but it can probably also happen if an image never had data
at all. This patch raises a correct exception instead of TypeError.

Switch relevant unit tests to Glance V2, as V1 is long deprecated.

Change-Id: I75e5ae7f46ce3a1506ed6108ff05df3083fd5084
Closes-Bug: #1741223
This commit is contained in:
Dmitry Tantsur 2018-01-04 16:47:20 +01:00
parent bf8cb05aa4
commit 70039cbe60
4 changed files with 38 additions and 3 deletions

View File

@ -217,6 +217,14 @@ class BaseImageService(object):
return return
image_chunks = self.call(method, image_id) image_chunks = self.call(method, image_id)
# NOTE(dtantsur): when using Glance V2, image_chunks is a wrapper
# around real data, so we have to check the wrapped data for None.
# Glance V1 returns HTTP 404 in this case, so no need to fix it.
# TODO(dtantsur): remove the hasattr check when we no longer support
# Glance V1.
if hasattr(image_chunks, 'wrapped') and image_chunks.wrapped is None:
raise exception.ImageDownloadFailed(
image_href=image_href, reason=_('image contains no data.'))
if data is None: if data is None:
return image_chunks return image_chunks

View File

@ -92,11 +92,11 @@ class TestGlanceImageService(base.TestCase):
def setUp(self): def setUp(self):
super(TestGlanceImageService, self).setUp() super(TestGlanceImageService, self).setUp()
client = stubs.StubGlanceClient() self.client = stubs.StubGlanceClient()
self.context = context.RequestContext(auth_token=True) self.context = context.RequestContext(auth_token=True)
self.context.user_id = 'fake' self.context.user_id = 'fake'
self.context.project_id = 'fake' self.context.project_id = 'fake'
self.service = service.GlanceImageService(client, 1, self.context) self.service = service.GlanceImageService(self.client, 2, self.context)
self.config(glance_api_servers=['http://localhost'], group='glance') self.config(glance_api_servers=['http://localhost'], group='glance')
self.config(auth_strategy='keystone', group='glance') self.config(auth_strategy='keystone', group='glance')
@ -203,6 +203,17 @@ class TestGlanceImageService(base.TestCase):
stub_service.download(image_id, writer) stub_service.download(image_id, writer)
self.assertTrue(mock_sleep.called) self.assertTrue(mock_sleep.called)
def test_download_no_data(self):
self.client.fake_wrapped = None
image_id = uuidutils.generate_uuid()
image = self._make_datetime_fixture()
with mock.patch.object(self.client, 'get', return_value=image,
autospec=True):
self.assertRaisesRegex(exception.ImageDownloadFailed,
'image contains no data',
self.service.download, image_id)
@mock.patch('sendfile.sendfile', autospec=True) @mock.patch('sendfile.sendfile', autospec=True)
@mock.patch('os.path.getsize', autospec=True) @mock.patch('os.path.getsize', autospec=True)
@mock.patch('%s.open' % __name__, new=mock.mock_open(), create=True) @mock.patch('%s.open' % __name__, new=mock.mock_open(), create=True)

View File

@ -18,8 +18,18 @@ from glanceclient import exc as glance_exc
NOW_GLANCE_FORMAT = "2010-10-11T10:30:22" NOW_GLANCE_FORMAT = "2010-10-11T10:30:22"
class _GlanceWrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __iter__(self):
return iter(())
class StubGlanceClient(object): class StubGlanceClient(object):
fake_wrapped = object()
def __init__(self, images=None): def __init__(self, images=None):
self._images = [] self._images = []
_images = images or [] _images = images or []
@ -38,7 +48,7 @@ class StubGlanceClient(object):
def data(self, image_id): def data(self, image_id):
self.get(image_id) self.get(image_id)
return [] return _GlanceWrapper(self.fake_wrapped)
class FakeImage(object): class FakeImage(object):

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fails deployment with the correct error message in a node's ``last_error``
field if an image from the Image service doesn't contain any data.
See `bug 1741223 <https://bugs.launchpad.net/bugs/1741223>`_ for details.