Expose allow parameters for URL discovery

The Discover class can fiilter API versions by experimental status, deprecated
status, and unknown status, and potentially more designations in the future.
The parameters that control this were not exposed in the Session or Adapter, so
users could not take advantage of this filtering through normal means. This
patch creates an 'allow' parameter for the Adapter that will get passed down as
keyword arguments into Discover.raw_version_data().

Now, given an unversioned endpoint like:

    $ openstack endpoint show cinder
    +--------------+----------------------------------+
    | Field        | Value                            |
    +--------------+----------------------------------+
    | adminurl     | http://192.168.122.183:8776      |
    | enabled      | True                             |
    | id           | 485107c1d92b41829c331a2dc82aaaeb |
    | internalurl  | http://192.168.122.183:8776      |
    | publicurl    | http://192.168.122.183:8776      |
    | region       | RegionOne                        |
    | service_id   | 01b4f36a173d4c59b31fc95763095373 |
    | service_name | cinder                           |
    | service_type | volume                           |
    +--------------+----------------------------------+

an Adapter can be used like this (this example would be expected to fail
since it disallows the deprecated volume V1 API):

    auth = Password(<auth_params>)
    sess = session.Session(auth=auth)
    adptr = adapter.Adapter(sess)
    adptr.get('<project-id>/volumes',
              endpoint_filter={'service_type': 'volume',
                               'interface': 'public',
                               'version': 1},
              allow={'allow_deprecated': False}))

This is inspired by an abandoned patch to keystoneclient[1] that exposed this
information as a tuple. The problem with exposing it like that is that
raw_version_data() defaults allow_deprecated to True, so including 'deprecated'
in the tuple or not including it would have the same result. Using a dict
allows us to keep the Discover interface the same.

[1] https://review.openstack.org/#/c/130159

Co-authored-by: Endre Karlson <endre.karlson@hp.com>

Change-Id: I54c29e1c2a4a2b02a3967f4ea108b8d2533616eb
Closes-bug: #1394245
This commit is contained in:
Colleen Murphy 2016-04-22 21:56:44 -07:00
parent f6e57bc706
commit 118c9629e5
5 changed files with 40 additions and 7 deletions

View File

@ -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('/<project-id>/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.

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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)