Adds the ability to sort images with multiple keys

Extend rest api v2 with multiple sort keys support.
Example:
/v2/images/detail?sort_key=name&sort_key=size
Changed database api which now takes sort_key param as a list instead of string
python-glanceclient support will be added in separate commit

Change-Id: Ib7a6aeb2df3bc5d23fe8e070290b5bfcab00c0f5
DocImpact
Partial-Bug: 1221274
This commit is contained in:
Mike Fedosin 2014-09-11 13:08:30 +04:00
parent f9820a25d3
commit 2c3e3656b0
12 changed files with 268 additions and 58 deletions

View File

@ -88,7 +88,7 @@ class ImagesController(object):
return image
def index(self, req, marker=None, limit=None, sort_key='created_at',
def index(self, req, marker=None, limit=None, sort_key=['created_at'],
sort_dir='desc', filters=None, member_status='accepted'):
result = {}
if filters is None:
@ -102,7 +102,8 @@ class ImagesController(object):
image_repo = self.gateway.get_repo(req.context)
try:
images = image_repo.list(marker=marker, limit=limit,
sort_key=sort_key, sort_dir=sort_dir,
sort_key=sort_key,
sort_dir=sort_dir,
filters=filters,
member_status=member_status)
if len(images) != 0 and len(images) == limit:
@ -590,9 +591,15 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer):
while 'tag' in params:
tags.append(params.pop('tag').strip())
# NOTE (mfedosin) Do the same with sorting keys
# v2/images?sort_key=name&sort_key=size
sort_keys = []
while 'sort_key' in params:
sort_keys.append(self._validate_sort_key(
params.pop('sort_key').strip()))
query_params = {
'sort_key': self._validate_sort_key(
params.pop('sort_key', 'created_at')),
'sort_dir': self._validate_sort_dir(sort_dir),
'filters': self._get_filters(params),
'member_status': self._validate_member_status(member_status),
@ -607,6 +614,13 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer):
if tags:
query_params['filters']['tags'] = tags
# NOTE(mfedosin): param is still called sort_key, instead of sort_keys
# It's done because in v1 it's still a single value.
if sort_keys:
query_params['sort_key'] = sort_keys
else:
query_params['sort_key'] = ['created_at']
return query_params

View File

