Remove next pagination link for floating IP list

First of all the floating IP list API does not
support pagination as it doesn't pass any kind
of limit or marker down to the central API so
we simply get all the floating IPs returned.

But the floating IP API still accepts the limit
and marker query parameters, if limit is not given
we default to the default_limit_v2 config option
that has default value of 20.

If a project has exactly 20 floating IPs we hit
the bug documented in [1] where the returned list
is exactly the size of the limit used.

In the openstacksdk [2] this raises an exception
when the last item is the marker in the next url.

In the designateclient OSC plugin [3] this will
loop forever and generate HTTP requests until
stopped.

We resolve this by making the next link configurable
in the options for the object adapters but default
`next` to True to keep backward compatibility and
then set this to false for the floating IP list.

We add that since we cannot set the `links` option
to False as that would change the returned API
response body by removing links from the body
entirely.

[1] 595894be22/designate/objects/adapters/api_v2/base.py (L120)
[2] 79fbbbabff/openstack/resource.py (L2080)
[3] 490b0d6e76/designateclient/v2/base.py (L36)

Closes-Bug: #2106112
Change-Id: I60ed37719fd0411159b8ec4f68ca7b001eb8a7be
Signed-off-by: Tobias Urdin <tobias.urdin@binero.com>
This commit is contained in:
Tobias Urdin 2025-04-03 10:06:37 +02:00
parent b448b52f8f
commit e80666a1dd
4 changed files with 43 additions and 3 deletions

View file

@ -37,7 +37,8 @@ class APIv2Adapter(base.DesignateAdapter):
if (cls.MODIFICATIONS['options'].get('links', True) and
'request' in kwargs):
r_list['links'] = cls._get_collection_links(
list_objects, kwargs['request']
list_objects, kwargs['request'],
next=cls.MODIFICATIONS['options'].get('next', True)
)
# Check if we should include metadata
if isinstance(list_objects, ovoobj_base.PagedListObjectMixin):
@ -96,11 +97,14 @@ class APIv2Adapter(base.DesignateAdapter):
item_path += '/' + part
@classmethod
def _get_collection_links(cls, item_list, request):
def _get_collection_links(cls, item_list, request, next=True):
links = {
'self': cls._get_collection_href(request)
}
if not next:
return links
params = request.GET
# defined in etc/designate/designate.conf.sample

View file

@ -62,6 +62,9 @@ class FloatingIPListAPIv2Adapter(base.APIv2Adapter):
MODIFICATIONS = {
'options': {
'links': True,
# Floating IP does not support pagination so
# dont generate any next url in collection links.
'next': False,
'resource_name': 'floatingip',
'collection_name': 'floatingips',
}

View file

@ -414,3 +414,27 @@ class ZoneTransferRequestAPIv2AdapterTest(oslotest.base.BaseTestCase):
)
self.assertNotIn('target_project_id', render_object)
class FloatingIPListAPIv2AdapterTest(oslotest.base.BaseTestCase):
def test_floatingip_no_next_collection_link(self):
"""Test that we dont provide a next collection link."""
mock_request = mock.Mock()
mock_request.GET = {'limit': 20}
mock_request.host_url = 'http://192.0.2.1'
mock_request.path = '/v2/reverse/floatingips'
obj = {'ptrdname': None, 'ttl': None, 'description': None}
items = [
adapters.FloatingIPListAPIv2Adapter.parse(
obj, objects.FloatingIP()) for _ in range(0, 20)]
res = adapters.FloatingIPListAPIv2Adapter.render_list(
items, request=mock_request)
expected_links = {
'self': 'http://192.0.2.1/v2/reverse/floatingips?limit=20'
}
# Test that we return all floating ips and that we dont
# return any next key in our collection links.
self.assertEqual(20, len(res['floatingips']))
self.assertEqual(expected_links, res['links'])

View file

@ -0,0 +1,9 @@
---
fixes:
- |
Fixed collection links for floating IP API endpoint returning a ``next``
key pointing to itself when the exact amount of floating IPs in a project
was the same as config ``default_limit_v2`` that would cause some clients
to enter an endless loop.
See `bug #2106112 <https://bugs.launchpad.net/designate/+bug/2106112>`_.