diff --git a/openstack/image/v2/_proxy.py b/openstack/image/v2/_proxy.py index 3a689d991..3b2d65f3c 100644 --- a/openstack/image/v2/_proxy.py +++ b/openstack/image/v2/_proxy.py @@ -157,8 +157,7 @@ class Proxy(proxy.Proxy): :returns: The updated image :rtype: :class:`~openstack.image.v2.image.Image` """ - res = self._get_resource(_image.Image, image) - return res.commit(self, **attrs) + return self._update(_image.Image, image, **attrs) def deactivate_image(self, image): """Deactivate an image diff --git a/openstack/image/v2/image.py b/openstack/image/v2/image.py index 42f54e23c..bcf4681f9 100644 --- a/openstack/image/v2/image.py +++ b/openstack/image/v2/image.py @@ -12,9 +12,6 @@ import hashlib -import copy -import jsonpatch - from openstack import _log from openstack import exceptions from openstack.image import image_service @@ -36,6 +33,7 @@ class Image(resource.Resource): allow_delete = True allow_list = True commit_method = 'PATCH' + commit_jsonpatch = True _query_mapping = resource.QueryParameters( "name", "visibility", @@ -114,12 +112,6 @@ class Image(resource.Resource): #: when you set the show_image_direct_url option to true in the #: Image service's configuration file. direct_url = resource.Body('direct_url') - #: An image property. - path = resource.Body('path') - #: Value of image property used in add or replace operations expressed - #: in JSON notation. For example, you must enclose strings in quotation - #: marks, and you do not enclose numeric values in quotation marks. - value = resource.Body('value') #: The URL to access the image file kept in external store. url = resource.Body('url') #: The location metadata. @@ -294,21 +286,16 @@ class Image(resource.Resource): return resp.content - def commit(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() + def _prepare_request(self, requires_id=None, prepend_key=False, + patch=False): + request = super(Image, self)._prepare_request(requires_id=requires_id, + prepend_key=prepend_key, + patch=patch) + if patch: + headers = { + 'Content-Type': 'application/openstack-images-v2.1-json-patch', + 'Accept': '' + } + request.headers.update(headers) - # Update values from **attrs so they can be passed to jsonpatch - new = copy.deepcopy(self.to_dict()) - new.update(**attrs) - - patch_string = jsonpatch.make_patch(original, new).to_string() - resp = session.patch(url, - data=patch_string, - headers=headers) - self._translate_response(resp, has_body=True) - return self + return request diff --git a/openstack/tests/unit/image/v2/test_image.py b/openstack/tests/unit/image/v2/test_image.py index b5a8b870c..e2d1ba1b1 100644 --- a/openstack/tests/unit/image/v2/test_image.py +++ b/openstack/tests/unit/image/v2/test_image.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import json import operator from keystoneauth1 import adapter @@ -45,8 +44,6 @@ EXAMPLE = { 'file': '15', 'locations': ['15', '16'], 'direct_url': '17', - 'path': '18', - 'value': '19', 'url': '20', 'metadata': {'21': '22'}, 'architecture': '23', @@ -142,8 +139,6 @@ class TestImage(base.TestCase): self.assertEqual(EXAMPLE['file'], sot.file) self.assertEqual(EXAMPLE['locations'], sot.locations) self.assertEqual(EXAMPLE['direct_url'], sot.direct_url) - self.assertEqual(EXAMPLE['path'], sot.path) - self.assertEqual(EXAMPLE['value'], sot.value) self.assertEqual(EXAMPLE['url'], sot.url) self.assertEqual(EXAMPLE['metadata'], sot.metadata) self.assertEqual(EXAMPLE['architecture'], sot.architecture) @@ -312,7 +307,9 @@ class TestImage(base.TestCase): self.assertEqual(rv, resp) def test_image_update(self): - sot = image.Image(**EXAMPLE) + values = EXAMPLE.copy() + del values['instance_uuid'] + sot = image.Image(**values) # Let the translate pass through, that portion is tested elsewhere sot._translate_response = mock.Mock() @@ -326,21 +323,18 @@ class TestImage(base.TestCase): 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' + value = [{"value": "fake_name", "op": "replace", "path": "/name"}, + {"value": "fake_value", "op": "add", + "path": "/instance_uuid"}] - sot.commit(self.sess, **fake_img) + sot.name = 'fake_name' + sot.instance_uuid = 'fake_value' + sot.commit(self.sess) 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( - sorted(json.loads(value), key=operator.itemgetter('value')), - sorted( - json.loads(call_kwargs['data']), - key=operator.itemgetter('value'))) + sorted(value, key=operator.itemgetter('value')), + sorted(call_kwargs['json'], key=operator.itemgetter('value'))) diff --git a/releasenotes/notes/image-update-76bd3bf24c1c1380.yaml b/releasenotes/notes/image-update-76bd3bf24c1c1380.yaml new file mode 100644 index 000000000..47599e085 --- /dev/null +++ b/releasenotes/notes/image-update-76bd3bf24c1c1380.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + When using the Image API, it is no longer possible to set arbitrary + properties, not known to the SDK, via ``image.update_image`` API.