@ -75,7 +75,7 @@ class ImageRepo(object):
image = self._format_image_from_db(db_api_image, tags)
return ImageProxy(image, self.context, self.db_api)
def list(self, marker=None, limit=None, sort_key='created_at',
def list(self, marker=None, limit=None, sort_key=['created_at'],
sort_dir='desc', filters=None, member_status='accepted'):
db_api_images = self.db_api.image_get_all(
self.context, filters=filters, marker=marker, limit=limit,

View File

@ -118,7 +118,7 @@ def is_image_visible(context, image, status=None):
@_get_client
def image_get_all(client, filters=None, marker=None, limit=None,
sort_key='created_at', sort_dir='desc',
sort_key=['created_at'], sort_dir='desc',
member_status='accepted', is_public=None,
admin_as_user=False, return_tag=False):
"""

View File

@ -15,6 +15,7 @@
# under the License.
import copy
import datetime
import functools
import uuid
@ -337,13 +338,29 @@ def _do_pagination(context, images, marker, limit, show_deleted,
def _sort_images(images, sort_key, sort_dir):
reverse = False
if images and not (sort_key in images[0]):
raise exception.InvalidSortKey()
keyfn = lambda x: (x[sort_key] if x[sort_key] is not None else '',
x['created_at'], x['id'])
reverse = sort_dir == 'desc'
images.sort(key=keyfn, reverse=reverse)
for key in ['created_at', 'id']:
if key not in sort_key:
sort_key.append(key)
for key in sort_key:
if images and not (key in images[0]):
raise exception.InvalidSortKey()
def sort_func(element):
keys = []
for key in sort_key:
if element[key] is not None:
keys.append(element[key])
else:
if key in ['created_at', 'updated_at', 'deleted_at']:
keys.append(datetime.datetime.min)
else:
keys.append('')
return tuple(keys)
images.sort(key=sort_func, reverse=reverse)
return images
@ -375,7 +392,7 @@ def image_get(context, image_id, session=None, force_show_deleted=False):
@log_call
def image_get_all(context, filters=None, marker=None, limit=None,
sort_key='created_at', sort_dir='desc',
sort_key=['created_at'], sort_dir='desc',
member_status='accepted', is_public=None,
admin_as_user=False, return_tag=False):
filters = filters or {}
@ -975,7 +992,6 @@ def _sort_tasks(tasks, sort_key, sort_dir):
x['created_at'], x['id'])
reverse = sort_dir == 'desc'
tasks.sort(key=keyfn, reverse=reverse)
return tasks

View File

@ -524,7 +524,7 @@ def _select_images_query(context, image_conditions, admin_as_user,
def image_get_all(context, filters=None, marker=None, limit=None,
sort_key='created_at', sort_dir='desc',
sort_key=['created_at'], sort_dir='desc',
member_status='accepted', is_public=None,
admin_as_user=False, return_tag=False):
"""
@ -535,7 +535,7 @@ def image_get_all(context, filters=None, marker=None, limit=None,
filters on the image properties attribute
:param marker: image id after which to start page
:param limit: maximum number of images to return
:param sort_key: image attribute by which results should be sorted
:param sort_key: list of image attributes by which results should be sorted
:param sort_dir: direction in which results should be sorted (asc, desc)
:param member_status: only return shared images that have this membership
status
@ -585,11 +585,12 @@ def image_get_all(context, filters=None, marker=None, limit=None,
marker,
force_show_deleted=showing_deleted)
sort_keys = ['created_at', 'id']
sort_keys.insert(0, sort_key) if sort_key not in sort_keys else sort_keys
for key in ['created_at', 'id']:
if key not in sort_key:
sort_key.append(key)
query = _paginate_query(query, models.Image, limit,
sort_keys,
sort_key,
marker=marker_image,
sort_dir=sort_dir)

View File

@ -195,7 +195,7 @@ class Controller(object):
params = {
'filters': self._get_filters(req),
'limit': self._get_limit(req),
'sort_key': self._get_sort_key(req),
'sort_key': [self._get_sort_key(req)],
'sort_dir': self._get_sort_dir(req),
'marker': self._get_marker(req),
}
@ -283,7 +283,7 @@ class Controller(object):
def _get_sort_key(self, req):
"""Parse a sort key query param from the request object."""
sort_key = req.params.get('sort_key', None)
sort_key = req.params.get('sort_key', 'created_at')
if sort_key is not None and sort_key not in SUPPORTED_SORT_KEYS:
_keys = ', '.join(SUPPORTED_SORT_KEYS)
msg = _("Unsupported sort_key. Acceptable values: %s") % (_keys,)

View File

@ -538,7 +538,7 @@ class DriverTests(object):
'owner': TENANT1})
images = self.db_api.image_get_all(ctxt1, marker=UUIDX,
sort_key='name',
sort_key=['name'],
sort_dir='desc')
image_ids = [image['id'] for image in images]
expected = []
@ -560,7 +560,7 @@ class DriverTests(object):
'owner': TENANT1})
images = self.db_api.image_get_all(ctxt1, marker=UUIDX,
sort_key='disk_format',
sort_key=['disk_format'],
sort_dir='desc')
image_ids = [image['id'] for image in images]
expected = []
@ -582,7 +582,7 @@ class DriverTests(object):
'owner': TENANT1})
images = self.db_api.image_get_all(ctxt1, marker=UUIDX,
sort_key='container_format',
sort_key=['container_format'],
sort_dir='desc')
image_ids = [image['id'] for image in images]
expected = []
@ -604,7 +604,7 @@ class DriverTests(object):
'owner': TENANT1})
images = self.db_api.image_get_all(ctxt1, marker=UUIDX,
sort_key='name',
sort_key=['name'],
sort_dir='asc')
image_ids = [image['id'] for image in images]
expected = [UUID3, UUID2, UUID1]
@ -626,7 +626,7 @@ class DriverTests(object):
'owner': TENANT1})
images = self.db_api.image_get_all(ctxt1, marker=UUIDX,
sort_key='disk_format',
sort_key=['disk_format'],
sort_dir='asc')
image_ids = [image['id'] for image in images]
expected = [UUID3, UUID2, UUID1]
@ -648,7 +648,7 @@ class DriverTests(object):
'owner': TENANT1})
images = self.db_api.image_get_all(ctxt1, marker=UUIDX,
sort_key='container_format',
sort_key=['container_format'],
sort_dir='asc')
image_ids = [image['id'] for image in images]
expected = [UUID3, UUID2, UUID1]
@ -814,7 +814,7 @@ class DriverTests(object):
def test_image_get_all_invalid_sort_key(self):
self.assertRaises(exception.InvalidSortKey, self.db_api.image_get_all,
self.context, sort_key='blah')
self.context, sort_key=['blah'])
def test_image_get_all_limit_marker(self):
images = self.db_api.image_get_all(self.context, limit=2)

