diff --git a/doc/source/using-sessions.rst b/doc/source/using-sessions.rst index 08866c56..60fda6fb 100644 --- a/doc/source/using-sessions.rst +++ b/doc/source/using-sessions.rst @@ -158,7 +158,24 @@ The endpoint filter is a simple key-value filter and can be provided with any number of arguments. It is then up to the auth plugin to correctly use the parameters it understands. -The session object determines the URL matching the filter and append to it the +If you want to further limit your service discovery by allowing experimental +APIs or disallowing deprecated APIs, you can use the ``allow`` parameter:: + + >>> resp = session.get('//volumes', + endpoint_filter={'service_type': 'volume', + 'interface': 'public', + 'version': 1}, + allow={'allow_deprecated': False}) + +The discoverable types of endpoints that `allow` can recognize are: + +- `allow_deprecated`: Allow experimental version endpoints. + +- `allow_experimental`: Allow deprecated version endpoints. + +- `allow_unknown`: Allow endpoints with an unrecognised status. + +The session object determines the URL matching the filters and append to it the provided path and so create a valid request. If multiple URL matches are found then any one may be chosen. diff --git a/keystoneauth1/adapter.py b/keystoneauth1/adapter.py index bf0ec519..a82b9bf9 100644 --- a/keystoneauth1/adapter.py +++ b/keystoneauth1/adapter.py @@ -43,13 +43,15 @@ class Adapter(object): :param logger: A logging object to use for requests that pass through this adapter. :type logger: logging.Logger + :param dict allow: Extra filters to pass when discovering API versions. + (optional) """ @positional() def __init__(self, session, service_type=None, service_name=None, interface=None, region_name=None, endpoint_override=None, version=None, auth=None, user_agent=None, - connect_retries=None, logger=None): + connect_retries=None, logger=None, allow={}): # NOTE(jamielennox): when adding new parameters to adapter please also # add them to the adapter call in httpclient.HTTPClient.__init__ as # well as to load_adapter_from_argparse below if the argument is @@ -66,6 +68,7 @@ class Adapter(object): self.auth = auth self.connect_retries = connect_retries self.logger = logger + self.allow = allow def _set_endpoint_filter_kwargs(self, kwargs): if self.service_type: @@ -94,6 +97,8 @@ class Adapter(object): kwargs.setdefault('connect_retries', self.connect_retries) if self.logger: kwargs.setdefault('logger', self.logger) + if self.allow: + kwargs.setdefault('allow', self.allow) return self.session.request(url, method, **kwargs) diff --git a/keystoneauth1/identity/base.py b/keystoneauth1/identity/base.py index 622bd9fd..eda92a6c 100644 --- a/keystoneauth1/identity/base.py +++ b/keystoneauth1/identity/base.py @@ -158,7 +158,7 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin): def get_endpoint(self, session, service_type=None, interface=None, region_name=None, service_name=None, version=None, - **kwargs): + allow={}, **kwargs): """Return a valid endpoint for a service. If a valid token is not present then a new one will be fetched using @@ -180,6 +180,8 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin): (optional) :param tuple version: The minimum version number required for this endpoint. (optional) + :param dict allow: Extra filters to pass when discovering API + versions. (optional) :raises keystoneauth1.exceptions.http.HttpError: An error from an invalid HTTP response. @@ -237,7 +239,7 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin): 'Fallback to using that endpoint as the base url.', url) else: - url = disc.url_for(version) + url = disc.url_for(version, **allow) return url diff --git a/keystoneauth1/session.py b/keystoneauth1/session.py index d8882799..63e826c0 100644 --- a/keystoneauth1/session.py +++ b/keystoneauth1/session.py @@ -291,7 +291,7 @@ class Session(object): endpoint_filter=None, auth=None, requests_auth=None, raise_exc=True, allow_reauth=True, log=True, endpoint_override=None, connect_retries=0, logger=_logger, - **kwargs): + allow={}, **kwargs): """Send an HTTP request with the specified characteristics. Wrapper around `requests.Session.request` to handle tasks such as @@ -357,6 +357,8 @@ class Session(object): If not provided the keystoneauth1.session default logger will be used. :type logger: logging.Logger + :param dict allow: Extra filters to pass when discovering API + versions. (optional) :param kwargs: any other parameter that can be passed to :meth:`requests.Session.request` (such as `headers`). Except: @@ -398,7 +400,8 @@ class Session(object): if endpoint_override: base_url = endpoint_override % _StringFormatter(self, auth) elif endpoint_filter: - base_url = self.get_endpoint(auth, **endpoint_filter) + base_url = self.get_endpoint(auth, allow=allow, + **endpoint_filter) if not base_url: raise exceptions.EndpointNotFound() diff --git a/keystoneauth1/tests/unit/test_session.py b/keystoneauth1/tests/unit/test_session.py index 94e545be..cc6bc799 100644 --- a/keystoneauth1/tests/unit/test_session.py +++ b/keystoneauth1/tests/unit/test_session.py @@ -717,6 +717,9 @@ class AdapterTest(utils.TestCase): REGION_NAME = uuid.uuid4().hex USER_AGENT = uuid.uuid4().hex VERSION = uuid.uuid4().hex + ALLOW = {'allow_deprecated': False, + 'allow_experimental': True, + 'allow_unknown': True} TEST_URL = CalledAuthPlugin.ENDPOINT @@ -730,7 +733,8 @@ class AdapterTest(utils.TestCase): interface=self.INTERFACE, region_name=self.REGION_NAME, user_agent=self.USER_AGENT, - version=self.VERSION) + version=self.VERSION, + allow=self.ALLOW) def _verify_endpoint_called(self, adpt): self.assertEqual(self.SERVICE_TYPE, @@ -752,6 +756,8 @@ class AdapterTest(utils.TestCase): self.assertEqual(resp.text, response) self._verify_endpoint_called(adpt) + self.assertEqual(self.ALLOW, + adpt.auth.endpoint_arguments['allow']) self.assertTrue(adpt.auth.get_token_called) self.assertRequestHeaderEqual('User-Agent', self.USER_AGENT)