Merge "Close Glance image if downloading failed."

This commit is contained in:
Zuul 2022-01-17 10:31:21 +00:00 committed by Gerrit Code Review
commit 3d411cad60
2 changed files with 49 additions and 15 deletions

View File

@ -419,6 +419,7 @@ class GlanceImageServiceV2(object):
if data is None: if data is None:
write_image = False write_image = False
try:
# Retrieve properties for verification of Glance image signature # Retrieve properties for verification of Glance image signature
verifier = self._get_verifier(context, image_id, trusted_certs) verifier = self._get_verifier(context, image_id, trusted_certs)
@ -429,7 +430,6 @@ class GlanceImageServiceV2(object):
else: else:
return return
try:
for chunk in image_chunks: for chunk in image_chunks:
if verifier: if verifier:
verifier.update(chunk) verifier.update(chunk)
@ -463,6 +463,8 @@ class GlanceImageServiceV2(object):
data.flush() data.flush()
self._safe_fsync(data) self._safe_fsync(data)
data.close() data.close()
if isinstance(image_chunks, glance_utils.IterableWithLength):
image_chunks.iterable.close()
if data is None: if data is None:
return image_chunks return image_chunks

View File

@ -16,12 +16,14 @@
import copy import copy
import datetime import datetime
import io
from io import StringIO from io import StringIO
import urllib.parse as urlparse import urllib.parse as urlparse
import cryptography import cryptography
from cursive import exception as cursive_exception from cursive import exception as cursive_exception
import ddt import ddt
import glanceclient.common.utils
import glanceclient.exc import glanceclient.exc
from glanceclient.v1 import images from glanceclient.v1 import images
from glanceclient.v2 import schemas from glanceclient.v2 import schemas
@ -701,8 +703,7 @@ class TestDownloadNoDirectUri(test.NoDBTestCase):
with testtools.ExpectedException(exception.ImageUnacceptable): with testtools.ExpectedException(exception.ImageUnacceptable):
service.download(ctx, mock.sentinel.image_id) service.download(ctx, mock.sentinel.image_id)
@mock.patch('glanceclient.common.utils.IterableWithLength') @mock.patch('os.path.getsize')
@mock.patch('os.path.getsize', return_value=1)
@mock.patch('builtins.open') @mock.patch('builtins.open')
@mock.patch('nova.image.glance.LOG') @mock.patch('nova.image.glance.LOG')
@mock.patch('nova.image.glance.GlanceImageServiceV2._get_verifier') @mock.patch('nova.image.glance.GlanceImageServiceV2._get_verifier')
@ -710,7 +711,7 @@ class TestDownloadNoDirectUri(test.NoDBTestCase):
@mock.patch('nova.image.glance.GlanceImageServiceV2.show') @mock.patch('nova.image.glance.GlanceImageServiceV2.show')
def test_download_direct_rbd_uri_v2( def test_download_direct_rbd_uri_v2(
self, show_mock, get_tran_mock, get_verifier_mock, log_mock, self, show_mock, get_tran_mock, get_verifier_mock, log_mock,
open_mock, getsize_mock, iterable_with_length_mock): open_mock, getsize_mock):
self.flags(enable_rbd_download=True, group='glance') self.flags(enable_rbd_download=True, group='glance')
show_mock.return_value = { show_mock.return_value = {
'locations': [ 'locations': [
@ -724,9 +725,13 @@ class TestDownloadNoDirectUri(test.NoDBTestCase):
get_tran_mock.return_value = tran_mod get_tran_mock.return_value = tran_mod
client = mock.MagicMock() client = mock.MagicMock()
ctx = mock.sentinel.ctx ctx = mock.sentinel.ctx
image_content = ["rbd1", "rbd2"]
getsize_mock.return_value = len(image_content)
file_object = mock.MagicMock(spec=io.BytesIO)
file_object.__iter__.return_value = image_content
writer = mock.MagicMock() writer = mock.MagicMock()
writer.__enter__.return_value = file_object
open_mock.return_value = writer open_mock.return_value = writer
iterable_with_length_mock.return_value = ["rbd1", "rbd2"]
service = glance.GlanceImageServiceV2(client) service = glance.GlanceImageServiceV2(client)
verifier = mock.MagicMock() verifier = mock.MagicMock()
@ -994,7 +999,7 @@ class TestDownloadSignatureVerification(test.NoDBTestCase):
service.download, service.download,
context=None, image_id=None, context=None, image_id=None,
data=None, dst_path=None) data=None, dst_path=None)
mock_log.error.assert_called_once_with(mock.ANY, mock.ANY) self.assertEqual(mock_log.error.call_count, 2)
@mock.patch('nova.image.glance.LOG') @mock.patch('nova.image.glance.LOG')
@mock.patch('nova.image.glance.GlanceImageServiceV2.show') @mock.patch('nova.image.glance.GlanceImageServiceV2.show')
@ -1052,6 +1057,33 @@ class TestDownloadSignatureVerification(test.NoDBTestCase):
mock_dest.truncate.assert_called_once_with(0) mock_dest.truncate.assert_called_once_with(0)
self.assertTrue(mock_dest.close.called) self.assertTrue(mock_dest.close.called)
@mock.patch('builtins.open')
@mock.patch('cursive.signature_utils.get_verifier')
@mock.patch('nova.image.glance.GlanceImageServiceV2.show')
@mock.patch('nova.image.glance.GlanceImageServiceV2._safe_fsync')
def test_download_dst_path_with_invalid_signature_v2(
self, mock_fsync, mock_show, mock_get_verifier, mock_open):
glance_iterable = mock.MagicMock(spec=io.BytesIO)
glance_iterable.__iter__.return_value = self.fake_img_data
self.client.call.return_value = fake_glance_response(
glanceclient.common.utils.IterableWithLength(
iterable=glance_iterable, length=len(self.fake_img_data)))
service = glance.GlanceImageServiceV2(self.client)
mock_get_verifier.side_effect = \
cursive_exception.SignatureVerificationError
mock_dest = mock.MagicMock()
mock_open.return_value = mock_dest
mock_show.return_value = self.fake_img_props
fake_path = 'FAKE_PATH'
self.assertRaises(cursive_exception.SignatureVerificationError,
service.download,
context=None, image_id=None,
data=None, dst_path=fake_path)
mock_open.assert_called_once_with(fake_path, 'wb')
mock_fsync.assert_called_once_with(mock_dest)
mock_dest.close.assert_called()
glance_iterable.close.assert_called()
class TestDownloadCertificateValidation(test.NoDBTestCase): class TestDownloadCertificateValidation(test.NoDBTestCase):
"""Tests the download method of the GlanceImageServiceV2 when """Tests the download method of the GlanceImageServiceV2 when