View File

@ -113,7 +113,7 @@ class TestSqlAlchemyDBDataIntegrity(base.TestDriver):
self.stubs.Set(self.db_api, '_paginate_query',
fake_paginate_query)
self.db_api.image_get_all(self.context, sort_key='created_at')
self.db_api.image_get_all(self.context, sort_key=['created_at'])
def test_paginate_non_redundant_sort_keys(self):
original_method = self.db_api._paginate_query
@ -126,7 +126,7 @@ class TestSqlAlchemyDBDataIntegrity(base.TestDriver):
self.stubs.Set(self.db_api, '_paginate_query',
fake_paginate_query)
self.db_api.image_get_all(self.context, sort_key='name')
self.db_api.image_get_all(self.context, sort_key=['name'])
class TestSqlAlchemyTask(base.TaskTests):

View File

@ -302,10 +302,29 @@ class TestImageRepo(test_utils.BaseTestCase):
self.assertEqual(set([UUID1, UUID3]), image_ids)
def test_sorted_list(self):
images = self.image_repo.list(sort_key='size', sort_dir='asc')
images = self.image_repo.list(sort_key=['size'], sort_dir='asc')
image_ids = [i.image_id for i in images]
self.assertEqual([UUID1, UUID2, UUID3], image_ids)
def test_sorted_list_with_multiple_keys(self):
temp_id = 'd80a1a6c-bd1f-41c5-90ee-81afedb1d58d'
image = _db_fixture(temp_id, owner=TENANT1, checksum=CHECKSUM,
name='1', size=1024,
is_public=True, status='active',
locations=[{'url': UUID1_LOCATION,
'metadata': UUID1_LOCATION_METADATA,
'status': 'active'}])
self.db.image_create(None, image)
images = self.image_repo.list(sort_key=['name', 'size'],
sort_dir='asc')
image_ids = [i.image_id for i in images]
self.assertEqual([UUID1, temp_id, UUID2, UUID3], image_ids)
images = self.image_repo.list(sort_key=['size', 'name'],
sort_dir='asc')
image_ids = [i.image_id for i in images]
self.assertEqual([UUID1, UUID2, temp_id, UUID3], image_ids)
def test_add_image(self):
image = self.image_factory.new_image(name='added image')
self.assertEqual(image.updated_at, image.created_at)

View File

