Merge "Add method to get the api major version"

This commit is contained in:
Jenkins 2017-08-24 19:31:52 +00:00 committed by Gerrit Code Review
commit 31c24c4bd9
4 changed files with 198 additions and 0 deletions

View File

@ -243,6 +243,25 @@ class Adapter(object):
return self.session.get_endpoint_data(auth or self.auth, **kwargs)
def get_api_major_version(self, auth=None, **kwargs):
"""Get the major API version as provided by the auth plugin.
:param auth: The auth plugin to use for token. Overrides the plugin on
the session. (optional)
:type auth: keystoneauth1.plugin.BaseAuthPlugin
:raises keystoneauth1.exceptions.auth_plugins.MissingAuthPlugin: if a
plugin is not available.
:return: The major version of the API of the service discovered.
:rtype: tuple or None
"""
self._set_endpoint_filter_kwargs(kwargs)
if self.endpoint_override:
kwargs['endpoint_override'] = self.endpoint_override
return self.session.get_api_major_version(auth or self.auth, **kwargs)
def invalidate(self, auth=None):
"""Invalidate an authentication plugin."""
return self.session.invalidate(auth or self.auth)

View File

@ -12,6 +12,7 @@
import abc
import base64
import functools
import hashlib
import json
import threading
@ -379,6 +380,134 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin):
allow_version_hack=allow_version_hack, **kwargs)
return endpoint_data.url if endpoint_data else None
def get_api_major_version(self, session, service_type=None, interface=None,
region_name=None, service_name=None,
version=None, allow=None,
allow_version_hack=True, skip_discovery=False,
discover_versions=False, min_version=None,
max_version=None, **kwargs):
"""Return the major API version for a service.
If a valid token is not present then a new one will be fetched using
the session and kwargs.
version, min_version and max_version can all be given either as a
string or a tuple.
Valid interface types: `public` or `publicURL`,
`internal` or `internalURL`,
`admin` or 'adminURL`
:param session: A session object that can be used for communication.
:type session: keystoneauth1.session.Session
:param string service_type: The type of service to lookup the endpoint
for. This plugin will return None (failure)
if service_type is not provided.
:param interface: Type of endpoint. Can be a single value or a list
of values. If it's a list of values, they will be
looked for in order of preference. Can also be
`keystoneauth1.plugin.AUTH_INTERFACE` to indicate
that the auth_url should be used instead of the
value in the catalog. (optional, defaults to public)
:param string region_name: The region the endpoint should exist in.
(optional)
:param string service_name: The name of the service in the catalog.
(optional)
:param version: The minimum version number required for this
endpoint. (optional)
:param dict allow: Extra filters to pass when discovering API
versions. (optional)
:param bool allow_version_hack: Allow keystoneauth to hack up catalog
URLS to support older schemes.
(optional, default True)
:param bool skip_discovery: Whether to skip version discovery even
if a version has been given. This is useful
if endpoint_override or similar has been
given and grabbing additional information
about the endpoint is not useful.
:param bool discover_versions: Whether to get version metadata from
the version discovery document even
if it's not neccessary to fulfill the
major version request. Defaults to False
because get_endpoint doesn't need
metadata. (optional, defaults to False)
:param min_version: The minimum version that is acceptable. Mutually
exclusive with version. If min_version is given
with no max_version it is as if max version is
'latest'. (optional)
:param max_version: The maximum version that is acceptable. Mutually
exclusive with version. If min_version is given
with no max_version it is as if max version is
'latest'. (optional)
:raises keystoneauth1.exceptions.http.HttpError: An error from an
invalid HTTP response.
:return: The major version of the API of the service discovered.
:rtype: tuple or None
.. note:: Implementation notes follow. Users should not need to wrap
their head around these implementation note.
`get_api_major_version` should do what is expected with the
least possible cost while still consistently returning a
value if possible.
There are many cases when major version can be satisfied
without actually calling the discovery endpoint (like when the version
is in the url). If the user has a cloud with the versioned endpoint
``https://volume.example.com/v3`` in the catalog for the
``block-storage`` service and they do::
client = adapter.Adapter(
session, service_type='block-storage', min_version=2,
max_version=3)
volume_version = client.get_api_major_version()
The version actually be returned with no api calls other than getting
the token. For that reason, :meth:`.get_api_major_version` first
calls :meth:`.get_endpoint_data` with ``discover_versions=False``.
If their catalog has an unversioned endpoint
``https://volume.example.com`` for the ``block-storage`` service
and they do this::
client = adapter.Adapter(session, service_type='block-storage')
client is now set up to "use whatever is in the catalog". Since the
url doesn't have a version, :meth:`.get_endpoint_data` with
``discover_versions=False`` will result in ``api_version=None``.
(No version was requested so it didn't need to do the round trip)
In order to find out what version the endpoint actually is, we must
make a round trip. Therefore, if ``api_version`` is ``None`` after
the first call, :meth:`.get_api_major_version` will make a second
call to :meth:`.get_endpoint_data` with ``discover_versions=True``.
"""
allow = allow or {}
# Explode `version` into min_version and max_version - everything below
# here uses the latter rather than the former.
min_version, max_version = discover._normalize_version_args(
version, min_version, max_version)
# Using functools.partial here just to reduce copy-pasta of params
get_endpoint_data = functools.partial(
self.get_endpoint_data,
session, service_type=service_type, interface=interface,
region_name=region_name, service_name=service_name,
allow=allow, min_version=min_version, max_version=max_version,
skip_discovery=skip_discovery,
allow_version_hack=allow_version_hack, **kwargs)
data = get_endpoint_data(discover_versions=discover_versions)
if (not data or not data.api_version) and not discover_versions:
# It's possible that no version was requested and the endpoint
# in the catalog has no version in the URL. A version has been
# requested, so now it's ok to run discovery.
data = get_endpoint_data(discover_versions=True)
if not data:
return None
return data.api_version
def get_user_id(self, session, **kwargs):
return self.get_access(session).user_id

