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 discover
from keystoneclient import httpclient from keystoneclient import httpclient
from keystoneclient import session as client_session
# Using client.HTTPClient is deprecated. Use httpclient.HTTPClient instead. # Using client.HTTPClient is deprecated. Use httpclient.HTTPClient instead.
HTTPClient = httpclient.HTTPClient 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. """Factory function to create a new identity service client.
:param tuple version: The required version of the identity API. If :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 at least the specified minor version. For example to
specify the 3.1 API use (3, 1). specify the 3.1 API use (3, 1).
:param bool unstable: Accept endpoints not marked as 'stable'. (optional) :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 :param kwargs: Additional arguments are passed through to the client
that is being created. that is being created.
:returns: New keystone client object :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: DiscoveryFailure if the server's response is invalid
:raises: VersionNotAvailable if a suitable client cannot be found. :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, d = discover.Discover(session=session, **kwargs)
unstable=unstable) return d.create_client(version=version, unstable=unstable)

View File

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

View File

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

View File

@@ -249,3 +249,40 @@ class Session(object):
def patch(self, url, **kwargs): def patch(self, url, **kwargs):
return self.request(url, 'PATCH', **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): for r, s in zip(req_resp.history, ses_resp.history):
self.assertEqual(r.url, s.url) self.assertEqual(r.url, s.url)
self.assertEqual(r.status_code, s.status_code) 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)