@ -214,7 +214,8 @@ class TestImagesController(base.IsolatedUnitTest):
self.config(limit_param_default=1, api_limit_max=3)
request = unit_test_utils.get_fake_request()
output = self.controller.index(request, marker=UUID3, limit=1,
sort_key='created_at', sort_dir='desc')
sort_key=['created_at'],
sort_dir='desc')
self.assertEqual(1, len(output['images']))
actual = set([image.image_id for image in output['images']])
expected = set([UUID2])
@ -423,7 +424,20 @@ class TestImagesController(base.IsolatedUnitTest):
def test_index_with_sort_key(self):
path = '/images'
request = unit_test_utils.get_fake_request(path)
output = self.controller.index(request, sort_key='created_at', limit=3)
output = self.controller.index(request, sort_key=['created_at'],
limit=3)
actual = [image.image_id for image in output['images']]
self.assertEqual(3, len(actual))
self.assertEqual(UUID3, actual[0])
self.assertEqual(UUID2, actual[1])
self.assertEqual(UUID1, actual[2])
def test_index_with_multiple_sort_keys(self):
path = '/images'
request = unit_test_utils.get_fake_request(path)
output = self.controller.index(request,
sort_key=['created_at', 'name'],
limit=3)
actual = [image.image_id for image in output['images']]
self.assertEqual(3, len(actual))
self.assertEqual(UUID3, actual[0])
@ -441,7 +455,7 @@ class TestImagesController(base.IsolatedUnitTest):
path = '/images'
request = unit_test_utils.get_fake_request(path)
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, request, sort_key='foo')
self.controller.index, request, sort_key=['foo'])
def test_index_zero_images(self):
self.db.reset()
@ -2433,7 +2447,7 @@ class TestImagesDeserializer(test_utils.BaseTestCase):
request = unit_test_utils.get_fake_request(path)
expected = {'limit': 1,
'marker': marker,
'sort_key': 'created_at',
'sort_key': ['created_at'],
'sort_dir': 'desc',
'member_status': 'pending',
'filters': {}}
@ -2481,7 +2495,7 @@ class TestImagesDeserializer(test_utils.BaseTestCase):
def test_index_zero_limit(self):
request = unit_test_utils.get_fake_request('/images?limit=0')
expected = {'limit': 0,
'sort_key': 'created_at',
'sort_key': ['created_at'],
'member_status': 'accepted',
'sort_dir': 'desc',
'filters': {}}
@ -2525,18 +2539,38 @@ class TestImagesDeserializer(test_utils.BaseTestCase):
request = unit_test_utils.get_fake_request('/images?sort_key=id')
output = self.deserializer.index(request)
expected = {
'sort_key': 'id',
'sort_key': ['id'],
'sort_dir': 'desc',
'member_status': 'accepted',
'filters': {}
}
self.assertEqual(expected, output)
def test_index_multiple_sort_keys(self):
request = unit_test_utils.get_fake_request('/images?'
'sort_key=name&'
'sort_key=size')
output = self.deserializer.index(request)
expected = {
'sort_key': ['name', 'size'],
'sort_dir': 'desc',
'member_status': 'accepted',
'filters': {}
}
self.assertEqual(expected, output)
def test_index_invalid_multiple_sort_keys(self):
request = unit_test_utils.get_fake_request('/images?'
'sort_key=name&'
'sort_key=blah')
self.assertRaises(webob.exc.HTTPBadRequest,
self.deserializer.index, request)
def test_index_sort_dir_asc(self):
request = unit_test_utils.get_fake_request('/images?sort_dir=asc')
output = self.deserializer.index(request)
expected = {
'sort_key': 'created_at',
'sort_key': ['created_at'],
'sort_dir': 'asc',
'member_status': 'accepted',
'filters': {}}

View File