View File

@ -958,6 +958,22 @@ class Session(object):
auth = self._auth_required(auth, 'determine endpoint URL')
return auth.get_endpoint_data(self, **kwargs)
def get_api_major_version(self, auth=None, **kwargs):
"""Get the major API version as provided by the auth plugin.
:param auth: The auth plugin to use for token. Overrides the plugin on
the session. (optional)
:type auth: keystoneauth1.plugin.BaseAuthPlugin
:raises keystoneauth1.exceptions.auth_plugins.MissingAuthPlugin: if a
plugin is not available.
:return: The major version of the API of the service discovered.
:rtype: tuple or None
"""
auth = self._auth_required(auth, 'determine endpoint URL')
return auth.get_api_major_version(self, **kwargs)
def get_auth_connection_params(self, auth=None, **kwargs):
"""Return auth connection params as provided by the auth plugin.

View File

@ -385,6 +385,40 @@ class CommonIdentityTests(object):
self.assertEqual(v2_compute, url_v2)
self.assertEqual(v3_compute, url_v3)
def test_discovering_version_no_discovery(self):
a = self.create_auth_plugin()
s = session.Session(auth=a)
# Grab a version that can be returned without doing discovery
# This tests that it doesn't make a discovery call because we don't
# have a reqquest mock, and this will throw an exception if it tries
version = s.get_api_major_version(
service_type='volumev2', interface='admin')
self.assertEqual((2, 0), version)
def test_discovering_version_with_discovery(self):
a = self.create_auth_plugin()
s = session.Session(auth=a)
v2_compute = self.TEST_COMPUTE_ADMIN + '/v2.0'
v3_compute = self.TEST_COMPUTE_ADMIN + '/v3'
disc = fixture.DiscoveryList(v2=False, v3=False)
disc.add_v2(v2_compute)
disc.add_v3(v3_compute)
self.stub_url('GET', [], base_url=self.TEST_COMPUTE_ADMIN, json=disc)
# This needs to do version discovery to find the version
version = s.get_api_major_version(
service_type='compute', interface='admin')
self.assertEqual((3, 0), version)
self.assertEqual(
self.requests_mock.request_history[-1].url,
self.TEST_COMPUTE_ADMIN)
def test_direct_discovering_with_relative_link(self):
# need to construct list this way for relative
disc = fixture.DiscoveryList(v2=False, v3=False)