From d24bebb538296894c9dc0665e5dcec7cf1aa882f Mon Sep 17 00:00:00 2001 From: dineshbhor Date: Wed, 13 Apr 2016 13:27:04 +0000 Subject: [PATCH] Fix update_image unsupported media type Currently update_image returns unsupported media type because unexpected Content-Type (application/json) being passed to glance service. Provided expected Content-Type and body with patch operations(op). Used make_patch() method from jsonpatch library to create patch for updating image. Closes-Bug: #1455620 Change-Id: I3d77648d55b2870e40ff689b47574e68aa72d7f6 --- openstack/image/v2/_proxy.py | 3 +- openstack/image/v2/image.py | 16 +++++++++++ openstack/tests/unit/image/v2/test_image.py | 32 +++++++++++++++++++++ openstack/tests/unit/image/v2/test_proxy.py | 18 ++++++++++-- requirements.txt | 1 + 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/openstack/image/v2/_proxy.py b/openstack/image/v2/_proxy.py index c886b305..14907dbd 100644 --- a/openstack/image/v2/_proxy.py +++ b/openstack/image/v2/_proxy.py @@ -158,7 +158,8 @@ class Proxy(proxy2.BaseProxy): :returns: The updated image :rtype: :class:`~openstack.image.v2.image.Image` """ - return self._update(_image.Image, image, **attrs) + img = self._get_resource(_image.Image, image) + return img.update(self._session, **attrs) def deactivate_image(self, image): """Deactivate an image diff --git a/openstack/image/v2/image.py b/openstack/image/v2/image.py index d3298c0d..5694ce7f 100644 --- a/openstack/image/v2/image.py +++ b/openstack/image/v2/image.py @@ -13,6 +13,8 @@ import hashlib import logging +import jsonpatch + from openstack import exceptions from openstack.image import image_service from openstack import resource2 @@ -283,3 +285,17 @@ class Image(resource2.Resource): "Unable to verify the integrity of image %s" % (self.id)) return resp.content + + def update(self, session, **attrs): + url = utils.urljoin(self.base_path, self.id) + headers = { + 'Content-Type': 'application/openstack-images-v2.1-json-patch', + 'Accept': '' + } + original = self.to_dict() + patch_string = jsonpatch.make_patch(original, attrs).to_string() + resp = session.patch(url, endpoint_filter=self.service, + data=patch_string, + headers=headers) + self._translate_response(resp, has_body=True) + return self diff --git a/openstack/tests/unit/image/v2/test_image.py b/openstack/tests/unit/image/v2/test_image.py index bebb4c53..2a3acf45 100644 --- a/openstack/tests/unit/image/v2/test_image.py +++ b/openstack/tests/unit/image/v2/test_image.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import json + import mock import testtools @@ -292,3 +294,33 @@ class TestImage(testtools.TestCase): stream=True) self.assertEqual(rv, resp) + + def test_image_update(self): + sot = image.Image(**EXAMPLE) + # Let the translate pass through, that portion is tested elsewhere + sot._translate_response = mock.Mock() + + resp = mock.Mock() + resp.content = b"abc" + headers = { + 'Content-Type': 'application/openstack-images-v2.1-json-patch', + 'Accept': '', + } + resp.headers = headers + resp.status_code = 200 + self.sess.patch.return_value = resp + + value = ('[{"value": "fake_name", "op": "replace", "path": "/name"}, ' + '{"value": "fake_value", "op": "add", ' + '"path": "/new_property"}]') + fake_img = sot.to_dict() + fake_img['name'] = 'fake_name' + fake_img['new_property'] = 'fake_value' + + sot.update(self.sess, **fake_img) + url = 'images/' + IDENTIFIER + self.sess.patch.assert_called_once() + call = self.sess.patch.call_args + call_args, call_kwargs = call + self.assertEqual(url, call_args[0]) + self.assertEqual(json.loads(value), json.loads(call_kwargs['data'])) diff --git a/openstack/tests/unit/image/v2/test_proxy.py b/openstack/tests/unit/image/v2/test_proxy.py index b8a1ee9d..8bb060c7 100644 --- a/openstack/tests/unit/image/v2/test_proxy.py +++ b/openstack/tests/unit/image/v2/test_proxy.py @@ -16,8 +16,11 @@ from openstack import exceptions from openstack.image.v2 import _proxy from openstack.image.v2 import image from openstack.image.v2 import member +from openstack.tests.unit.image.v2 import test_image as fake_image from openstack.tests.unit import test_proxy_base2 +EXAMPLE = fake_image.EXAMPLE + class TestImageProxy(test_proxy_base2.TestProxyBase): def setUp(self): @@ -53,8 +56,19 @@ class TestImageProxy(test_proxy_base2.TestProxyBase): def test_image_delete_ignore(self): self.verify_delete(self.proxy.delete_image, image.Image, True) - def test_image_update(self): - self.verify_update(self.proxy.update_image, image.Image) + @mock.patch("openstack.resource2.Resource._translate_response") + @mock.patch("openstack.proxy2.BaseProxy._get") + @mock.patch("openstack.image.v2.image.Image.update") + def test_image_update(self, mock_update_image, mock_get_image, + mock_transpose): + original_image = image.Image(**EXAMPLE) + mock_get_image.return_value = original_image + EXAMPLE['name'] = 'fake_name' + updated_image = image.Image(**EXAMPLE) + mock_update_image.return_value = updated_image.to_dict() + result = self.proxy.update_image(original_image, + **updated_image.to_dict()) + self.assertEqual('fake_name', result.get('name')) def test_image_get(self): self.verify_get(self.proxy.get_image, image.Image) diff --git a/requirements.txt b/requirements.txt index b2a4bb5c..49923243 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 +jsonpatch>=1.1 # BSD six>=1.9.0 # MIT stevedore>=1.20.0 # Apache-2.0 os-client-config>=1.27.0 # Apache-2.0