Merge "Provide a conversion function for creating session"

This commit is contained in:
Jenkins
2014-02-07 03:38:54 +00:00
committed by Gerrit Code Review
5 changed files with 132 additions and 46 deletions

View File

@@ -14,13 +14,14 @@
from keystoneclient import discover
from keystoneclient import httpclient
from keystoneclient import session as client_session
# Using client.HTTPClient is deprecated. Use httpclient.HTTPClient instead.
HTTPClient = httpclient.HTTPClient
def Client(version=None, unstable=False, **kwargs):
def Client(version=None, unstable=False, session=None, **kwargs):
"""Factory function to create a new identity service client.
:param tuple version: The required version of the identity API. If
@@ -29,6 +30,9 @@ def Client(version=None, unstable=False, **kwargs):
at least the specified minor version. For example to
specify the 3.1 API use (3, 1).
:param bool unstable: Accept endpoints not marked as 'stable'. (optional)
:param Session session: A session object to be used for communication. If
one is not provided it will be constructed from the
provided kwargs. (optional)
:param kwargs: Additional arguments are passed through to the client
that is being created.
:returns: New keystone client object
@@ -37,6 +41,8 @@ def Client(version=None, unstable=False, **kwargs):
:raises: DiscoveryFailure if the server's response is invalid
:raises: VersionNotAvailable if a suitable client cannot be found.
"""
if not session:
session = client_session.Session.construct(kwargs)
return discover.Discover(**kwargs).create_client(version=version,
unstable=unstable)
d = discover.Discover(session=session, **kwargs)
return d.create_client(version=version, unstable=unstable)

View File

