Merge "Use keystoneauth plugins and session instead of keystoneclient"

This commit is contained in:
Jenkins 2017-08-10 14:59:31 +00:00 committed by Gerrit Code Review
commit 19c89ca49f
12 changed files with 253 additions and 166 deletions

@ -31,6 +31,8 @@ source openrc alt_demo alt_demo
export OS_ALT_USERNAME=${OS_USERNAME} export OS_ALT_USERNAME=${OS_USERNAME}
export OS_ALT_TENANT_NAME=${OS_TENANT_NAME} 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} export OS_ALT_PASSWORD=${OS_PASSWORD}
# Get admin credentials. # Get admin credentials.
@ -46,10 +48,14 @@ uri = $OS_AUTH_URL
user = $OS_USERNAME user = $OS_USERNAME
tenant = $OS_TENANT_NAME tenant = $OS_TENANT_NAME
pass = $OS_PASSWORD pass = $OS_PASSWORD
user_domain = $OS_USER_DOMAIN_NAME
project_domain = $OS_PROJECT_DOMAIN_NAME
[demo] [demo]
user = $OS_ALT_USERNAME user = $OS_ALT_USERNAME
tenant = $OS_ALT_TENANT_NAME tenant = $OS_ALT_TENANT_NAME
pass = $OS_ALT_PASSWORD pass = $OS_ALT_PASSWORD
user_domain = $OS_ALT_USER_DOMAIN_NAME
project_domain = $OS_ALT_PROJECT_DOMAIN_NAME
EOF EOF
cd $MISTRALCLIENT_DIR cd $MISTRALCLIENT_DIR

@ -15,6 +15,8 @@
import copy import copy
import json import json
from keystoneauth1 import exceptions
class Resource(object): class Resource(object):
resource_name = 'Something' resource_name = 'Something'
@ -89,7 +91,10 @@ class ResourceManager(object):
if dump_json: if dump_json:
data = json.dumps(data) 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: if resp.status_code != 201:
self._raise_api_exception(resp) self._raise_api_exception(resp)
@ -100,7 +105,10 @@ class ResourceManager(object):
if dump_json: if dump_json:
data = json.dumps(data) 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: if resp.status_code != 200:
self._raise_api_exception(resp) self._raise_api_exception(resp)
@ -108,7 +116,10 @@ class ResourceManager(object):
return self.resource_class(self, extract_json(resp, response_key)) return self.resource_class(self, extract_json(resp, response_key))
def _list(self, url, response_key=None): 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: if resp.status_code != 200:
self._raise_api_exception(resp) self._raise_api_exception(resp)
@ -117,7 +128,10 @@ class ResourceManager(object):
for resource_data in extract_json(resp, response_key)] for resource_data in extract_json(resp, response_key)]
def _get(self, url, response_key=None): 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: if resp.status_code == 200:
return self.resource_class(self, extract_json(resp, response_key)) return self.resource_class(self, extract_json(resp, response_key))
@ -125,7 +139,10 @@ class ResourceManager(object):
self._raise_api_exception(resp) self._raise_api_exception(resp)
def _delete(self, url): 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: if resp.status_code != 204:
self._raise_api_exception(resp) self._raise_api_exception(resp)

