Allow project switching for Designate API

Contrary to other OpenStack APIs, the Designate v2 API
implements project switching based on HTTP headers, especially
x-auth-sudo-project-id and x-auth-all-projects.

This commit adds feature parity with other OpenStack APIs, i. e.
direct usage of parameters "project_id" and "all_projects" for
DNS resource list operations.

Story: 2010909
Task: 48748
Change-Id: I1cb1efbf1f243cca0c5bb6e1058d25b2ad863355
Signed-off-by: Jan Hartkopf <jhartkopf@inovex.de>
This commit is contained in:
Jan Hartkopf 2023-11-29 17:36:46 +01:00
parent 04cdd0a061
commit e7bdb02f90
6 changed files with 69 additions and 3 deletions

View File

@ -72,6 +72,26 @@ class Resource(resource.Resource):
"No %s found for %s" % (cls.__name__, name_or_id) "No %s found for %s" % (cls.__name__, name_or_id)
) )
@classmethod
def list(
cls,
session,
project_id=None,
all_projects=None,
**params,
):
headers: ty.Union[ty.Dict[str, str] | None] = (
{} if project_id or all_projects else None
)
if headers is not None:
if project_id:
headers["x-auth-sudo-project-id"] = str(project_id)
if all_projects:
headers["x-auth-all-projects"] = str(all_projects)
return super().list(session=session, headers=headers, **params)
@classmethod @classmethod
def _get_next_link(cls, uri, response, data, marker, limit, total_yielded): def _get_next_link(cls, uri, response, data, marker, limit, total_yielded):
next_link = None next_link = None

View File

@ -561,7 +561,7 @@ class Proxy(proxy.Proxy):
# ======== Zone Shares ======== # ======== Zone Shares ========
def zone_shares(self, zone, **query): def zone_shares(self, zone, **query):
"""Retrieve a generator of zone sharess """Retrieve a generator of zone shares
:param zone: The zone ID or a :param zone: The zone ID or a
:class:`~openstack.dns.v2.zone.Zone` instance :class:`~openstack.dns.v2.zone.Zone` instance

View File

@ -880,7 +880,7 @@ class Proxy(adapter.Adapter):
if resource_name in skip_resources: if resource_name in skip_resources:
self.log.debug( self.log.debug(
f"Skipping resource {resource_name} " "in project cleanup" f"Skipping resource {resource_name} in project cleanup"
) )
return True return True

View File

@ -1990,6 +1990,7 @@ class Resource(dict):
allow_unknown_params=False, allow_unknown_params=False,
*, *,
microversion=None, microversion=None,
headers=None,
**params, **params,
): ):
"""This method is a generator which yields resource objects. """This method is a generator which yields resource objects.
@ -2010,6 +2011,8 @@ class Resource(dict):
passing everything known to the server. ``False`` will result in passing everything known to the server. ``False`` will result in
validation exception when unknown query parameters are passed. validation exception when unknown query parameters are passed.
:param str microversion: API version to override the negotiated one. :param str microversion: API version to override the negotiated one.
:param dict headers: Additional headers to inject into the HTTP
request.
:param dict params: These keyword arguments are passed through the :param dict params: These keyword arguments are passed through the
:meth:`~openstack.resource.QueryParamter._transpose` method :meth:`~openstack.resource.QueryParamter._transpose` method
to find if any of them match expected query parameters to be sent to find if any of them match expected query parameters to be sent
@ -2079,6 +2082,10 @@ class Resource(dict):
return False return False
return True return True
headers_final = {"Accept": "application/json"}
if headers:
headers_final = {**headers_final, **headers}
# Track the total number of resources yielded so we can paginate # Track the total number of resources yielded so we can paginate
# swift objects # swift objects
total_yielded = 0 total_yielded = 0
@ -2086,7 +2093,7 @@ class Resource(dict):
# Copy query_params due to weird mock unittest interactions # Copy query_params due to weird mock unittest interactions
response = session.get( response = session.get(
uri, uri,
headers={"Accept": "application/json"}, headers=headers_final,
params=query_params.copy(), params=query_params.copy(),
microversion=microversion, microversion=microversion,
) )

View File

@ -18,6 +18,7 @@ from unittest import mock
from keystoneauth1 import adapter from keystoneauth1 import adapter
import requests import requests
from openstack import dns
from openstack import exceptions from openstack import exceptions
from openstack import format from openstack import format
from openstack import resource from openstack import resource
@ -2651,6 +2652,38 @@ class TestResourceActions(base.TestCase):
Test.base_path % {"something": uri_param}, Test.base_path % {"something": uri_param},
) )
def test_list_with_injected_headers(self):
mock_empty = mock.Mock()
mock_empty.status_code = 200
mock_empty.json.return_value = {"resources": []}
self.session.get.side_effect = [mock_empty]
_ = list(
self.test_class.list(self.session, headers={'X-Test': 'value'})
)
expected = {'Accept': 'application/json', 'X-Test': 'value'}
self.assertEqual(
expected, self.session.get.call_args.kwargs['headers']
)
@mock.patch.object(resource.Resource, 'list')
def test_list_dns_with_headers(self, mock_resource_list):
dns.v2._base.Resource.list(
self.session,
project_id='1234',
all_projects=True,
)
expected = {
'x-auth-sudo-project-id': '1234',
'x-auth-all-projects': 'True',
}
self.assertEqual(
expected, mock_resource_list.call_args.kwargs['headers']
)
def test_allow_invalid_list_params(self): def test_allow_invalid_list_params(self):
qp = "query param!" qp = "query param!"
qp_name = "query-param" qp_name = "query-param"

View File

@ -0,0 +1,6 @@
---
features:
- |
Add functionality to list DNS resources for a certain project only,
or for all projects, using the new `project_id` and `all_projects`
parameters.