Allow None values to be returned from the API
Currently, Glance's API v2 doesn't return fields whose value is None. This, unfortunately, is wrong for a client perspective since it would create inconsistencies between calls and images due to the lack of fields in the response. The API should guarantee consistency in its replies and ensure all fields have a value, even if it's None. NOTE: This work is part of the migration to v2. It fixes inconsistencies in the API and improves the interaction between the client library and Glance. NOTE2: A follow-up patch will bump the minor API version, wait for it. ApiImpact DocImpact Closes-bug: #1398314 Change-Id: Ieaddd8a686cf7361f18cb1ee83b7887cdca22bd6
This commit is contained in:
parent
5a265dada4
commit
d17a1ed78a
@ -45,7 +45,7 @@ class Schema(object):
|
|||||||
def filter(self, obj):
|
def filter(self, obj):
|
||||||
filtered = {}
|
filtered = {}
|
||||||
for key, value in six.iteritems(obj):
|
for key, value in six.iteritems(obj):
|
||||||
if self._filter_func(self.properties, key) and value is not None:
|
if self._filter_func(self.properties, key):
|
||||||
filtered[key] = value
|
filtered[key] = value
|
||||||
return filtered
|
return filtered
|
||||||
|
|
||||||
|
@ -132,6 +132,9 @@ class TestImages(functional.FunctionalTest):
|
|||||||
u'disk_format',
|
u'disk_format',
|
||||||
u'container_format',
|
u'container_format',
|
||||||
u'owner',
|
u'owner',
|
||||||
|
u'checksum',
|
||||||
|
u'size',
|
||||||
|
u'virtual_size',
|
||||||
])
|
])
|
||||||
self.assertEqual(checked_keys, set(image.keys()))
|
self.assertEqual(checked_keys, set(image.keys()))
|
||||||
expected_image = {
|
expected_image = {
|
||||||
@ -192,6 +195,9 @@ class TestImages(functional.FunctionalTest):
|
|||||||
u'disk_format',
|
u'disk_format',
|
||||||
u'container_format',
|
u'container_format',
|
||||||
u'owner',
|
u'owner',
|
||||||
|
u'checksum',
|
||||||
|
u'size',
|
||||||
|
u'virtual_size',
|
||||||
])
|
])
|
||||||
self.assertEqual(checked_keys, set(image.keys()))
|
self.assertEqual(checked_keys, set(image.keys()))
|
||||||
expected_image = {
|
expected_image = {
|
||||||
@ -279,9 +285,9 @@ class TestImages(functional.FunctionalTest):
|
|||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
image = jsonutils.loads(response.text)
|
image = jsonutils.loads(response.text)
|
||||||
self.assertEqual(image_id, image['id'])
|
self.assertEqual(image_id, image['id'])
|
||||||
self.assertFalse('checksum' in image)
|
self.assertIsNone(image['checksum'])
|
||||||
self.assertNotIn('size', image)
|
self.assertIsNone(image['size'])
|
||||||
self.assertNotIn('virtual_size', image)
|
self.assertIsNone(image['virtual_size'])
|
||||||
self.assertEqual('bar', image['foo'])
|
self.assertEqual('bar', image['foo'])
|
||||||
self.assertFalse(image['protected'])
|
self.assertFalse(image['protected'])
|
||||||
self.assertEqual('kernel', image['type'])
|
self.assertEqual('kernel', image['type'])
|
||||||
@ -456,8 +462,8 @@ class TestImages(functional.FunctionalTest):
|
|||||||
response = requests.patch(path, headers=headers, data=data)
|
response = requests.patch(path, headers=headers, data=data)
|
||||||
self.assertEqual(200, response.status_code, response.text)
|
self.assertEqual(200, response.status_code, response.text)
|
||||||
image = jsonutils.loads(response.text)
|
image = jsonutils.loads(response.text)
|
||||||
self.assertNotIn('size', image)
|
self.assertIsNone(image['size'])
|
||||||
self.assertNotIn('virtual_size', image)
|
self.assertIsNone(image['virtual_size'])
|
||||||
self.assertEqual('queued', image['status'])
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
# Deletion should work. Deleting image-1
|
# Deletion should work. Deleting image-1
|
||||||
@ -2111,8 +2117,8 @@ class TestImages(functional.FunctionalTest):
|
|||||||
image = jsonutils.loads(response.text)
|
image = jsonutils.loads(response.text)
|
||||||
image_id = image['id']
|
image_id = image['id']
|
||||||
self.assertEqual('queued', image['status'])
|
self.assertEqual('queued', image['status'])
|
||||||
self.assertNotIn('size', image)
|
self.assertIsNone(image['size'])
|
||||||
self.assertNotIn('virtual_size', image)
|
self.assertIsNone(image['virtual_size'])
|
||||||
|
|
||||||
file_path = os.path.join(self.test_dir, 'fake_image')
|
file_path = os.path.join(self.test_dir, 'fake_image')
|
||||||
with open(file_path, 'w') as fap:
|
with open(file_path, 'w') as fap:
|
||||||
|
@ -92,6 +92,7 @@ class TestTasks(functional.FunctionalTest):
|
|||||||
u'self',
|
u'self',
|
||||||
u'status',
|
u'status',
|
||||||
u'type',
|
u'type',
|
||||||
|
u'result',
|
||||||
u'updated_at'])
|
u'updated_at'])
|
||||||
self.assertEqual(checked_keys, set(task.keys()))
|
self.assertEqual(checked_keys, set(task.keys()))
|
||||||
expected_task = {
|
expected_task = {
|
||||||
|
@ -2743,6 +2743,16 @@ class TestImagesSerializer(test_utils.BaseTestCase):
|
|||||||
'self': '/v2/images/%s' % UUID2,
|
'self': '/v2/images/%s' % UUID2,
|
||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
|
'size': None,
|
||||||
|
'name': None,
|
||||||
|
'owner': None,
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'checksum': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'virtual_size': None,
|
||||||
|
'container_format': None,
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'first': '/v2/images',
|
'first': '/v2/images',
|
||||||
@ -2847,6 +2857,15 @@ class TestImagesSerializer(test_utils.BaseTestCase):
|
|||||||
'self': '/v2/images/%s' % UUID2,
|
'self': '/v2/images/%s' % UUID2,
|
||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
|
'size': None,
|
||||||
|
'name': None,
|
||||||
|
'owner': None,
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'checksum': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'virtual_size': None,
|
||||||
|
'container_format': None,
|
||||||
}
|
}
|
||||||
response = webob.Response()
|
response = webob.Response()
|
||||||
self.serializer.show(response, self.fixtures[1])
|
self.serializer.show(response, self.fixtures[1])
|
||||||
@ -3113,6 +3132,10 @@ class TestImagesSerializerWithExtendedSchema(test_utils.BaseTestCase):
|
|||||||
'self': '/v2/images/%s' % UUID2,
|
'self': '/v2/images/%s' % UUID2,
|
||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'container_format': None,
|
||||||
}
|
}
|
||||||
response = webob.Response()
|
response = webob.Response()
|
||||||
self.serializer.show(response, self.fixture)
|
self.serializer.show(response, self.fixture)
|
||||||
@ -3137,6 +3160,10 @@ class TestImagesSerializerWithExtendedSchema(test_utils.BaseTestCase):
|
|||||||
'self': '/v2/images/%s' % UUID2,
|
'self': '/v2/images/%s' % UUID2,
|
||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'container_format': None,
|
||||||
}
|
}
|
||||||
response = webob.Response()
|
response = webob.Response()
|
||||||
self.serializer.show(response, self.fixture)
|
self.serializer.show(response, self.fixture)
|
||||||
@ -3173,6 +3200,10 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase):
|
|||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
'owner': '2c014f32-55eb-467d-8fcb-4bd706012f81',
|
'owner': '2c014f32-55eb-467d-8fcb-4bd706012f81',
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'container_format': None,
|
||||||
}
|
}
|
||||||
response = webob.Response()
|
response = webob.Response()
|
||||||
serializer.show(response, self.fixture)
|
serializer.show(response, self.fixture)
|
||||||
@ -3203,6 +3234,10 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase):
|
|||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
'owner': '2c014f32-55eb-467d-8fcb-4bd706012f81',
|
'owner': '2c014f32-55eb-467d-8fcb-4bd706012f81',
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'container_format': None,
|
||||||
}
|
}
|
||||||
response = webob.Response()
|
response = webob.Response()
|
||||||
serializer.show(response, self.fixture)
|
serializer.show(response, self.fixture)
|
||||||
@ -3227,6 +3262,10 @@ class TestImagesSerializerWithAdditionalProperties(test_utils.BaseTestCase):
|
|||||||
'self': '/v2/images/%s' % UUID2,
|
'self': '/v2/images/%s' % UUID2,
|
||||||
'file': '/v2/images/%s/file' % UUID2,
|
'file': '/v2/images/%s/file' % UUID2,
|
||||||
'schema': '/v2/schemas/image',
|
'schema': '/v2/schemas/image',
|
||||||
|
'min_ram': None,
|
||||||
|
'min_disk': None,
|
||||||
|
'disk_format': None,
|
||||||
|
'container_format': None,
|
||||||
}
|
}
|
||||||
response = webob.Response()
|
response = webob.Response()
|
||||||
serializer.show(response, self.fixture)
|
serializer.show(response, self.fixture)
|
||||||
|
Loading…
Reference in New Issue
Block a user