From e95d8e9abfc4822e34dfbaeaf762ea3def4aa0f5 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sun, 24 Jun 2018 11:32:47 -0500 Subject: [PATCH] Finish migrating image tests to requests-mock We let some of these slip through originally. Fix them so we can do other things. Guess what? There were bugs in the v1 codepath. Change-Id: Ia51598548cde0ddb4f9e96b166274e9a104cf649 --- shade/openstackcloud.py | 17 +- shade/tests/unit/test_image.py | 471 +++++++++++++++++---------------- 2 files changed, 249 insertions(+), 239 deletions(-) diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index ac1290cea..59fcd50cf 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -4867,7 +4867,9 @@ class OpenStackCloud( image_kwargs['properties'].update(meta) image_kwargs['name'] = name - image = self._image_client.post('/images', json=image_kwargs) + image = self._get_and_munchify( + 'image', + self._image_client.post('/images', json=image_kwargs)) checksum = image_kwargs['properties'].get(IMAGE_MD5_KEY, '') try: @@ -4879,21 +4881,24 @@ class OpenStackCloud( if checksum: headers['x-image-meta-checksum'] = checksum - image = self._image_client.put( - '/images/{id}'.format(id=image.id), - headers=headers, data=image_data) + image = self._get_and_munchify( + 'image', + self._image_client.put( + '/images/{id}'.format(id=image.id), + headers=headers, data=image_data)) except exc.OpenStackCloudHTTPError: self.log.debug("Deleting failed upload of image %s", name) try: - self._image_client.delete('/images/{id}'.format(id=image.id)) + self._image_client.delete( + '/images/{id}'.format(id=image.id)) except exc.OpenStackCloudHTTPError: # We're just trying to clean up - if it doesn't work - shrug self.log.debug( "Failed deleting image after we failed uploading it.", exc_info=True) raise - return image + return self._normalize_image(image) def _upload_image_put( self, name, filename, meta, wait, timeout, **image_kwargs): diff --git a/shade/tests/unit/test_image.py b/shade/tests/unit/test_image.py index b9ec47937..dae58a56f 100644 --- a/shade/tests/unit/test_image.py +++ b/shade/tests/unit/test_image.py @@ -19,8 +19,6 @@ import operator import tempfile import uuid -import mock -import munch import six import shade @@ -484,295 +482,302 @@ class TestImage(BaseTestImage): name, imagefile.name, wait=True, timeout=1, is_public=False, **kwargs) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_v1( - self, mock_image_client, mock_is_client_version): - # TODO(mordred) Fix this to use requests_mock - mock_is_client_version.return_value = False - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) + def test_create_image_put_v1(self): + self.cloud.cloud_config.config['image_api_version'] = '1' - args = {'name': '42 name', + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': 'qcow2', 'properties': { - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), 'is_public': False}} - ret = munch.Munch(args.copy()) - ret['id'] = '42' + + ret = args.copy() + ret['id'] = self.image_id ret['status'] = 'success' - mock_image_client.get.side_effect = [ - [], - [ret], - [ret], - ] - mock_image_client.post.return_value = ret - mock_image_client.put.return_value = ret - self._call_create_image('42 name') - mock_image_client.post.assert_called_with('/images', json=args) - mock_image_client.put.assert_called_with( - '/images/42', data=mock.ANY, - headers={ - 'x-image-meta-checksum': mock.ANY, - 'x-glance-registry-purge-props': 'false' - }) - mock_image_client.get.assert_called_with('/images/detail', params={}) - self.assertEqual( - self._munch_images(ret), self.cloud.list_images()) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_v1_bad_delete( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = False - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v1/images/detail', + json={'images': []}), + dict(method='POST', + uri='https://image.example.com/v1/images', + json={'image': ret}, + validate=dict(json=args)), + dict(method='PUT', + uri='https://image.example.com/v1/images/{id}'.format( + id=self.image_id), + json={'image': ret}, + validate=dict(headers={ + 'x-image-meta-checksum': fakes.NO_MD5, + 'x-glance-registry-purge-props': 'false' + })), + dict(method='GET', + uri='https://image.example.com/v1/images/detail', + json={'images': [ret]}), + ]) + self._call_create_image(self.image_name) + self.assertEqual(self._munch_images(ret), self.cloud.list_images()) - args = {'name': '42 name', + def test_create_image_put_v1_bad_delete(self): + self.cloud.cloud_config.config['image_api_version'] = '1' + + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': 'qcow2', 'properties': { - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), 'is_public': False}} - ret = munch.Munch(args.copy()) - ret['id'] = '42' + + ret = args.copy() + ret['id'] = self.image_id ret['status'] = 'success' - mock_image_client.get.side_effect = [ - [], - [ret], - ] - mock_image_client.post.return_value = ret - mock_image_client.put.side_effect = exc.OpenStackCloudHTTPError( - "Some error", {}) + + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v1/images/detail', + json={'images': []}), + dict(method='POST', + uri='https://image.example.com/v1/images', + json={'image': ret}, + validate=dict(json=args)), + dict(method='PUT', + uri='https://image.example.com/v1/images/{id}'.format( + id=self.image_id), + status_code=400, + validate=dict(headers={ + 'x-image-meta-checksum': fakes.NO_MD5, + 'x-glance-registry-purge-props': 'false' + })), + dict(method='DELETE', + uri='https://image.example.com/v1/images/{id}'.format( + id=self.image_id), + json={'images': [ret]}), + ]) + self.assertRaises( exc.OpenStackCloudHTTPError, self._call_create_image, - '42 name') - mock_image_client.post.assert_called_with('/images', json=args) - mock_image_client.put.assert_called_with( - '/images/42', data=mock.ANY, - headers={ - 'x-image-meta-checksum': mock.ANY, - 'x-glance-registry-purge-props': 'false' - }) - mock_image_client.delete.assert_called_with('/images/42') + self.image_name) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_update_image_no_patch( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True + self.assert_calls() + + def test_update_image_no_patch(self): self.cloud.image_api_use_tasks = False - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) - - args = {'name': '42 name', + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': 'qcow2', - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', - 'visibility': 'private', - 'min_disk': 0, 'min_ram': 0} - ret = munch.Munch(args.copy()) - ret['id'] = '42' + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), + 'visibility': 'private'} + + ret = args.copy() + ret['id'] = self.image_id ret['status'] = 'success' - mock_image_client.get.side_effect = [ - [], - [ret], - [ret], - ] + self.cloud.update_image_properties( image=self._image_dict(ret), - **{'owner_specified.shade.object': 'images/42 name'}) - mock_image_client.get.assert_called_with('/images', params={}) - mock_image_client.patch.assert_not_called() + **{'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name)}) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_v2_bad_delete( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True + self.assert_calls() + + def test_create_image_put_v2_bad_delete(self): self.cloud.image_api_use_tasks = False - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) - - args = {'name': '42 name', + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': 'qcow2', - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', - 'visibility': 'private', - 'min_disk': 0, 'min_ram': 0} - ret = munch.Munch(args.copy()) - ret['id'] = '42' + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), + 'visibility': 'private'} + + ret = args.copy() + ret['id'] = self.image_id ret['status'] = 'success' - mock_image_client.get.side_effect = [ - [], - [ret], - [ret], - ] - mock_image_client.post.return_value = ret - mock_image_client.put.side_effect = exc.OpenStackCloudHTTPError( - "Some error", {}) + + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': []}), + dict(method='POST', + uri='https://image.example.com/v2/images', + json=ret, + validate=dict(json=args)), + dict(method='PUT', + uri='https://image.example.com/v2/images/{id}/file'.format( + id=self.image_id), + status_code=400, + validate=dict( + headers={ + 'Content-Type': 'application/octet-stream', + }, + )), + dict(method='DELETE', + uri='https://image.example.com/v2/images/{id}'.format( + id=self.image_id)), + ]) + self.assertRaises( exc.OpenStackCloudHTTPError, self._call_create_image, - '42 name', min_disk='0', min_ram=0) - mock_image_client.post.assert_called_with('/images', json=args) - mock_image_client.put.assert_called_with( - '/images/42/file', - headers={'Content-Type': 'application/octet-stream'}, - data=mock.ANY) - mock_image_client.delete.assert_called_with('/images/42') + self.image_name) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_bad_int( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True + self.assert_calls() + + def test_create_image_put_bad_int(self): self.cloud.image_api_use_tasks = False + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': []}), + ]) + self.assertRaises( exc.OpenStackCloudException, - self._call_create_image, '42 name', min_disk='fish', min_ram=0) - mock_image_client.post.assert_not_called() + self._call_create_image, self.image_name, + min_disk='fish', min_ram=0) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_user_int( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True + self.assert_calls() + + def test_create_image_put_user_int(self): self.cloud.image_api_use_tasks = False - args = {'name': '42 name', + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': u'qcow2', - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), 'int_v': '12345', 'visibility': 'private', 'min_disk': 0, 'min_ram': 0} - ret = munch.Munch(args.copy()) - ret['id'] = '42' - ret['status'] = 'success' - mock_image_client.get.side_effect = [ - [], - [ret], - [ret] - ] - mock_image_client.post.return_value = ret - self._call_create_image( - '42 name', min_disk='0', min_ram=0, int_v=12345) - mock_image_client.post.assert_called_with('/images', json=args) - mock_image_client.put.assert_called_with( - '/images/42/file', - headers={'Content-Type': 'application/octet-stream'}, - data=mock.ANY) - mock_image_client.get.assert_called_with('/images', params={}) - self.assertEqual( - self._munch_images(ret), self.cloud.list_images()) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_meta_int( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True + ret = args.copy() + ret['id'] = self.image_id + ret['status'] = 'success' + + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': []}), + dict(method='POST', + uri='https://image.example.com/v2/images', + json=ret, + validate=dict(json=args)), + dict(method='PUT', + uri='https://image.example.com/v2/images/{id}/file'.format( + id=self.image_id), + validate=dict( + headers={ + 'Content-Type': 'application/octet-stream', + }, + )), + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': [ret]}), + ]) + + self._call_create_image( + self.image_name, min_disk='0', min_ram=0, int_v=12345) + + self.assert_calls() + + def test_create_image_put_meta_int(self): self.cloud.image_api_use_tasks = False - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) - - self._call_create_image( - '42 name', min_disk='0', min_ram=0, meta={'int_v': 12345}) - args = {'name': '42 name', + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': u'qcow2', - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), 'int_v': 12345, 'visibility': 'private', 'min_disk': 0, 'min_ram': 0} - ret = munch.Munch(args.copy()) - ret['id'] = '42' - ret['status'] = 'success' - mock_image_client.get.return_value = [ret] - mock_image_client.post.return_value = ret - mock_image_client.get.assert_called_with('/images', params={}) - self.assertEqual( - self._munch_images(ret), self.cloud.list_images()) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_protected( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True + ret = args.copy() + ret['id'] = self.image_id + ret['status'] = 'success' + + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': []}), + dict(method='POST', + uri='https://image.example.com/v2/images', + json=ret, + validate=dict(json=args)), + dict(method='PUT', + uri='https://image.example.com/v2/images/{id}/file'.format( + id=self.image_id), + validate=dict( + headers={ + 'Content-Type': 'application/octet-stream', + }, + )), + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': [ret]}), + ]) + + self._call_create_image( + self.image_name, min_disk='0', min_ram=0, meta={'int_v': 12345}) + + self.assert_calls() + + def test_create_image_put_protected(self): self.cloud.image_api_use_tasks = False - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) - - args = {'name': '42 name', + args = {'name': self.image_name, 'container_format': 'bare', 'disk_format': u'qcow2', - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', + 'owner_specified.shade.md5': fakes.NO_MD5, + 'owner_specified.shade.sha256': fakes.NO_SHA256, + 'owner_specified.shade.object': 'images/{name}'.format( + name=self.image_name), + 'int_v': '12345', 'protected': False, - 'int_v': '12345', 'visibility': 'private', 'min_disk': 0, 'min_ram': 0} - ret = munch.Munch(args.copy()) - ret['id'] = '42' + + ret = args.copy() + ret['id'] = self.image_id ret['status'] = 'success' - mock_image_client.get.side_effect = [ - [], - [ret], - [ret], - ] - mock_image_client.put.return_value = ret - mock_image_client.post.return_value = ret + + self.register_uris([ + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': []}), + dict(method='POST', + uri='https://image.example.com/v2/images', + json=ret, + validate=dict(json=args)), + dict(method='PUT', + uri='https://image.example.com/v2/images/{id}/file'.format( + id=self.image_id), + validate=dict( + headers={ + 'Content-Type': 'application/octet-stream', + }, + )), + dict(method='GET', + uri='https://image.example.com/v2/images', + json={'images': [ret]}), + ]) + self._call_create_image( - '42 name', min_disk='0', min_ram=0, properties={'int_v': 12345}, - protected=False) - mock_image_client.post.assert_called_with('/images', json=args) - mock_image_client.put.assert_called_with( - '/images/42/file', data=mock.ANY, - headers={'Content-Type': 'application/octet-stream'}) - self.assertEqual(self._munch_images(ret), self.cloud.list_images()) + self.image_name, min_disk='0', min_ram=0, + properties={'int_v': 12345}, protected=False) - @mock.patch.object(shade.OpenStackCloud, '_is_client_version') - @mock.patch.object(shade.OpenStackCloud, '_image_client') - def test_create_image_put_user_prop( - self, mock_image_client, mock_is_client_version): - mock_is_client_version.return_value = True - self.cloud.image_api_use_tasks = False - - mock_image_client.get.return_value = [] - self.assertEqual([], self.cloud.list_images()) - - args = {'name': '42 name', - 'container_format': 'bare', 'disk_format': u'qcow2', - 'owner_specified.shade.md5': mock.ANY, - 'owner_specified.shade.sha256': mock.ANY, - 'owner_specified.shade.object': 'images/42 name', - 'int_v': '12345', - 'xenapi_use_agent': 'False', - 'visibility': 'private', - 'min_disk': 0, 'min_ram': 0} - ret = munch.Munch(args.copy()) - ret['id'] = '42' - ret['status'] = 'success' - mock_image_client.get.return_value = [ret] - mock_image_client.post.return_value = ret - self._call_create_image( - '42 name', min_disk='0', min_ram=0, properties={'int_v': 12345}) - mock_image_client.get.assert_called_with('/images', params={}) - self.assertEqual( - self._munch_images(ret), self.cloud.list_images()) + self.assert_calls() def test_get_image_by_id(self): self.register_uris([