@ -15,15 +15,16 @@
import base64 import base64
import copy import copy
import logging
import os import os
from oslo_utils import importutils from oslo_utils import importutils
import requests
import logging import requests
AUTH_TOKEN = 'auth_token' AUTH_TOKEN = 'auth_token'
SESSION = 'session'
CACERT = 'cacert' CACERT = 'cacert'
CERT_FILE = 'cert' CERT_FILE = 'cert'
CERT_KEY = 'key' CERT_KEY = 'key'
@ -33,6 +34,7 @@ USER_ID = 'user_id'
REGION_NAME = 'region_name' REGION_NAME = 'region_name'
TARGET_AUTH_TOKEN = 'target_auth_token' TARGET_AUTH_TOKEN = 'target_auth_token'
TARGET_SESSION = 'target_session'
TARGET_AUTH_URI = 'target_auth_url' TARGET_AUTH_URI = 'target_auth_url'
TARGET_PROJECT_ID = 'target_project_id' TARGET_PROJECT_ID = 'target_project_id'
TARGET_USER_ID = 'target_user_id' TARGET_USER_ID = 'target_user_id'
@ -59,7 +61,9 @@ 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.session = kwargs.get('session')
if not self.session:
self.session = requests.Session()
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)
@ -68,6 +72,7 @@ class HTTPClient(object):
self.region_name = kwargs.get(REGION_NAME) self.region_name = kwargs.get(REGION_NAME)
self.ssl_options = {} self.ssl_options = {}
self.target_session = kwargs.get(TARGET_SESSION)
self.target_auth_token = kwargs.get(TARGET_AUTH_TOKEN) self.target_auth_token = kwargs.get(TARGET_AUTH_TOKEN)
self.target_auth_uri = kwargs.get(TARGET_AUTH_URI) self.target_auth_uri = kwargs.get(TARGET_AUTH_URI)
self.target_user_id = kwargs.get(TARGET_USER_ID) self.target_user_id = kwargs.get(TARGET_USER_ID)
@ -80,11 +85,6 @@ 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 '
@ -94,47 +94,42 @@ 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.')
# These are already set by the session, so it's not needed if self.insecure:
if not self.session: self.ssl_options['verify'] = False
if self.insecure: else:
self.ssl_options['verify'] = False if self.cacert:
self.ssl_options['verify'] = self.cacert
else: else:
if self.cacert: self.ssl_options['verify'] = True
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 self.crud_provider.get(self.base_url + url, **options) return self.session.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 self.crud_provider.post(self.base_url + url, return self.session.post(self.base_url + url, data=body, **options)
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 self.crud_provider.put(self.base_url + url, return self.session.put(self.base_url + url, data=body, **options)
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 self.crud_provider.delete(self.base_url + url, return self.session.delete(self.base_url + url, **options)
**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)
@ -152,9 +147,9 @@ class HTTPClient(object):
if not headers: if not headers:
headers = {} headers = {}
if not self.session: if isinstance(self.session, requests.Session):
if self.auth_token: if self.auth_token:
headers['x-auth-token'] = 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

@ -54,6 +54,10 @@ class Client(object):
auth_handler = auth.get_auth_handler(auth_type) auth_handler = auth.get_auth_handler(auth_type)
auth_response = auth_handler.authenticate(req, session=session) or {} 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) req.update(auth_response)
mistral_url = auth_response.get('mistral_url') or mistral_url mistral_url = auth_response.get('mistral_url') or mistral_url

@ -12,14 +12,19 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from keystoneclient import client import logging
from mistralclient import auth
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 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): def authenticate(self, req, session=None):
"""Performs authentication via Keystone. """Performs authentication via Keystone.
@ -44,8 +49,8 @@ class KeystoneAuthHandler(auth.AuthHandler):
project_name = req.get('project_name') project_name = req.get('project_name')
project_id = req.get('project_id') project_id = req.get('project_id')
region_name = req.get('region_name') region_name = req.get('region_name')
user_domain_name = req.get('user_domain_name', 'Default') user_domain_name = req.get('user_domain_name')
project_domain_name = req.get('project_domain_name', 'Default') project_domain_name = req.get('project_domain_name')
cacert = req.get('cacert') cacert = req.get('cacert')
insecure = req.get('insecure', False) insecure = req.get('insecure', False)
@ -56,12 +61,8 @@ class KeystoneAuthHandler(auth.AuthHandler):
target_auth_token = req.get('target_auth_token') target_auth_token = req.get('target_auth_token')
target_project_name = req.get('target_project_name') target_project_name = req.get('target_project_name')
target_project_id = req.get('target_project_id') 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')
target_user_domain_name = req.get('target_user_domain_name', 'Default') target_project_domain_name = req.get('target_project_domain_name')
target_project_domain_name = req.get(
'target_project_domain_name',
'Default'
)
target_cacert = req.get('target_cacert') target_cacert = req.get('target_cacert')
target_insecure = req.get('target_insecure') target_insecure = req.get('target_insecure')
@ -77,33 +78,42 @@ class KeystoneAuthHandler(auth.AuthHandler):
auth_response = {} auth_response = {}
if session: if not session:
keystone = client.Client(session=session) auth = None
elif auth_url: if auth_token:
keystone = client.Client( auth = auth_plugin.Token(
username=username, auth_url=auth_url,
user_id=user_id, token=auth_token,
password=api_key, project_id=project_id,
token=auth_token, project_name=project_name,
tenant_id=project_id, project_domain_name=project_domain_name,
tenant_name=project_name, cacert=cacert,
auth_url=auth_url, insecure=insecure)
cacert=cacert, elif api_key and (username or user_id):
insecure=insecure, auth = auth_plugin.Password(
user_domain_name=user_domain_name, auth_url=auth_url,
project_domain_name=project_domain_name username=username,
) user_id=user_id,
keystone.authenticate() password=api_key,
auth_response.update({ user_domain_name=user_domain_name,
api.AUTH_TOKEN: keystone.auth_token, project_id=project_id,
api.PROJECT_ID: keystone.project_id, project_name=project_name,
api.USER_ID: keystone.user_id, 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: if not mistral_url:
try: try:
mistral_url = keystone.service_catalog.url_for( mistral_url = session.get_endpoint(
service_type=service_type, service_type=service_type,
endpoint_type=endpoint_type, endpoint_type=endpoint_type,
region_name=region_name region_name=region_name
@ -112,35 +122,48 @@ class KeystoneAuthHandler(auth.AuthHandler):
mistral_url = None mistral_url = None
auth_response['mistral_url'] = mistral_url auth_response['mistral_url'] = mistral_url
auth_response['session'] = session
if target_auth_url: if target_auth_url:
target_keystone = client.Client( if target_auth_token:
username=target_username, target_auth = auth_plugin.Token(
user_id=target_user_id, auth_url=target_auth_url,
password=target_api_key, token=target_auth_token,
token=target_auth_token, project_id=target_project_id,
tenant_id=target_project_id, project_name=target_project_name,
tenant_name=target_project_name, project_domain_name=target_project_domain_name,
project_id=target_project_id, cacert=target_cacert,
project_name=target_project_name, insecure=target_insecure)
auth_url=target_auth_url, elif target_api_key and (target_username or target_user_id):
cacert=target_cacert, target_auth = auth_plugin.Password(
insecure=target_insecure, auth_url=target_auth_url,
region_name=target_region_name, username=target_username,
user_domain_name=target_user_domain_name, user_id=target_user_id,
project_domain_name=target_project_domain_name 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({ auth_response.update({
api.TARGET_AUTH_TOKEN: target_keystone.auth_token, api.TARGET_AUTH_TOKEN: target_auth_token,
api.TARGET_PROJECT_ID: target_keystone.project_id, api.TARGET_PROJECT_ID: target_session.get_project_id(),
api.TARGET_USER_ID: target_keystone.user_id, api.TARGET_USER_ID: target_session.get_user_id(),
api.TARGET_AUTH_URI: target_auth_url, api.TARGET_AUTH_URI: target_auth_url,
api.TARGET_SERVICE_CATALOG: jsonutils.dumps( api.TARGET_SERVICE_CATALOG: jsonutils.dumps(
target_keystone.auth_ref target_auth.get_access(
) target_session)._data['access'])
}) })
return auth_response return auth_response

@ -285,8 +285,7 @@ class MistralShell(app.App):
'--os-tenant-name', '--os-tenant-name',
action='store', action='store',
dest='tenant_name', dest='tenant_name',
default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME', default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME'),
default='Default'),
help='Authentication tenant name (Env: OS_TENANT_NAME' help='Authentication tenant name (Env: OS_TENANT_NAME'
' or OS_PROJECT_NAME)' ' or OS_PROJECT_NAME)'
) )
@ -295,8 +294,7 @@ class MistralShell(app.App):
'--os-project-name', '--os-project-name',
action='store', action='store',
dest='project_name', dest='project_name',
default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME', default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME'),
default='Default'),
help='Authentication project name (Env: OS_TENANT_NAME' help='Authentication project name (Env: OS_TENANT_NAME'
' or OS_PROJECT_NAME), will use tenant_name if both' ' or OS_PROJECT_NAME), will use tenant_name if both'
' tenant_name and project_name are set' ' tenant_name and project_name are set'
@ -314,7 +312,7 @@ class MistralShell(app.App):
'--os-project-domain-name', '--os-project-domain-name',
action='store', action='store',
dest='project_domain_name', dest='project_domain_name',
default=env('OS_PROJECT_DOMAIN_NAME', default='Default'), default=env('OS_PROJECT_DOMAIN_NAME'),
help='Authentication project domain name' help='Authentication project domain name'
' (Env: OS_PROJECT_DOMAIN_NAME)' ' (Env: OS_PROJECT_DOMAIN_NAME)'
) )
@ -323,7 +321,7 @@ class MistralShell(app.App):
'--os-user-domain-name', '--os-user-domain-name',
action='store', action='store',
dest='user_domain_name', dest='user_domain_name',
default=env('OS_USER_DOMAIN_NAME', default='Default'), default=env('OS_USER_DOMAIN_NAME'),
help='Authentication user domain name' help='Authentication user domain name'
' (Env: OS_USER_DOMAIN_NAME)' ' (Env: OS_USER_DOMAIN_NAME)'
) )
@ -435,7 +433,7 @@ class MistralShell(app.App):
'--os-target-tenant-name', '--os-target-tenant-name',
action='store', action='store',
dest='target_tenant_name', dest='target_tenant_name',
default=env('OS_TARGET_TENANT_NAME', 'Default'), default=env('OS_TARGET_TENANT_NAME'),
help='Authentication tenant name for target cloud' help='Authentication tenant name for target cloud'
' (Env: OS_TARGET_TENANT_NAME)' ' (Env: OS_TARGET_TENANT_NAME)'
) )

