Skip to content

Commit 3d411ca

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Close Glance image if downloading failed."
2 parents 24e02f1 + 43bca18 commit 3d411ca

File tree

2 files changed

+48
-14
lines changed

2 files changed

+48
-14
lines changed

nova/image/glance.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -419,17 +419,17 @@ def _verify_and_write(self, context, image_id, trusted_certs,
419419
if data is None:
420420
write_image = False
421421

422-
# Retrieve properties for verification of Glance image signature
423-
verifier = self._get_verifier(context, image_id, trusted_certs)
422+
try:
423+
# Retrieve properties for verification of Glance image signature
424+
verifier = self._get_verifier(context, image_id, trusted_certs)
424425

425-
# Exit early if we do not need write nor verify
426-
if verifier is None and write_image is False:
427-
if data is None:
428-
return image_chunks
429-
else:
430-
return
426+
# Exit early if we do not need write nor verify
427+
if verifier is None and write_image is False:
428+
if data is None:
429+
return image_chunks
430+
else:
431+
return
431432

432-
try:
433433
for chunk in image_chunks:
434434
if verifier:
435435
verifier.update(chunk)
@@ -463,6 +463,8 @@ def _verify_and_write(self, context, image_id, trusted_certs,
463463
data.flush()
464464
self._safe_fsync(data)
465465
data.close()
466+
if isinstance(image_chunks, glance_utils.IterableWithLength):
467+
image_chunks.iterable.close()
466468

467469
if data is None:
468470
return image_chunks

nova/tests/unit/image/test_glance.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616

1717
import copy
1818
import datetime
19+
import io
1920
from io import StringIO
2021
import urllib.parse as urlparse
2122

2223
import cryptography
2324
from cursive import exception as cursive_exception
2425
import ddt
26+
import glanceclient.common.utils
2527
import glanceclient.exc
2628
from glanceclient.v1 import images
2729
from glanceclient.v2 import schemas
@@ -701,16 +703,15 @@ def test_download_no_returned_image_data_v2(
701703
with testtools.ExpectedException(exception.ImageUnacceptable):
702704
service.download(ctx, mock.sentinel.image_id)
703705

704-
@mock.patch('glanceclient.common.utils.IterableWithLength')
705-
@mock.patch('os.path.getsize', return_value=1)
706+
@mock.patch('os.path.getsize')
706707
@mock.patch('builtins.open')
707708
@mock.patch('nova.image.glance.LOG')
708709
@mock.patch('nova.image.glance.GlanceImageServiceV2._get_verifier')
709710
@mock.patch('nova.image.glance.GlanceImageServiceV2._get_transfer_method')
710711
@mock.patch('nova.image.glance.GlanceImageServiceV2.show')
711712
def test_download_direct_rbd_uri_v2(
712713
self, show_mock, get_tran_mock, get_verifier_mock, log_mock,
713-
open_mock, getsize_mock, iterable_with_length_mock):
714+
open_mock, getsize_mock):
714715
self.flags(enable_rbd_download=True, group='glance')
715716
show_mock.return_value = {
716717
'locations': [
@@ -724,9 +725,13 @@ def test_download_direct_rbd_uri_v2(
724725
get_tran_mock.return_value = tran_mod
725726
client = mock.MagicMock()
726727
ctx = mock.sentinel.ctx
728+
image_content = ["rbd1", "rbd2"]
729+
getsize_mock.return_value = len(image_content)
730+
file_object = mock.MagicMock(spec=io.BytesIO)
731+
file_object.__iter__.return_value = image_content
727732
writer = mock.MagicMock()
733+
writer.__enter__.return_value = file_object
728734
open_mock.return_value = writer
729-
iterable_with_length_mock.return_value = ["rbd1", "rbd2"]
730735
service = glance.GlanceImageServiceV2(client)
731736

732737
verifier = mock.MagicMock()
@@ -994,7 +999,7 @@ def test_download_with_get_verifier_failure_v2(self,
994999
service.download,
9951000
context=None, image_id=None,
9961001
data=None, dst_path=None)
997-
mock_log.error.assert_called_once_with(mock.ANY, mock.ANY)
1002+
self.assertEqual(mock_log.error.call_count, 2)
9981003

9991004
@mock.patch('nova.image.glance.LOG')
10001005
@mock.patch('nova.image.glance.GlanceImageServiceV2.show')
@@ -1052,6 +1057,33 @@ def test_download_dst_path_signature_fail_v2(self, mock_fsync,
10521057
mock_dest.truncate.assert_called_once_with(0)
10531058
self.assertTrue(mock_dest.close.called)
10541059

1060+
@mock.patch('builtins.open')
1061+
@mock.patch('cursive.signature_utils.get_verifier')
1062+
@mock.patch('nova.image.glance.GlanceImageServiceV2.show')
1063+
@mock.patch('nova.image.glance.GlanceImageServiceV2._safe_fsync')
1064+
def test_download_dst_path_with_invalid_signature_v2(
1065+
self, mock_fsync, mock_show, mock_get_verifier, mock_open):
1066+
glance_iterable = mock.MagicMock(spec=io.BytesIO)
1067+
glance_iterable.__iter__.return_value = self.fake_img_data
1068+
self.client.call.return_value = fake_glance_response(
1069+
glanceclient.common.utils.IterableWithLength(
1070+
iterable=glance_iterable, length=len(self.fake_img_data)))
1071+
service = glance.GlanceImageServiceV2(self.client)
1072+
mock_get_verifier.side_effect = \
1073+
cursive_exception.SignatureVerificationError
1074+
mock_dest = mock.MagicMock()
1075+
mock_open.return_value = mock_dest
1076+
mock_show.return_value = self.fake_img_props
1077+
fake_path = 'FAKE_PATH'
1078+
self.assertRaises(cursive_exception.SignatureVerificationError,
1079+
service.download,
1080+
context=None, image_id=None,
1081+
data=None, dst_path=fake_path)
1082+
mock_open.assert_called_once_with(fake_path, 'wb')
1083+
mock_fsync.assert_called_once_with(mock_dest)
1084+
mock_dest.close.assert_called()
1085+
glance_iterable.close.assert_called()
1086+
10551087

10561088
class TestDownloadCertificateValidation(test.NoDBTestCase):
10571089
"""Tests the download method of the GlanceImageServiceV2 when

0 commit comments

Comments
 (0)