@ -264,7 +264,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'marker': UUID3, 'sort_key': 'name',
'kwargs': {'marker': UUID3, 'sort_key': ['name'],
'sort_dir': 'asc'},
}]
req.body = jsonutils.dumps(cmd)
@ -297,7 +297,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'marker': UUID3, 'sort_key': 'name',
'kwargs': {'marker': UUID3, 'sort_key': ['name'],
'sort_dir': 'desc'},
}]
req.body = jsonutils.dumps(cmd)
@ -330,7 +330,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'marker': UUID3, 'sort_key': 'disk_format',
'kwargs': {'marker': UUID3, 'sort_key': ['disk_format'],
'sort_dir': 'asc'},
}]
req.body = jsonutils.dumps(cmd)
@ -363,7 +363,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'marker': UUID3, 'sort_key': 'disk_format',
'kwargs': {'marker': UUID3, 'sort_key': ['disk_format'],
'sort_dir': 'desc'},
}]
req.body = jsonutils.dumps(cmd)
@ -396,7 +396,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'marker': UUID3, 'sort_key': 'container_format',
'kwargs': {'marker': UUID3, 'sort_key': ['container_format'],
'sort_dir': 'asc'},
}]
req.body = jsonutils.dumps(cmd)
@ -429,7 +429,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'marker': UUID3, 'sort_key': 'container_format',
'kwargs': {'marker': UUID3, 'sort_key': ['container_format'],
'sort_dir': 'desc'},
}]
req.body = jsonutils.dumps(cmd)
@ -831,7 +831,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'name', 'sort_dir': 'asc'}
'kwargs': {'sort_key': ['name'], 'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
res = req.get_response(self.api)
@ -883,7 +883,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'status', 'sort_dir': 'asc'}
'kwargs': {'sort_key': ['status'], 'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
res = req.get_response(self.api)
@ -934,7 +934,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'disk_format', 'sort_dir': 'asc'}
'kwargs': {'sort_key': ['disk_format'], 'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
res = req.get_response(self.api)
@ -985,7 +985,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'container_format',
'kwargs': {'sort_key': ['container_format'],
'sort_dir': 'desc'}
}]
req.body = jsonutils.dumps(cmd)
@ -1033,7 +1033,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'size',
'kwargs': {'sort_key': ['size'],
'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
@ -1088,7 +1088,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'created_at',
'kwargs': {'sort_key': ['created_at'],
'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
@ -1143,7 +1143,7 @@ class TestRegistryRPC(base.IsolatedUnitTest):
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': 'updated_at',
'kwargs': {'sort_key': ['updated_at'],
'sort_dir': 'desc'}
}]
req.body = jsonutils.dumps(cmd)
@ -1158,6 +1158,94 @@ class TestRegistryRPC(base.IsolatedUnitTest):
self.assertEqual(UUID2, images[2]['id'])
self.assertEqual(UUID1, images[3]['id'])
def test_get_index_sort_multiple_keys(self):
"""
Tests that the registry API returns list of
public images sorted by name-size and size-name.
"""
uuid4_time = timeutils.utcnow() + datetime.timedelta(seconds=10)
uuid3_time = uuid4_time + datetime.timedelta(seconds=5)
UUID3 = _gen_uuid()
extra_fixture = {'id': UUID3,
'status': 'active',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'asdf',
'size': 19,
'checksum': None,
'created_at': None,
'updated_at': uuid3_time}
db_api.image_create(self.context, extra_fixture)
UUID4 = _gen_uuid()
extra_fixture = {'id': UUID4,
'status': 'active',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'xyz',
'size': 20,
'checksum': None,
'created_at': None,
'updated_at': uuid4_time}
db_api.image_create(self.context, extra_fixture)
UUID5 = _gen_uuid()
extra_fixture = {'id': UUID5,
'status': 'active',
'is_public': True,
'disk_format': 'vhd',
'container_format': 'ovf',
'name': 'asdf',
'size': 20,
'checksum': None,
'created_at': None,
'updated_at': uuid4_time}
db_api.image_create(self.context, extra_fixture)
req = webob.Request.blank('/rpc')
req.method = "POST"
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': ['name', 'size'],
'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
res = req.get_response(self.api)
self.assertEqual(200, res.status_int)
res_dict = jsonutils.loads(res.body)[0]
images = res_dict
self.assertEqual(5, len(images))
self.assertEqual(UUID3, images[0]['id'])
self.assertEqual(UUID5, images[1]['id'])
self.assertEqual(UUID1, images[2]['id'])
self.assertEqual(UUID2, images[3]['id'])
self.assertEqual(UUID4, images[4]['id'])
cmd = [{
'command': 'image_get_all',
'kwargs': {'sort_key': ['size', 'name'],
'sort_dir': 'asc'}
}]
req.body = jsonutils.dumps(cmd)
res = req.get_response(self.api)
self.assertEqual(200, res.status_int)
res_dict = jsonutils.loads(res.body)[0]
images = res_dict
self.assertEqual(5, len(images))
self.assertEqual(UUID1, images[0]['id'])
self.assertEqual(UUID3, images[1]['id'])
self.assertEqual(UUID2, images[2]['id'])
self.assertEqual(UUID5, images[3]['id'])
self.assertEqual(UUID4, images[4]['id'])
def test_create_image(self):
"""Tests that the registry API creates the image"""
fixture = {'name': 'fake public image',

View File

@ -116,7 +116,8 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='name', sort_dir='asc')
images = self.client.image_get_all(sort_key=['name'],
sort_dir='asc')
self.assertEqualImages(images, (UUID3, UUID1, UUID2, UUID4),
unjsonify=False)
@ -140,7 +141,8 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='status', sort_dir='desc')
images = self.client.image_get_all(sort_key=['status'],
sort_dir='desc')
self.assertEqualImages(images, (UUID3, UUID4, UUID2, UUID1),
unjsonify=False)
@ -163,7 +165,7 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='disk_format',
images = self.client.image_get_all(sort_key=['disk_format'],
sort_dir='asc')
self.assertEqualImages(images, (UUID1, UUID3, UUID4, UUID2),
@ -188,7 +190,7 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='container_format',
images = self.client.image_get_all(sort_key=['container_format'],
sort_dir='desc')
self.assertEqualImages(images, (UUID2, UUID4, UUID3, UUID1),
@ -215,7 +217,7 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='size', sort_dir='asc')
images = self.client.image_get_all(sort_key=['size'], sort_dir='asc')
self.assertEqualImages(images, (UUID4, UUID1, UUID2, UUID3),
unjsonify=False)
@ -238,7 +240,7 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='created_at',
images = self.client.image_get_all(sort_key=['created_at'],
sort_dir='asc')
self.assertEqualImages(images, (UUID1, UUID2, UUID4, UUID3),
@ -264,12 +266,48 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key='updated_at',
images = self.client.image_get_all(sort_key=['updated_at'],
sort_dir='desc')
self.assertEqualImages(images, (UUID3, UUID4, UUID2, UUID1),
unjsonify=False)
def test_get_image_details_sort_multiple_keys(self):
"""
Tests that a detailed call returns list of
public images sorted alphabetically by name-size and
size-name in ascending order.
"""
UUID3 = _gen_uuid()
extra_fixture = self.get_fixture(id=UUID3, name='asdf',
size=19)
db_api.image_create(self.context, extra_fixture)
UUID4 = _gen_uuid()
extra_fixture = self.get_fixture(id=UUID4, name='xyz',
size=20)
db_api.image_create(self.context, extra_fixture)
UUID5 = _gen_uuid()
extra_fixture = self.get_fixture(id=UUID5, name='asdf',
size=20)
db_api.image_create(self.context, extra_fixture)
images = self.client.image_get_all(sort_key=['name', 'size'],
sort_dir='asc')
self.assertEqualImages(images, (UUID3, UUID5, UUID1, UUID2, UUID4),
unjsonify=False)
images = self.client.image_get_all(sort_key=['size', 'name'],
sort_dir='asc')
self.assertEqualImages(images, (UUID1, UUID3, UUID2, UUID5, UUID4),
unjsonify=False)
def test_image_get_index_marker(self):
"""Test correct set of images returned with marker param."""
uuid4_time = timeutils.utcnow() + datetime.timedelta(seconds=10)