@ -39,10 +39,14 @@ def credentials(group='admin'):
username = os.environ.get('OS_USERNAME') username = os.environ.get('OS_USERNAME')
password = os.environ.get('OS_PASSWORD') password = os.environ.get('OS_PASSWORD')
tenant_name = os.environ.get('OS_TENANT_NAME') 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: else:
username = os.environ.get('OS_ALT_USERNAME') username = os.environ.get('OS_ALT_USERNAME')
password = os.environ.get('OS_ALT_PASSWORD') password = os.environ.get('OS_ALT_PASSWORD')
tenant_name = os.environ.get('OS_ALT_TENANT_NAME') 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') auth_url = os.environ.get('OS_AUTH_URL')
@ -52,6 +56,8 @@ def credentials(group='admin'):
password = password or config.get(group, 'pass') password = password or config.get(group, 'pass')
tenant_name = tenant_name or config.get(group, 'tenant') tenant_name = tenant_name or config.get(group, 'tenant')
auth_url = auth_url or config.get('auth', 'uri') 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. # 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 # Since tempest openstack client doesn't properly handle it, we switch
@ -77,6 +83,7 @@ class MistralCLIAuth(base.ClientTestBase):
username=creds['username'], username=creds['username'],
password=creds['password'], password=creds['password'],
tenant_name=creds['tenant_name'], tenant_name=creds['tenant_name'],
project_name=creds['tenant_name'],
uri=creds['auth_url'], uri=creds['auth_url'],
cli_dir=CLI_DIR cli_dir=CLI_DIR
) )
@ -130,6 +137,7 @@ class MistralCLIAltAuth(base.ClientTestBase):
clients = base.CLIClient( clients = base.CLIClient(
username=creds['username'], username=creds['username'],
password=creds['password'], password=creds['password'],
project_name=creds['tenant_name'],
tenant_name=creds['tenant_name'], tenant_name=creds['tenant_name'],
uri=creds['auth_url'], uri=creds['auth_url'],
cli_dir=CLI_DIR cli_dir=CLI_DIR

@ -192,7 +192,8 @@ class WorkflowSharingCLITests(base_v2.MistralClientTestBase):
self.assertEqual('pending', status) 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) member = self.mistral_alt_user("member-update", params=cmd_param)
status = self.get_field_value(member, 'Status') status = self.get_field_value(member, 'Status')

@ -35,26 +35,26 @@ PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
class BaseClientTests(base.BaseTestCase): class BaseClientTests(base.BaseTestCase):
@staticmethod @staticmethod
def setup_keystone_mock(keystone_client_mock): def setup_keystone_mock(session_mock):
keystone_client_instance = keystone_client_mock.return_value keystone_client_instance = session_mock.return_value
keystone_client_instance.auth_token = uuidutils.generate_uuid() keystone_client_instance.auth_token = uuidutils.generate_uuid()
keystone_client_instance.project_id = uuidutils.generate_uuid() keystone_client_instance.project_id = uuidutils.generate_uuid()
keystone_client_instance.user_id = uuidutils.generate_uuid() keystone_client_instance.user_id = uuidutils.generate_uuid()
keystone_client_instance.auth_ref = str(json.dumps({})) keystone_client_instance.auth_ref = str(json.dumps({}))
return keystone_client_instance return keystone_client_instance
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
def test_mistral_url_from_catalog_v2(self, keystone_client_mock): def test_mistral_url_from_catalog_v2(self, session_mock):
keystone_client_instance = self.setup_keystone_mock( session = mock.Mock()
keystone_client_mock session_mock.side_effect = [session]
)
url_for = mock.Mock(return_value='http://mistral_host:8989/v2') get_endpoint = mock.Mock(return_value='http://mistral_host:8989/v2')
keystone_client_instance.service_catalog.url_for = url_for session.get_endpoint = get_endpoint
mistralclient = client.client( mistralclient = client.client(
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
auth_url=AUTH_HTTP_URL_v2_0, auth_url=AUTH_HTTP_URL_v2_0,
service_type='workflowv2' service_type='workflowv2'
) )
@ -64,19 +64,20 @@ class BaseClientTests(base.BaseTestCase):
mistralclient.actions.http_client.base_url mistralclient.actions.http_client.base_url
) )
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
def test_mistral_url_from_catalog(self, keystone_client_mock): def test_mistral_url_from_catalog(self, session_mock):
keystone_client_instance = self.setup_keystone_mock( session = mock.Mock()
keystone_client_mock session_mock.side_effect = [session]
)
url_for = mock.Mock(return_value='http://mistral_host:8989/v2') get_endpoint = mock.Mock(return_value='http://mistral_host:8989/v2')
session.get_endpoint = get_endpoint
keystone_client_instance.service_catalog.url_for = url_for
mistralclient = client.client( mistralclient = client.client(
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
auth_url=AUTH_HTTP_URL_v3, auth_url=AUTH_HTTP_URL_v3,
service_type='workflowv2' service_type='workflowv2'
) )
@ -86,82 +87,97 @@ class BaseClientTests(base.BaseTestCase):
mistralclient.actions.http_client.base_url mistralclient.actions.http_client.base_url
) )
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
@mock.patch('mistralclient.api.httpclient.HTTPClient') @mock.patch('mistralclient.api.httpclient.HTTPClient')
def test_mistral_url_default(self, http_client_mock, keystone_client_mock): def test_mistral_url_default(self, http_client_mock, session_mock):
keystone_client_instance = self.setup_keystone_mock( session = mock.Mock()
keystone_client_mock session_mock.side_effect = [session]
)
url_for = mock.Mock(side_effect=Exception) get_endpoint = mock.Mock(side_effect=Exception)
keystone_client_instance.service_catalog.url_for = url_for session.get_endpoint = get_endpoint
client.client( client.client(
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
auth_url=AUTH_HTTP_URL_v3 auth_url=AUTH_HTTP_URL_v3
) )
self.assertTrue(http_client_mock.called) self.assertTrue(http_client_mock.called)
mistral_url_for_http = http_client_mock.call_args[0][0] 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(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') @mock.patch('mistralclient.api.httpclient.HTTPClient')
def test_target_parameters_processed( def test_target_parameters_processed(
self, self,
http_client_mock, 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') session = mock.MagicMock()
keystone_client_instance.service_catalog.url_for = url_for 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( client.client(
username='user',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
target_username='tmistral', target_username='tmistral',
target_project_name='tmistralp', target_project_name='tmistralp',
target_auth_url=AUTH_HTTP_URL_v3, 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' target_region_name='tregion'
) )
self.assertTrue(http_client_mock.called) self.assertTrue(http_client_mock.called)
mistral_url_for_http = http_client_mock.call_args[0][0] mistral_url_for_http = http_client_mock.call_args[0][0]
kwargs = http_client_mock.call_args[1] 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 = { expected_values = {
'target_project_id': keystone_client_instance.project_id, 'target_project_id': 'projectid',
'target_auth_token': keystone_client_instance.auth_token, 'target_auth_token': 'authtoken',
'target_user_id': keystone_client_instance.user_id, 'target_user_id': 'userid',
'target_auth_url': AUTH_HTTP_URL_v3, 'target_auth_url': AUTH_HTTP_URL_v3,
'target_project_name': 'tmistralp', 'target_project_name': 'tmistralp',
'target_username': 'tmistral', 'target_username': 'tmistral',
'target_region_name': 'tregion', 'target_region_name': 'tregion',
'target_service_catalog': '"{}"' 'target_service_catalog': "{}"
} }
for key in expected_values: for key in expected_values:
self.assertEqual(expected_values[key], kwargs[key]) self.assertEqual(expected_values[key], kwargs[key])
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
@mock.patch('mistralclient.api.httpclient.HTTPClient') @mock.patch('mistralclient.api.httpclient.HTTPClient')
def test_mistral_url_https_insecure(self, http_client_mock, def test_mistral_url_https_insecure(self, http_client_mock, session_mock):
keystone_client_mock):
keystone_client_instance = self.setup_keystone_mock( # noqa keystone_client_instance = self.setup_keystone_mock( # noqa
keystone_client_mock session_mock
) )
expected_args = ( expected_args = (
@ -172,6 +188,9 @@ class BaseClientTests(base.BaseTestCase):
mistral_url=MISTRAL_HTTPS_URL, mistral_url=MISTRAL_HTTPS_URL,
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
auth_url=AUTH_HTTP_URL_v3, auth_url=AUTH_HTTP_URL_v3,
cacert=None, cacert=None,
insecure=True 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[0], expected_args)
self.assertEqual(http_client_mock.call_args[1]['insecure'], True) 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') @mock.patch('mistralclient.api.httpclient.HTTPClient')
def test_mistral_url_https_secure(self, http_client_mock, def test_mistral_url_https_secure(self, http_client_mock, session_mock):
keystone_client_mock):
fd, cert_path = tempfile.mkstemp(suffix='.pem') fd, cert_path = tempfile.mkstemp(suffix='.pem')
keystone_client_instance = self.setup_keystone_mock( # noqa keystone_client_instance = self.setup_keystone_mock( # noqa
keystone_client_mock session_mock
) )
expected_args = ( expected_args = (
@ -200,6 +218,9 @@ class BaseClientTests(base.BaseTestCase):
mistral_url=MISTRAL_HTTPS_URL, mistral_url=MISTRAL_HTTPS_URL,
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
auth_url=AUTH_HTTP_URL_v3, auth_url=AUTH_HTTP_URL_v3,
cacert=cert_path, cacert=cert_path,
insecure=False 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[0], expected_args)
self.assertEqual(http_client_mock.call_args[1]['cacert'], cert_path) self.assertEqual(http_client_mock.call_args[1]['cacert'], cert_path)
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
def test_mistral_url_https_bad_cacert(self, keystone_client_mock): def test_mistral_url_https_bad_cacert(self, session_mock):
keystone_client_instance = self.setup_keystone_mock( # noqa keystone_client_instance = self.setup_keystone_mock( # noqa
keystone_client_mock session_mock
) )
self.assertRaises( self.assertRaises(
@ -224,19 +245,22 @@ class BaseClientTests(base.BaseTestCase):
mistral_url=MISTRAL_HTTPS_URL, mistral_url=MISTRAL_HTTPS_URL,
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
auth_url=AUTH_HTTP_URL_v3, auth_url=AUTH_HTTP_URL_v3,
cacert='/path/to/foobar', cacert='/path/to/foobar',
insecure=False insecure=False
) )
@mock.patch('logging.Logger.warning') @mock.patch('logging.Logger.warning')
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
def test_mistral_url_https_bad_insecure(self, keystone_client_mock, def test_mistral_url_https_bad_insecure(self, session_mock,
log_warning_mock): log_warning_mock):
fd, path = tempfile.mkstemp(suffix='.pem') fd, path = tempfile.mkstemp(suffix='.pem')
keystone_client_instance = self.setup_keystone_mock( keystone_client_instance = self.setup_keystone_mock(
keystone_client_mock session_mock
) )
try: try:
@ -244,6 +268,9 @@ class BaseClientTests(base.BaseTestCase):
mistral_url=MISTRAL_HTTPS_URL, mistral_url=MISTRAL_HTTPS_URL,
user_id=keystone_client_instance.user_id, user_id=keystone_client_instance.user_id,
project_id=keystone_client_instance.project_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, auth_url=AUTH_HTTP_URL_v3,
cacert=path, cacert=path,
insecure=True insecure=True
@ -254,17 +281,19 @@ class BaseClientTests(base.BaseTestCase):
self.assertTrue(log_warning_mock.called) self.assertTrue(log_warning_mock.called)
@mock.patch('keystoneclient.client.Client') @mock.patch('keystoneauth1.session.Session')
@mock.patch('mistralclient.api.httpclient.HTTPClient') @mock.patch('mistralclient.api.httpclient.HTTPClient')
def test_mistral_profile_enabled(self, http_client_mock, def test_mistral_profile_enabled(self, http_client_mock, session_mock):
keystone_client_mock):
keystone_client_instance = self.setup_keystone_mock( # noqa keystone_client_instance = self.setup_keystone_mock( # noqa
keystone_client_mock session_mock
) )
client.client( client.client(
username='mistral', username='mistral',
project_name='mistral', project_name='mistral',
api_key='password',
user_domain_name='Default',
project_domain_name='Default',
auth_url=AUTH_HTTP_URL_v3, auth_url=AUTH_HTTP_URL_v3,
profile=PROFILER_HMAC_KEY profile=PROFILER_HMAC_KEY
) )

@ -38,7 +38,7 @@ PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
PROFILER_TRACE_ID = uuidutils.generate_uuid() PROFILER_TRACE_ID = uuidutils.generate_uuid()
EXPECTED_AUTH_HEADERS = { EXPECTED_AUTH_HEADERS = {
'x-auth-token': AUTH_TOKEN, 'X-Auth-Token': AUTH_TOKEN,
'X-Project-Id': PROJECT_ID, 'X-Project-Id': PROJECT_ID,
'X-User-Id': USER_ID, 'X-User-Id': USER_ID,
'X-Region-Name': REGION_NAME 'X-Region-Name': REGION_NAME

@ -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.

@ -6,7 +6,7 @@ osc-lib>=1.7.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.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 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 PyYAML>=3.10.0 # MIT
requests>=2.14.2 # Apache-2.0 requests>=2.14.2 # Apache-2.0
six>=1.9.0 # MIT six>=1.9.0 # MIT