@@ -17,7 +17,7 @@ import logging
import six
from keystoneclient import exceptions
from keystoneclient import httpclient
from keystoneclient import session as client_session
from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
@@ -81,7 +81,7 @@ class _KeystoneVersion(object):
def __eq__(self, other):
return self.version == other.version and self.status == other.status
def __call__(self, **kwargs):
def create_client(self, **kwargs):
if kwargs:
client_kwargs = self.client_kwargs.copy()
client_kwargs.update(kwargs)
@@ -89,6 +89,9 @@ class _KeystoneVersion(object):
client_kwargs = self.client_kwargs
return self.client_class(**client_kwargs)
def __call__(self, **kwargs):
return self.create_client(**kwargs)
@property
def _str_ver(self):
ver = ".".join([str(v) for v in self.version])
@@ -132,28 +135,35 @@ def _normalize_version_number(version):
raise TypeError("Invalid version specified: %s" % version)
def available_versions(url, **kwargs):
def available_versions(url, session=None, **kwargs):
headers = {'Accept': 'application/json'}
client = httpclient.HTTPClient(**kwargs)
resp, body_resp = client.request(url, 'GET', headers=headers)
if not session:
session = client_session.Session.construct(kwargs)
# In the event of querying a root URL we will get back a list of
# available versions.
try:
return body_resp['versions']['values']
except (KeyError, TypeError):
pass
resp = session.get(url, headers=headers)
# Otherwise if we query an endpoint like /v2.0 then we will get back
# just the one available version.
try:
return [body_resp['version']]
except (KeyError, TypeError):
body_resp = resp.json()
except ValueError:
pass
else:
# In the event of querying a root URL we will get back a list of
# available versions.
try:
return body_resp['versions']['values']
except (KeyError, TypeError):
pass
# Otherwise if we query an endpoint like /v2.0 then we will get back
# just the one available version.
try:
return [body_resp['version']]
except KeyError:
pass
raise exceptions.DiscoveryFailure("Invalid Response - Bad version"
" data returned: %s" % body_resp)
" data returned: %s" % resp.text)
class Discover(object):
@@ -164,7 +174,7 @@ class Discover(object):
operates upon the data that was retrieved.
"""
def __init__(self, **kwargs):
def __init__(self, session=None, **kwargs):
"""Construct a new discovery object.
The connection parameters associated with this method are the same
@@ -178,6 +188,9 @@ class Discover(object):
The initialization process also queries the server.
:param Session session: A session object that will be used for
communication. Clients will also be constructed
with this session.
:param string auth_url: Identity service endpoint for authorization.
(optional)
:param string endpoint: A user-supplied endpoint URL for the identity
@@ -185,26 +198,42 @@ class Discover(object):
:param string original_ip: The original IP of the requesting user
which will be sent to identity service in a
'Forwarded' header. (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
:param boolean debug: Enables debug logging of all request and
responses to the identity service.
default False (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
:param string cacert: Path to the Privacy Enhanced Mail (PEM) file
which contains the trusted authority X.509
certificates needed to established SSL connection
with the identity service. (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
:param string key: Path to the Privacy Enhanced Mail (PEM) file which
contains the unencrypted client private key needed
to established two-way SSL connection with the
identity service. (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
:param string cert: Path to the Privacy Enhanced Mail (PEM) file which
contains the corresponding X.509 client certificate
needed to established two-way SSL connection with
the identity service. (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
:param boolean insecure: Does not perform X.509 certificate validation
when establishing SSL connection with identity
service. default: False (optional)
DEPRECATED: use the session object. This is
ignored if a session is provided.
"""
if not session:
session = client_session.Session.construct(kwargs)
kwargs['session'] = session
url = kwargs.get('endpoint') or kwargs.get('auth_url')
if not url:
raise exceptions.DiscoveryFailure('Not enough information to '
@@ -212,7 +241,7 @@ class Discover(object):
'auth_url or endpoint')
self._client_kwargs = kwargs
self._available_versions = available_versions(url, **kwargs)
self._available_versions = available_versions(url, session=session)
def _get_client_constructor_kwargs(self, kwargs_dict={}, **kwargs):
client_kwargs = self._client_kwargs.copy()
@@ -420,4 +449,4 @@ class Discover(object):
raise exceptions.VersionNotAvailable(msg)
return chosen()
return chosen.create_client()

View File

@@ -55,14 +55,13 @@ request = client_session.request
class HTTPClient(object):
def __init__(self, username=None, tenant_id=None, tenant_name=None,
password=None, auth_url=None, region_name=None, timeout=None,
endpoint=None, token=None, cacert=None, key=None,
cert=None, insecure=False, original_ip=None, debug=False,
auth_ref=None, use_keyring=False, force_new_token=False,
stale_duration=None, user_id=None, user_domain_id=None,
user_domain_name=None, domain_id=None, domain_name=None,
project_id=None, project_name=None, project_domain_id=None,
project_domain_name=None, trust_id=None, session=None):
password=None, auth_url=None, region_name=None, endpoint=None,
token=None, debug=False, auth_ref=None, use_keyring=False,
force_new_token=False, stale_duration=None, user_id=None,
user_domain_id=None, user_domain_name=None, domain_id=None,
domain_name=None, project_id=None, project_name=None,
project_domain_id=None, project_domain_name=None,
trust_id=None, session=None, **kwargs):
"""Construct a new http client
:param string user_id: User ID for authentication. (optional)
@@ -223,22 +222,7 @@ class HTTPClient(object):
self._auth_token = None
if not session:
verify = cacert or True
if insecure:
verify = False
session_cert = None
if cert and key:
session_cert = (cert, key)
elif cert:
_logger.warn("Client cert was provided without corresponding "
"key. Ignoring.")
timeout = float(timeout) if timeout is not None else None
session = client_session.Session(verify=verify,
cert=session_cert,
original_ip=original_ip,
timeout=timeout)
session = client_session.Session.construct(kwargs)
self.session = session
self.domain = ''

View File

@@ -249,3 +249,40 @@ class Session(object):
def patch(self, url, **kwargs):
return self.request(url, 'PATCH', **kwargs)
@classmethod
def construct(cls, kwargs):
"""Handles constructing a session from the older HTTPClient args as
well as the new request style arguments.
*DEPRECATED*: This function is purely for bridging the gap between
older client arguments and the session arguments that they relate to.
It is not intended to be used as a generic Session Factory.
This function purposefully modifies the input kwargs dictionary so that
the remaining kwargs dict can be reused and passed on to other
functionswithout session arguments.
"""
verify = kwargs.pop('verify', None)
cacert = kwargs.pop('cacert', None)
cert = kwargs.pop('cert', None)
key = kwargs.pop('key', None)
insecure = kwargs.pop('insecure', False)
if verify is None:
if insecure:
verify = False
else:
verify = cacert or True
if cert and key:
# passing cert and key together is deprecated in favour of the
# requests lib form of having the cert and key as a tuple
cert = (cert, key)
return cls(verify=verify, cert=cert,
timeout=kwargs.pop('timeout', None),
session=kwargs.pop('session', None),
original_ip=kwargs.pop('original_ip', None),
user_agent=kwargs.pop('user_agent', None))

View File

@@ -222,3 +222,33 @@ class RedirectTests(utils.TestCase):
for r, s in zip(req_resp.history, ses_resp.history):
self.assertEqual(r.url, s.url)
self.assertEqual(r.status_code, s.status_code)
class ConstructSessionFromArgsTests(utils.TestCase):
KEY = 'keyfile'
CERT = 'certfile'
CACERT = 'cacert-path'
def _s(self, k=None, **kwargs):
k = k or kwargs
return client_session.Session.construct(k)
def test_verify(self):
self.assertFalse(self._s(insecure=True).verify)
self.assertTrue(self._s(verify=True, insecure=True).verify)
self.assertFalse(self._s(verify=False, insecure=True).verify)
self.assertEqual(self._s(cacert=self.CACERT).verify, self.CACERT)
def test_cert(self):
tup = (self.CERT, self.KEY)
self.assertEqual(self._s(cert=tup).cert, tup)
self.assertEqual(self._s(cert=self.CERT, key=self.KEY).cert, tup)
self.assertIsNone(self._s(key=self.KEY).cert)
def test_pass_through(self):
value = 42 # only a number because timeout needs to be
for key in ['timeout', 'session', 'original_ip', 'user_agent']:
args = {key: value}
self.assertEqual(getattr(self._s(args), key), value)
self.assertNotIn(key, args)