Skip to content

Commit ccfde27

Browse files
committed
Move image verification and writing out of download method
This should help the readability and also make it possible to call new _verify_and_write method with different arguments. Change-Id: I20913201cf945a7fde1f9e6264c415e1235db7b9
1 parent d4c857d commit ccfde27

File tree

2 files changed

+70
-41
lines changed

2 files changed

+70
-41
lines changed

nova/image/glance.py

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -326,59 +326,88 @@ def download(self, context, image_id, data=None, dst_path=None,
326326
raise exception.ImageUnacceptable(image_id=image_id,
327327
reason='Image has no associated data')
328328

329-
# Retrieve properties for verification of Glance image signature
330-
verifier = self._get_verifier(context, image_id, trusted_certs)
329+
return self._verify_and_write(context, image_id, trusted_certs,
330+
image_chunks, data, dst_path)
331+
332+
def _verify_and_write(self, context, image_id, trusted_certs,
333+
image_chunks, data, dst_path):
334+
"""Perform image signature verification and save the image file if needed.
335+
336+
This function writes the content of the image_chunks iterator either to
337+
a file object provided by the data parameter or to a filepath provided
338+
by dst_path parameter. If none of them are provided then no data will
339+
be written out but instead image_chunks iterator is returned.
340+
341+
:param image_id: The UUID of the image
342+
:param trusted_certs: A 'nova.objects.trusted_certs.TrustedCerts'
343+
object with a list of trusted image certificate
344+
IDs.
345+
:param image_chunks An iterator pointing to the image data
346+
:param data: File object to use when writing the image.
347+
If passed as None and dst_path is provided, new file is opened.
348+
:param dst_path: Filepath to transfer the image file to.
349+
:returns an iterable with image data, or nothing. Iterable is returned
350+
only when data param is None and dst_path is not provided (assuming
351+
the caller wants to process the data by itself).
352+
353+
"""
331354

332355
close_file = False
333356
if data is None and dst_path:
334357
data = open(dst_path, 'wb')
335358
close_file = True
336359

360+
write_image = True
337361
if data is None:
362+
write_image = False
338363

339-
# Perform image signature verification
340-
if verifier:
341-
try:
342-
for chunk in image_chunks:
343-
verifier.update(chunk)
344-
verifier.verify()
345-
346-
LOG.info('Image signature verification succeeded '
347-
'for image: %s', image_id)
348-
349-
except cryptography.exceptions.InvalidSignature:
350-
with excutils.save_and_reraise_exception():
351-
LOG.error('Image signature verification failed '
352-
'for image: %s', image_id)
353-
return image_chunks
354-
else:
355-
try:
356-
for chunk in image_chunks:
357-
if verifier:
358-
verifier.update(chunk)
359-
data.write(chunk)
364+
# Retrieve properties for verification of Glance image signature
365+
verifier = self._get_verifier(context, image_id, trusted_certs)
366+
367+
# Exit early if we do not need write nor verify
368+
if verifier is None and write_image is False:
369+
if data is None:
370+
return image_chunks
371+
else:
372+
return
373+
374+
try:
375+
for chunk in image_chunks:
360376
if verifier:
361-
verifier.verify()
362-
LOG.info('Image signature verification succeeded '
363-
'for image %s', image_id)
364-
except cryptography.exceptions.InvalidSignature:
377+
verifier.update(chunk)
378+
if write_image:
379+
data.write(chunk)
380+
if verifier:
381+
verifier.verify()
382+
LOG.info('Image signature verification succeeded '
383+
'for image %s', image_id)
384+
except cryptography.exceptions.InvalidSignature:
385+
if write_image:
365386
data.truncate(0)
366-
with excutils.save_and_reraise_exception():
367-
LOG.error('Image signature verification failed '
368-
'for image: %s', image_id)
369-
except Exception as ex:
387+
with excutils.save_and_reraise_exception():
388+
LOG.error('Image signature verification failed '
389+
'for image %s', image_id)
390+
except Exception as ex:
391+
if write_image:
370392
with excutils.save_and_reraise_exception():
371393
LOG.error("Error writing to %(path)s: %(exception)s",
372394
{'path': dst_path, 'exception': ex})
373-
finally:
374-
if close_file:
375-
# Ensure that the data is pushed all the way down to
376-
# persistent storage. This ensures that in the event of a
377-
# subsequent host crash we don't have running instances
378-
# using a corrupt backing file.
379-
data.flush()
380-
self._safe_fsync(data)
381-
data.close()
395+
else:
396+
with excutils.save_and_reraise_exception():
397+
LOG.error("Error during image verification: %s", ex)
398+
399+
finally:
400+
if close_file:
401+
# Ensure that the data is pushed all the way down to
402+
# persistent storage. This ensures that in the event of a
403+
# subsequent host crash we don't have running instances
404+
# using a corrupt backing file.
405+
data.flush()
406+
self._safe_fsync(data)
407+
data.close()
408+
409+
if data is None:
410+
return image_chunks
382411

383412
def _get_verifier(self, context, image_id, trusted_certs):
384413
verifier = None

nova/tests/unit/image/test_glance.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ def test_download_with_signature_verification_v2(self,
884884
'trusted image certificate IDs must be provided in '
885885
'order to validate an image certificate.')
886886
mock_log.debug.assert_called_once_with(msg)
887-
msg = ('Image signature verification succeeded for image: %s')
887+
msg = ('Image signature verification succeeded for image %s')
888888
mock_log.info.assert_called_once_with(msg, image_id)
889889

890890
@mock.patch.object(six.moves.builtins, 'open')

0 commit comments

Comments
 (0)