diff --git a/functionaltests/run_tests.sh b/functionaltests/run_tests.sh index d616d36d..e27796c0 100755 --- a/functionaltests/run_tests.sh +++ b/functionaltests/run_tests.sh @@ -31,6 +31,8 @@ source openrc alt_demo alt_demo export OS_ALT_USERNAME=${OS_USERNAME} export OS_ALT_TENANT_NAME=${OS_TENANT_NAME} +export OS_ALT_USER_DOMAIN_NAME=${OS_USER_DOMAIN_NAME} +export OS_ALT_PROJECT_DOMAIN__NAME=${OS_PROJECT_DOMAIN_NAME} export OS_ALT_PASSWORD=${OS_PASSWORD} # Get admin credentials. @@ -46,10 +48,14 @@ uri = $OS_AUTH_URL user = $OS_USERNAME tenant = $OS_TENANT_NAME pass = $OS_PASSWORD +user_domain = $OS_USER_DOMAIN_NAME +project_domain = $OS_PROJECT_DOMAIN_NAME [demo] user = $OS_ALT_USERNAME tenant = $OS_ALT_TENANT_NAME pass = $OS_ALT_PASSWORD +user_domain = $OS_ALT_USER_DOMAIN_NAME +project_domain = $OS_ALT_PROJECT_DOMAIN_NAME EOF cd $MISTRALCLIENT_DIR diff --git a/mistralclient/api/base.py b/mistralclient/api/base.py index d69f8819..c2b71b53 100644 --- a/mistralclient/api/base.py +++ b/mistralclient/api/base.py @@ -15,6 +15,8 @@ import copy import json +from keystoneauth1 import exceptions + class Resource(object): resource_name = 'Something' @@ -89,7 +91,10 @@ class ResourceManager(object): if dump_json: data = json.dumps(data) - resp = self.http_client.post(url, data) + try: + resp = self.http_client.post(url, data) + except exceptions.HttpError as ex: + self._raise_api_exception(ex.response) if resp.status_code != 201: self._raise_api_exception(resp) @@ -100,7 +105,10 @@ class ResourceManager(object): if dump_json: data = json.dumps(data) - resp = self.http_client.put(url, data) + try: + resp = self.http_client.put(url, data) + except exceptions.HttpError as ex: + self._raise_api_exception(ex.response) if resp.status_code != 200: self._raise_api_exception(resp) @@ -108,7 +116,10 @@ class ResourceManager(object): return self.resource_class(self, extract_json(resp, response_key)) def _list(self, url, response_key=None): - resp = self.http_client.get(url) + try: + resp = self.http_client.get(url) + except exceptions.HttpError as ex: + self._raise_api_exception(ex.response) if resp.status_code != 200: self._raise_api_exception(resp) @@ -117,7 +128,10 @@ class ResourceManager(object): for resource_data in extract_json(resp, response_key)] def _get(self, url, response_key=None): - resp = self.http_client.get(url) + try: + resp = self.http_client.get(url) + except exceptions.HttpError as ex: + self._raise_api_exception(ex.response) if resp.status_code == 200: return self.resource_class(self, extract_json(resp, response_key)) @@ -125,7 +139,10 @@ class ResourceManager(object): self._raise_api_exception(resp) def _delete(self, url): - resp = self.http_client.delete(url) + try: + resp = self.http_client.delete(url) + except exceptions.HttpError as ex: + self._raise_api_exception(ex.response) if resp.status_code != 204: self._raise_api_exception(resp) diff --git a/mistralclient/api/httpclient.py b/mistralclient/api/httpclient.py index f355cca6..f0254fe9 100644 --- a/mistralclient/api/httpclient.py +++ b/mistralclient/api/httpclient.py @@ -15,15 +15,16 @@ import base64 import copy +import logging import os from oslo_utils import importutils -import requests -import logging +import requests AUTH_TOKEN = 'auth_token' +SESSION = 'session' CACERT = 'cacert' CERT_FILE = 'cert' CERT_KEY = 'key' @@ -33,6 +34,7 @@ USER_ID = 'user_id' REGION_NAME = 'region_name' TARGET_AUTH_TOKEN = 'target_auth_token' +TARGET_SESSION = 'target_session' TARGET_AUTH_URI = 'target_auth_url' TARGET_PROJECT_ID = 'target_project_id' TARGET_USER_ID = 'target_user_id' @@ -59,7 +61,9 @@ def log_request(func): class HTTPClient(object): def __init__(self, base_url, **kwargs): self.base_url = base_url - self.session = kwargs.pop('session', None) + self.session = kwargs.get('session') + if not self.session: + self.session = requests.Session() self.auth_token = kwargs.get(AUTH_TOKEN) self.project_id = kwargs.get(PROJECT_ID) self.user_id = kwargs.get(USER_ID) @@ -68,6 +72,7 @@ class HTTPClient(object): self.region_name = kwargs.get(REGION_NAME) self.ssl_options = {} + self.target_session = kwargs.get(TARGET_SESSION) self.target_auth_token = kwargs.get(TARGET_AUTH_TOKEN) self.target_auth_uri = kwargs.get(TARGET_AUTH_URI) self.target_user_id = kwargs.get(TARGET_USER_ID) @@ -80,11 +85,6 @@ class HTTPClient(object): 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.cacert and not os.path.exists(self.cacert): raise ValueError('Unable to locate cacert file ' @@ -94,47 +94,42 @@ class HTTPClient(object): LOG.warning('Client is set to not verify even though ' 'cacert is provided.') - # These are already set by the session, so it's not needed - if not self.session: - if self.insecure: - self.ssl_options['verify'] = False + if self.insecure: + self.ssl_options['verify'] = False + else: + if self.cacert: + self.ssl_options['verify'] = self.cacert else: - if self.cacert: - self.ssl_options['verify'] = self.cacert - else: - self.ssl_options['verify'] = True + self.ssl_options['verify'] = True - self.ssl_options['cert'] = ( - kwargs.get(CERT_FILE), - kwargs.get(CERT_KEY) - ) + self.ssl_options['cert'] = ( + kwargs.get(CERT_FILE), + kwargs.get(CERT_KEY) + ) @log_request def get(self, url, headers=None): options = self._get_request_options('get', headers) - return self.crud_provider.get(self.base_url + url, **options) + return self.session.get(self.base_url + url, **options) @log_request def post(self, url, body, headers=None): options = self._get_request_options('post', headers) - return self.crud_provider.post(self.base_url + url, - data=body, **options) + return self.session.post(self.base_url + url, data=body, **options) @log_request def put(self, url, body, headers=None): options = self._get_request_options('put', headers) - return self.crud_provider.put(self.base_url + url, - data=body, **options) + return self.session.put(self.base_url + url, data=body, **options) @log_request def delete(self, url, headers=None): options = self._get_request_options('delete', headers) - return self.crud_provider.delete(self.base_url + url, - **options) + return self.session.delete(self.base_url + url, **options) def _get_request_options(self, method, headers): headers = self._update_headers(headers) @@ -152,9 +147,9 @@ class HTTPClient(object): if not headers: headers = {} - if not self.session: + if isinstance(self.session, requests.Session): if self.auth_token: - headers['x-auth-token'] = self.auth_token + headers['X-Auth-Token'] = self.auth_token if self.project_id: headers['X-Project-Id'] = self.project_id diff --git a/mistralclient/api/v2/client.py b/mistralclient/api/v2/client.py index c7dd0f06..123e88b5 100644 --- a/mistralclient/api/v2/client.py +++ b/mistralclient/api/v2/client.py @@ -54,6 +54,10 @@ class Client(object): auth_handler = auth.get_auth_handler(auth_type) auth_response = auth_handler.authenticate(req, session=session) or {} + # If the session was None and we're using keystone auth, it will be + # created by the auth_handler. + session = auth_response.pop('session', None) + req.update(auth_response) mistral_url = auth_response.get('mistral_url') or mistral_url diff --git a/mistralclient/auth/keystone.py b/mistralclient/auth/keystone.py index 2d6f98f9..be105449 100644 --- a/mistralclient/auth/keystone.py +++ b/mistralclient/auth/keystone.py @@ -12,14 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from keystoneclient import client -from mistralclient import auth +import logging + +import keystoneauth1.identity.generic as auth_plugin +from keystoneauth1 import session as ks_session +import mistralclient.api.httpclient as api +from mistralclient import auth as mistral_auth from oslo_serialization import jsonutils -import mistralclient.api.httpclient as api + +LOG = logging.getLogger(__name__) -class KeystoneAuthHandler(auth.AuthHandler): +class KeystoneAuthHandler(mistral_auth.AuthHandler): def authenticate(self, req, session=None): """Performs authentication via Keystone. @@ -44,8 +49,8 @@ class KeystoneAuthHandler(auth.AuthHandler): project_name = req.get('project_name') project_id = req.get('project_id') region_name = req.get('region_name') - user_domain_name = req.get('user_domain_name', 'Default') - project_domain_name = req.get('project_domain_name', 'Default') + user_domain_name = req.get('user_domain_name') + project_domain_name = req.get('project_domain_name') cacert = req.get('cacert') insecure = req.get('insecure', False) @@ -56,12 +61,8 @@ class KeystoneAuthHandler(auth.AuthHandler): target_auth_token = req.get('target_auth_token') target_project_name = req.get('target_project_name') target_project_id = req.get('target_project_id') - target_region_name = req.get('target_region_name') - target_user_domain_name = req.get('target_user_domain_name', 'Default') - target_project_domain_name = req.get( - 'target_project_domain_name', - 'Default' - ) + target_user_domain_name = req.get('target_user_domain_name') + target_project_domain_name = req.get('target_project_domain_name') target_cacert = req.get('target_cacert') target_insecure = req.get('target_insecure') @@ -77,33 +78,42 @@ class KeystoneAuthHandler(auth.AuthHandler): auth_response = {} - if session: - keystone = client.Client(session=session) - elif auth_url: - keystone = client.Client( - username=username, - user_id=user_id, - password=api_key, - token=auth_token, - tenant_id=project_id, - tenant_name=project_name, - auth_url=auth_url, - cacert=cacert, - insecure=insecure, - user_domain_name=user_domain_name, - project_domain_name=project_domain_name - ) - keystone.authenticate() - auth_response.update({ - api.AUTH_TOKEN: keystone.auth_token, - api.PROJECT_ID: keystone.project_id, - api.USER_ID: keystone.user_id, - }) + if not session: + auth = None + if auth_token: + auth = auth_plugin.Token( + auth_url=auth_url, + token=auth_token, + project_id=project_id, + project_name=project_name, + project_domain_name=project_domain_name, + cacert=cacert, + insecure=insecure) + elif api_key and (username or user_id): + auth = auth_plugin.Password( + auth_url=auth_url, + username=username, + user_id=user_id, + password=api_key, + user_domain_name=user_domain_name, + project_id=project_id, + project_name=project_name, + project_domain_name=project_domain_name) - if session or auth_url: + else: + # NOTE(jaosorior): We don't crash here cause it's needed for + # bash-completion to work. However, we do issue a warning to + # the user so if the request doesn't work. It's because of + # this. + LOG.warning("You must either provide a valid token or " + "a password (api_key) and a user.") + if auth: + session = ks_session.Session(auth=auth) + + if session: if not mistral_url: try: - mistral_url = keystone.service_catalog.url_for( + mistral_url = session.get_endpoint( service_type=service_type, endpoint_type=endpoint_type, region_name=region_name @@ -112,35 +122,48 @@ class KeystoneAuthHandler(auth.AuthHandler): mistral_url = None auth_response['mistral_url'] = mistral_url + auth_response['session'] = session if target_auth_url: - target_keystone = client.Client( - username=target_username, - user_id=target_user_id, - password=target_api_key, - token=target_auth_token, - tenant_id=target_project_id, - tenant_name=target_project_name, - project_id=target_project_id, - project_name=target_project_name, - auth_url=target_auth_url, - cacert=target_cacert, - insecure=target_insecure, - region_name=target_region_name, - user_domain_name=target_user_domain_name, - project_domain_name=target_project_domain_name - ) + if target_auth_token: + target_auth = auth_plugin.Token( + auth_url=target_auth_url, + token=target_auth_token, + project_id=target_project_id, + project_name=target_project_name, + project_domain_name=target_project_domain_name, + cacert=target_cacert, + insecure=target_insecure) + elif target_api_key and (target_username or target_user_id): + target_auth = auth_plugin.Password( + auth_url=target_auth_url, + username=target_username, + user_id=target_user_id, + password=target_api_key, + user_domain_name=target_user_domain_name, + project_id=target_project_id, + project_name=target_project_name, + project_domain_name=target_project_domain_name, + ) + else: + raise RuntimeError("You must either provide a valid token or " + "a password (target_api_key) and a user.") - target_keystone.authenticate() + target_session = ks_session.Session(auth=target_auth) + target_auth_headers = target_session.get_auth_headers() or {} + + # NOTE: (sharatss) The target_auth_token is required here so that + # it can be passed as a separate header later. + target_auth_token = target_auth_headers.get('X-Auth-Token') auth_response.update({ - api.TARGET_AUTH_TOKEN: target_keystone.auth_token, - api.TARGET_PROJECT_ID: target_keystone.project_id, - api.TARGET_USER_ID: target_keystone.user_id, + api.TARGET_AUTH_TOKEN: target_auth_token, + api.TARGET_PROJECT_ID: target_session.get_project_id(), + api.TARGET_USER_ID: target_session.get_user_id(), api.TARGET_AUTH_URI: target_auth_url, api.TARGET_SERVICE_CATALOG: jsonutils.dumps( - target_keystone.auth_ref - ) + target_auth.get_access( + target_session)._data['access']) }) return auth_response diff --git a/mistralclient/shell.py b/mistralclient/shell.py index 02353a27..d7a95095 100644 --- a/mistralclient/shell.py +++ b/mistralclient/shell.py @@ -285,8 +285,7 @@ class MistralShell(app.App): '--os-tenant-name', action='store', dest='tenant_name', - default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME', - default='Default'), + default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME'), help='Authentication tenant name (Env: OS_TENANT_NAME' ' or OS_PROJECT_NAME)' ) @@ -295,8 +294,7 @@ class MistralShell(app.App): '--os-project-name', action='store', dest='project_name', - default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME', - default='Default'), + default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME'), help='Authentication project name (Env: OS_TENANT_NAME' ' or OS_PROJECT_NAME), will use tenant_name if both' ' tenant_name and project_name are set' @@ -314,7 +312,7 @@ class MistralShell(app.App): '--os-project-domain-name', action='store', dest='project_domain_name', - default=env('OS_PROJECT_DOMAIN_NAME', default='Default'), + default=env('OS_PROJECT_DOMAIN_NAME'), help='Authentication project domain name' ' (Env: OS_PROJECT_DOMAIN_NAME)' ) @@ -323,7 +321,7 @@ class MistralShell(app.App): '--os-user-domain-name', action='store', dest='user_domain_name', - default=env('OS_USER_DOMAIN_NAME', default='Default'), + default=env('OS_USER_DOMAIN_NAME'), help='Authentication user domain name' ' (Env: OS_USER_DOMAIN_NAME)' ) @@ -435,7 +433,7 @@ class MistralShell(app.App): '--os-target-tenant-name', action='store', dest='target_tenant_name', - default=env('OS_TARGET_TENANT_NAME', 'Default'), + default=env('OS_TARGET_TENANT_NAME'), help='Authentication tenant name for target cloud' ' (Env: OS_TARGET_TENANT_NAME)' ) diff --git a/mistralclient/tests/functional/cli/base.py b/mistralclient/tests/functional/cli/base.py index 55df25d1..b870b151 100644 --- a/mistralclient/tests/functional/cli/base.py +++ b/mistralclient/tests/functional/cli/base.py @@ -39,10 +39,14 @@ def credentials(group='admin'): username = os.environ.get('OS_USERNAME') password = os.environ.get('OS_PASSWORD') tenant_name = os.environ.get('OS_TENANT_NAME') + user_domain = os.environ.get('OS_USER_DOMAIN_NAME') + project_domain = os.environ.get('OS_PROJECT_DOMAIN_NAME') else: username = os.environ.get('OS_ALT_USERNAME') password = os.environ.get('OS_ALT_PASSWORD') tenant_name = os.environ.get('OS_ALT_TENANT_NAME') + user_domain = os.environ.get('OS_ALT_USER_DOMAIN_NAME') + project_domain = os.environ.get('OS_ALT_PROJECT_DOMAIN_NAME') auth_url = os.environ.get('OS_AUTH_URL') @@ -52,6 +56,8 @@ def credentials(group='admin'): password = password or config.get(group, 'pass') tenant_name = tenant_name or config.get(group, 'tenant') auth_url = auth_url or config.get('auth', 'uri') + user_domain = user_domain or config.get(group, 'user_domain') + project_domain = project_domain or config.get(group, 'project_domain') # TODO(ddeja): Default value of OS_AUTH_URL is to provide url to v3 API. # Since tempest openstack client doesn't properly handle it, we switch @@ -77,6 +83,7 @@ class MistralCLIAuth(base.ClientTestBase): username=creds['username'], password=creds['password'], tenant_name=creds['tenant_name'], + project_name=creds['tenant_name'], uri=creds['auth_url'], cli_dir=CLI_DIR ) @@ -130,6 +137,7 @@ class MistralCLIAltAuth(base.ClientTestBase): clients = base.CLIClient( username=creds['username'], password=creds['password'], + project_name=creds['tenant_name'], tenant_name=creds['tenant_name'], uri=creds['auth_url'], cli_dir=CLI_DIR diff --git a/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py b/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py index 70971e74..1fe22bcf 100644 --- a/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py +++ b/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py @@ -192,7 +192,8 @@ class WorkflowSharingCLITests(base_v2.MistralClientTestBase): self.assertEqual('pending', status) - cmd_param = '%s workflow --status %s' % (self.wf[0]["ID"], new_status) + cmd_param = '%s workflow --status %s --member-id %s' % ( + self.wf[0]["ID"], new_status, self.get_project_id("demo")) member = self.mistral_alt_user("member-update", params=cmd_param) status = self.get_field_value(member, 'Status') diff --git a/mistralclient/tests/unit/test_client.py b/mistralclient/tests/unit/test_client.py index 7348e885..ebeadf6b 100644 --- a/mistralclient/tests/unit/test_client.py +++ b/mistralclient/tests/unit/test_client.py @@ -35,26 +35,26 @@ PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY' class BaseClientTests(base.BaseTestCase): @staticmethod - def setup_keystone_mock(keystone_client_mock): - keystone_client_instance = keystone_client_mock.return_value + def setup_keystone_mock(session_mock): + keystone_client_instance = session_mock.return_value keystone_client_instance.auth_token = uuidutils.generate_uuid() keystone_client_instance.project_id = uuidutils.generate_uuid() keystone_client_instance.user_id = uuidutils.generate_uuid() keystone_client_instance.auth_ref = str(json.dumps({})) return keystone_client_instance - @mock.patch('keystoneclient.client.Client') - def test_mistral_url_from_catalog_v2(self, keystone_client_mock): - keystone_client_instance = self.setup_keystone_mock( - keystone_client_mock - ) + @mock.patch('keystoneauth1.session.Session') + def test_mistral_url_from_catalog_v2(self, session_mock): + session = mock.Mock() + session_mock.side_effect = [session] - url_for = mock.Mock(return_value='http://mistral_host:8989/v2') - keystone_client_instance.service_catalog.url_for = url_for + get_endpoint = mock.Mock(return_value='http://mistral_host:8989/v2') + session.get_endpoint = get_endpoint mistralclient = client.client( username='mistral', project_name='mistral', + api_key='password', auth_url=AUTH_HTTP_URL_v2_0, service_type='workflowv2' ) @@ -64,19 +64,20 @@ class BaseClientTests(base.BaseTestCase): mistralclient.actions.http_client.base_url ) - @mock.patch('keystoneclient.client.Client') - def test_mistral_url_from_catalog(self, keystone_client_mock): - keystone_client_instance = self.setup_keystone_mock( - keystone_client_mock - ) + @mock.patch('keystoneauth1.session.Session') + def test_mistral_url_from_catalog(self, session_mock): + session = mock.Mock() + session_mock.side_effect = [session] - url_for = mock.Mock(return_value='http://mistral_host:8989/v2') - - keystone_client_instance.service_catalog.url_for = url_for + get_endpoint = mock.Mock(return_value='http://mistral_host:8989/v2') + session.get_endpoint = get_endpoint mistralclient = client.client( username='mistral', project_name='mistral', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3, service_type='workflowv2' ) @@ -86,82 +87,97 @@ class BaseClientTests(base.BaseTestCase): mistralclient.actions.http_client.base_url ) - @mock.patch('keystoneclient.client.Client') + @mock.patch('keystoneauth1.session.Session') @mock.patch('mistralclient.api.httpclient.HTTPClient') - def test_mistral_url_default(self, http_client_mock, keystone_client_mock): - keystone_client_instance = self.setup_keystone_mock( - keystone_client_mock - ) + def test_mistral_url_default(self, http_client_mock, session_mock): + session = mock.Mock() + session_mock.side_effect = [session] - url_for = mock.Mock(side_effect=Exception) - keystone_client_instance.service_catalog.url_for = url_for + get_endpoint = mock.Mock(side_effect=Exception) + session.get_endpoint = get_endpoint client.client( username='mistral', project_name='mistral', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3 ) self.assertTrue(http_client_mock.called) mistral_url_for_http = http_client_mock.call_args[0][0] - kwargs = http_client_mock.call_args[1] self.assertEqual(MISTRAL_HTTP_URL, mistral_url_for_http) - self.assertEqual( - keystone_client_instance.auth_token, kwargs['auth_token'] - ) - self.assertEqual( - keystone_client_instance.project_id, kwargs['project_id'] - ) - self.assertEqual( - keystone_client_instance.user_id, kwargs['user_id'] - ) - @mock.patch('keystoneclient.client.Client') + @mock.patch('keystoneauth1.identity.generic.Password') + @mock.patch('keystoneauth1.session.Session') @mock.patch('mistralclient.api.httpclient.HTTPClient') def test_target_parameters_processed( self, http_client_mock, - keystone_client_mock + session_mock, + password_mock ): - keystone_client_instance = self.setup_keystone_mock( - keystone_client_mock - ) - url_for = mock.Mock(return_value='http://mistral_host:8989/v2') - keystone_client_instance.service_catalog.url_for = url_for + session = mock.MagicMock() + target_session = mock.MagicMock() + session_mock.side_effect = [session, target_session] + auth = mock.MagicMock() + password_mock.side_effect = [auth, auth] + + get_endpoint = mock.Mock(return_value='http://mistral_host:8989/v2') + session.get_endpoint = get_endpoint + + target_session.get_project_id = mock.Mock(return_value='projectid') + target_session.get_user_id = mock.Mock(return_value='userid') + target_session.get_auth_headers = mock.Mock(return_value={ + 'X-Auth-Token': 'authtoken' + }) + + mock_access = mock.MagicMock() + mock_catalog = mock.MagicMock() + mock_catalog.catalog = {} + mock_access.service_catalog = mock_catalog + auth.get_access = mock.Mock(return_value=mock_access) client.client( + username='user', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', target_username='tmistral', target_project_name='tmistralp', target_auth_url=AUTH_HTTP_URL_v3, + target_api_key='tpassword', + target_user_domain_name='Default', + target_project_domain_name='Default', target_region_name='tregion' ) self.assertTrue(http_client_mock.called) mistral_url_for_http = http_client_mock.call_args[0][0] kwargs = http_client_mock.call_args[1] - self.assertEqual(MISTRAL_HTTP_URL, mistral_url_for_http) + self.assertEqual('http://mistral_host:8989/v2', mistral_url_for_http) expected_values = { - 'target_project_id': keystone_client_instance.project_id, - 'target_auth_token': keystone_client_instance.auth_token, - 'target_user_id': keystone_client_instance.user_id, + 'target_project_id': 'projectid', + 'target_auth_token': 'authtoken', + 'target_user_id': 'userid', 'target_auth_url': AUTH_HTTP_URL_v3, 'target_project_name': 'tmistralp', 'target_username': 'tmistral', 'target_region_name': 'tregion', - 'target_service_catalog': '"{}"' + 'target_service_catalog': "{}" } for key in expected_values: self.assertEqual(expected_values[key], kwargs[key]) - @mock.patch('keystoneclient.client.Client') + @mock.patch('keystoneauth1.session.Session') @mock.patch('mistralclient.api.httpclient.HTTPClient') - def test_mistral_url_https_insecure(self, http_client_mock, - keystone_client_mock): + def test_mistral_url_https_insecure(self, http_client_mock, session_mock): keystone_client_instance = self.setup_keystone_mock( # noqa - keystone_client_mock + session_mock ) expected_args = ( @@ -172,6 +188,9 @@ class BaseClientTests(base.BaseTestCase): mistral_url=MISTRAL_HTTPS_URL, username='mistral', project_name='mistral', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3, cacert=None, insecure=True @@ -181,14 +200,13 @@ class BaseClientTests(base.BaseTestCase): self.assertEqual(http_client_mock.call_args[0], expected_args) self.assertEqual(http_client_mock.call_args[1]['insecure'], True) - @mock.patch('keystoneclient.client.Client') + @mock.patch('keystoneauth1.session.Session') @mock.patch('mistralclient.api.httpclient.HTTPClient') - def test_mistral_url_https_secure(self, http_client_mock, - keystone_client_mock): + def test_mistral_url_https_secure(self, http_client_mock, session_mock): fd, cert_path = tempfile.mkstemp(suffix='.pem') keystone_client_instance = self.setup_keystone_mock( # noqa - keystone_client_mock + session_mock ) expected_args = ( @@ -200,6 +218,9 @@ class BaseClientTests(base.BaseTestCase): mistral_url=MISTRAL_HTTPS_URL, username='mistral', project_name='mistral', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3, cacert=cert_path, insecure=False @@ -212,10 +233,10 @@ class BaseClientTests(base.BaseTestCase): self.assertEqual(http_client_mock.call_args[0], expected_args) self.assertEqual(http_client_mock.call_args[1]['cacert'], cert_path) - @mock.patch('keystoneclient.client.Client') - def test_mistral_url_https_bad_cacert(self, keystone_client_mock): + @mock.patch('keystoneauth1.session.Session') + def test_mistral_url_https_bad_cacert(self, session_mock): keystone_client_instance = self.setup_keystone_mock( # noqa - keystone_client_mock + session_mock ) self.assertRaises( @@ -224,19 +245,22 @@ class BaseClientTests(base.BaseTestCase): mistral_url=MISTRAL_HTTPS_URL, username='mistral', project_name='mistral', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3, cacert='/path/to/foobar', insecure=False ) @mock.patch('logging.Logger.warning') - @mock.patch('keystoneclient.client.Client') - def test_mistral_url_https_bad_insecure(self, keystone_client_mock, + @mock.patch('keystoneauth1.session.Session') + def test_mistral_url_https_bad_insecure(self, session_mock, log_warning_mock): fd, path = tempfile.mkstemp(suffix='.pem') keystone_client_instance = self.setup_keystone_mock( - keystone_client_mock + session_mock ) try: @@ -244,6 +268,9 @@ class BaseClientTests(base.BaseTestCase): mistral_url=MISTRAL_HTTPS_URL, user_id=keystone_client_instance.user_id, project_id=keystone_client_instance.project_id, + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3, cacert=path, insecure=True @@ -254,17 +281,19 @@ class BaseClientTests(base.BaseTestCase): self.assertTrue(log_warning_mock.called) - @mock.patch('keystoneclient.client.Client') + @mock.patch('keystoneauth1.session.Session') @mock.patch('mistralclient.api.httpclient.HTTPClient') - def test_mistral_profile_enabled(self, http_client_mock, - keystone_client_mock): + def test_mistral_profile_enabled(self, http_client_mock, session_mock): keystone_client_instance = self.setup_keystone_mock( # noqa - keystone_client_mock + session_mock ) client.client( username='mistral', project_name='mistral', + api_key='password', + user_domain_name='Default', + project_domain_name='Default', auth_url=AUTH_HTTP_URL_v3, profile=PROFILER_HMAC_KEY ) diff --git a/mistralclient/tests/unit/test_httpclient.py b/mistralclient/tests/unit/test_httpclient.py index dba27680..a6475efe 100644 --- a/mistralclient/tests/unit/test_httpclient.py +++ b/mistralclient/tests/unit/test_httpclient.py @@ -38,7 +38,7 @@ PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY' PROFILER_TRACE_ID = uuidutils.generate_uuid() EXPECTED_AUTH_HEADERS = { - 'x-auth-token': AUTH_TOKEN, + 'X-Auth-Token': AUTH_TOKEN, 'X-Project-Id': PROJECT_ID, 'X-User-Id': USER_ID, 'X-Region-Name': REGION_NAME diff --git a/releasenotes/notes/remove-keystoneclient-dependency-f2981f29e6673f71.yaml b/releasenotes/notes/remove-keystoneclient-dependency-f2981f29e6673f71.yaml new file mode 100644 index 00000000..1ee1b64e --- /dev/null +++ b/releasenotes/notes/remove-keystoneclient-dependency-f2981f29e6673f71.yaml @@ -0,0 +1,6 @@ +--- +other: + - The dependency to python-keystoneclient was removed. Relying solely on + keystoneauth1. + - The user has to set the "OS_USER_DOMAIN_NAME" and "OS_PROJECT_DOMAIN_NAME" + explicitly if keystone v3 version is being used. diff --git a/requirements.txt b/requirements.txt index 4bb4c293..0be12617 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ osc-lib>=1.7.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 -python-keystoneclient>=3.8.0 # Apache-2.0 +keystoneauth1>=3.1.0 # Apache-2.0 PyYAML>=3.10.0 # MIT requests>=2.14.2 # Apache-2.0 six>=1.9.0 # MIT