Use keystoneauth plugins and session instead of keystoneclient

This removes the usage of keystoneclient in favor of using keystoneauth's
sessions. For this it uses keystone sessions by default for both the
caller and the target.

The default settings for the user and project's domain was removed as well,
since passing these to the generic auth plugins caused a failure. They
should only be set if the user had specified so.

Change-Id: Ia8e461d9e27cf1256c988d49eeef050872d5af12
Co-Authored-By: Sharat Sharma <sharat.sharma@nectechnologies.in>
Co-Authored-By: Thomas Hervé <therve@redhat.com>
Implements-blueprint: mistral-use-keystoneauth
This commit is contained in:
Sharat Sharma 2017-07-31 17:08:55 +05:30
parent 0380c88c4a
commit 55968a9ce4
12 changed files with 253 additions and 166 deletions

View File

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

View File

@ -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)
try:
resp = self.http_client.post(url, data) 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)
try:
resp = self.http_client.put(url, data) 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):
try:
resp = self.http_client.get(url) 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):
try:
resp = self.http_client.get(url) 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):
try:
resp = self.http_client.delete(url) 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)

View File

@ -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,8 +94,6 @@ 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 not self.session:
if self.insecure: if self.insecure:
self.ssl_options['verify'] = False self.ssl_options['verify'] = False
else: else:
@ -113,28 +111,25 @@ class HTTPClient(object):
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

View File

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

View File

@ -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(
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, username=username,
user_id=user_id, user_id=user_id,
password=api_key, 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, user_domain_name=user_domain_name,
project_domain_name=project_domain_name project_id=project_id,
) project_name=project_name,
keystone.authenticate() project_domain_name=project_domain_name)
auth_response.update({
api.AUTH_TOKEN: keystone.auth_token,
api.PROJECT_ID: keystone.project_id,
api.USER_ID: keystone.user_id,
})
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:
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, username=target_username,
user_id=target_user_id, user_id=target_user_id,
password=target_api_key, password=target_api_key,
token=target_auth_token, user_domain_name=target_user_domain_name,
tenant_id=target_project_id,
tenant_name=target_project_name,
project_id=target_project_id, project_id=target_project_id,
project_name=target_project_name, project_name=target_project_name,
auth_url=target_auth_url, project_domain_name=target_project_domain_name,
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
) )
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

View File

@ -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)'
) )

View File

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

View File

@ -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')

View File

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

View File

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

View File

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

View File

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