Accept keystone session in client

This already has auth configured, and allows us to re-use the session
even with other clients. Currently this is not used by the CLI; however
this is useful when instantiating the mistral client from a python
script or from another service.

Change-Id: I4fccb4117db11c966ac3f17ec6d2a53c657378e8
This commit is contained in:
Juan Antonio Osorio Robles 2017-03-17 18:03:29 +02:00
parent ca313b0de4
commit 83b3d0d39c
5 changed files with 51 additions and 31 deletions

View File

@ -58,6 +58,7 @@ def log_request(func):
class HTTPClient(object): class HTTPClient(object):
def __init__(self, base_url, **kwargs): def __init__(self, base_url, **kwargs):
self.base_url = base_url self.base_url = base_url
self.session = kwargs.pop('session', None)
self.auth_token = kwargs.get(AUTH_TOKEN) self.auth_token = kwargs.get(AUTH_TOKEN)
self.project_id = kwargs.get(PROJECT_ID) self.project_id = kwargs.get(PROJECT_ID)
self.user_id = kwargs.get(USER_ID) self.user_id = kwargs.get(USER_ID)
@ -77,6 +78,11 @@ class HTTPClient(object):
TARGET_PROJECT_DOMAIN_NAME TARGET_PROJECT_DOMAIN_NAME
) )
if self.session:
self.crud_provider = self.session
else:
self.crud_provider = requests
if self.base_url.startswith('https'): if self.base_url.startswith('https'):
if self.cacert and not os.path.exists(self.cacert): if self.cacert and not os.path.exists(self.cacert):
raise ValueError('Unable to locate cacert file ' raise ValueError('Unable to locate cacert file '
@ -86,42 +92,47 @@ class HTTPClient(object):
LOG.warning('Client is set to not verify even though ' LOG.warning('Client is set to not verify even though '
'cacert is provided.') 'cacert is provided.')
if self.insecure: # These are already set by the session, so it's not needed
self.ssl_options['verify'] = False if not self.session:
else: if self.insecure:
if self.cacert: self.ssl_options['verify'] = False
self.ssl_options['verify'] = self.cacert
else: else:
self.ssl_options['verify'] = True if self.cacert:
self.ssl_options['verify'] = self.cacert
else:
self.ssl_options['verify'] = True
self.ssl_options['cert'] = ( self.ssl_options['cert'] = (
kwargs.get(CERT_FILE), kwargs.get(CERT_FILE),
kwargs.get(CERT_KEY) kwargs.get(CERT_KEY)
) )
@log_request @log_request
def get(self, url, headers=None): def get(self, url, headers=None):
options = self._get_request_options('get', headers) options = self._get_request_options('get', headers)
return requests.get(self.base_url + url, **options) return self.crud_provider.get(self.base_url + url, **options)
@log_request @log_request
def post(self, url, body, headers=None): def post(self, url, body, headers=None):
options = self._get_request_options('post', headers) options = self._get_request_options('post', headers)
return requests.post(self.base_url + url, body, **options) return self.crud_provider.post(self.base_url + url,
data=body, **options)
@log_request @log_request
def put(self, url, body, headers=None): def put(self, url, body, headers=None):
options = self._get_request_options('put', headers) options = self._get_request_options('put', headers)
return requests.put(self.base_url + url, body, **options) return self.crud_provider.put(self.base_url + url,
data=body, **options)
@log_request @log_request
def delete(self, url, headers=None): def delete(self, url, headers=None):
options = self._get_request_options('delete', headers) options = self._get_request_options('delete', headers)
return requests.delete(self.base_url + url, **options) return self.crud_provider.delete(self.base_url + url,
**options)
def _get_request_options(self, method, headers): def _get_request_options(self, method, headers):
headers = self._update_headers(headers) headers = self._update_headers(headers)
@ -139,14 +150,15 @@ class HTTPClient(object):
if not headers: if not headers:
headers = {} headers = {}
if self.auth_token: if not self.session:
headers['x-auth-token'] = self.auth_token if self.auth_token:
headers['x-auth-token'] = self.auth_token
if self.project_id: if self.project_id:
headers['X-Project-Id'] = self.project_id headers['X-Project-Id'] = self.project_id
if self.user_id: if self.user_id:
headers['X-User-Id'] = self.user_id headers['X-User-Id'] = self.user_id
if self.target_auth_token: if self.target_auth_token:
headers['X-Target-Auth-Token'] = self.target_auth_token headers['X-Target-Auth-Token'] = self.target_auth_token

View File

@ -39,6 +39,9 @@ _DEFAULT_MISTRAL_URL = "http://localhost:8989/v2"
class Client(object): class Client(object):
def __init__(self, auth_type='keystone', **kwargs): def __init__(self, auth_type='keystone', **kwargs):
# We get the session at this point, as some instances of session
# objects might have mutexes that can't be deep-copied.
session = kwargs.pop('session', None)
req = copy.deepcopy(kwargs) req = copy.deepcopy(kwargs)
mistral_url = req.get('mistral_url') mistral_url = req.get('mistral_url')
profile = req.get('profile') profile = req.get('profile')
@ -47,7 +50,7 @@ class Client(object):
raise RuntimeError('Mistral url should be a string.') raise RuntimeError('Mistral url should be a string.')
auth_handler = auth.get_auth_handler(auth_type) auth_handler = auth.get_auth_handler(auth_type)
auth_response = auth_handler.authenticate(req) or {} auth_response = auth_handler.authenticate(req, session=session) or {}
req.update(auth_response) req.update(auth_response)
@ -59,7 +62,8 @@ class Client(object):
if profile: if profile:
osprofiler_profiler.init(profile) osprofiler_profiler.init(profile)
http_client = httpclient.HTTPClient(mistral_url, **req) http_client = httpclient.HTTPClient(mistral_url, session=session,
**req)
# Create all resource managers. # Create all resource managers.
self.workbooks = workbooks.WorkbookManager(http_client) self.workbooks = workbooks.WorkbookManager(http_client)

View File

@ -24,7 +24,7 @@ LOG = logging.getLogger(__name__)
class KeycloakAuthHandler(auth.AuthHandler): class KeycloakAuthHandler(auth.AuthHandler):
def authenticate(self, req): def authenticate(self, req, session=None):
"""Performs authentication using Keycloak OpenID Protocol. """Performs authentication using Keycloak OpenID Protocol.
:param req: Request dict containing list of parameters required :param req: Request dict containing list of parameters required
@ -46,6 +46,8 @@ class KeycloakAuthHandler(auth.AuthHandler):
cacert: SSL certificate file (Optional). cacert: SSL certificate file (Optional).
insecure: If True, SSL certificate is not verified (Optional). insecure: If True, SSL certificate is not verified (Optional).
:param session: Keystone session object. Not used by this plugin.
""" """
if not isinstance(req, dict): if not isinstance(req, dict):
raise TypeError('The input "req" is not typeof dict.') raise TypeError('The input "req" is not typeof dict.')

View File

@ -21,7 +21,7 @@ import mistralclient.api.httpclient as api
class KeystoneAuthHandler(auth.AuthHandler): class KeystoneAuthHandler(auth.AuthHandler):
def authenticate(self, req): def authenticate(self, req, session=None):
"""Performs authentication via Keystone. """Performs authentication via Keystone.
:param req: Request dict containing list of parameters required :param req: Request dict containing list of parameters required
@ -31,6 +31,7 @@ class KeystoneAuthHandler(auth.AuthHandler):
if not isinstance(req, dict): if not isinstance(req, dict):
raise TypeError('The input "req" is not typeof dict.') raise TypeError('The input "req" is not typeof dict.')
session = session
mistral_url = req.get('mistral_url') mistral_url = req.get('mistral_url')
endpoint_type = req.get('endpoint_type', 'publicURL') endpoint_type = req.get('endpoint_type', 'publicURL')
service_type = req.get('service_type', 'workflow2') service_type = req.get('service_type', 'workflow2')
@ -76,7 +77,9 @@ class KeystoneAuthHandler(auth.AuthHandler):
auth_response = {} auth_response = {}
if auth_url: if session:
keystone = client.Client(session=session)
elif auth_url:
keystone = client.Client( keystone = client.Client(
username=username, username=username,
user_id=user_id, user_id=user_id,
@ -90,15 +93,14 @@ class KeystoneAuthHandler(auth.AuthHandler):
user_domain_name=user_domain_name, user_domain_name=user_domain_name,
project_domain_name=project_domain_name project_domain_name=project_domain_name
) )
keystone.authenticate() keystone.authenticate()
auth_response.update({ auth_response.update({
api.AUTH_TOKEN: keystone.auth_token, api.AUTH_TOKEN: keystone.auth_token,
api.PROJECT_ID: keystone.project_id, api.PROJECT_ID: keystone.project_id,
api.USER_ID: keystone.user_id, api.USER_ID: keystone.user_id,
}) })
if session or auth_url:
if not mistral_url: if not mistral_url:
try: try:
mistral_url = keystone.service_catalog.url_for( mistral_url = keystone.service_catalog.url_for(

View File

@ -196,7 +196,7 @@ class HTTPClientTest(base.BaseTestCase):
requests.post.assert_called_with( requests.post.assert_called_with(
EXPECTED_URL, EXPECTED_URL,
EXPECTED_BODY, data=EXPECTED_BODY,
**expected_options **expected_options
) )
@ -216,7 +216,7 @@ class HTTPClientTest(base.BaseTestCase):
requests.put.assert_called_with( requests.put.assert_called_with(
EXPECTED_URL, EXPECTED_URL,
EXPECTED_BODY, data=EXPECTED_BODY,
**expected_options **expected_options
) )
@ -281,7 +281,7 @@ class HTTPClientTest(base.BaseTestCase):
requests.post.assert_called_with( requests.post.assert_called_with(
EXPECTED_URL, EXPECTED_URL,
EXPECTED_BODY, data=EXPECTED_BODY,
**EXPECTED_REQ_OPTIONS **EXPECTED_REQ_OPTIONS
) )
@ -305,7 +305,7 @@ class HTTPClientTest(base.BaseTestCase):
requests.put.assert_called_with( requests.put.assert_called_with(
EXPECTED_URL, EXPECTED_URL,
EXPECTED_BODY, data=EXPECTED_BODY,
**EXPECTED_REQ_OPTIONS **EXPECTED_REQ_OPTIONS
) )