From aa765e2ae994149edc1b80422a9546ee21c62821 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Fri, 29 Dec 2017 14:41:52 +1300 Subject: [PATCH] Support kubernetes python client 4.0.0 From 4.0.0, kubernetes-incubator/client-python uses multiprocessing libaray to send request to k8s cluster, which is not supported by eventlet. This patch introduced the following changes to fix the issue: - Use cotyledon for engine service rather than oslo.service - Update global requirments - Provide separate scripts for api and engine service References: [1] https://github.com/eventlet/eventlet/issues/147 [2] https://bugs.launchpad.net/taskflow/+bug/1225275 Change-Id: Ib99565e00eedc72c388e8ebec6b7f1453f77f30f --- devstack/plugin.sh | 6 +- qinling/api/controllers/v1/function.py | 15 +- qinling/api/service.py | 21 ++- qinling/cmd/api.py | 49 +++++++ qinling/cmd/engine.py | 49 +++++++ qinling/cmd/launch.py | 161 --------------------- qinling/config.py | 4 +- qinling/engine/service.py | 34 ++--- qinling/engine/utils.py | 2 +- qinling/orchestrator/kubernetes/manager.py | 8 +- qinling/orchestrator/kubernetes/utils.py | 34 +++++ qinling/storage/file_system.py | 7 +- qinling/utils/common.py | 54 +++++++ qinling/utils/openstack/keystone.py | 8 +- qinling_tempest_plugin/config.py | 2 +- qinling_tempest_plugin/tests/base.py | 9 +- qinling_tempest_plugin/tests/utils.py | 34 +++++ requirements.txt | 42 +++--- setup.cfg | 3 +- setup.py | 2 +- test-requirements.txt | 13 +- 21 files changed, 306 insertions(+), 251 deletions(-) create mode 100644 qinling/cmd/api.py create mode 100644 qinling/cmd/engine.py delete mode 100644 qinling/cmd/launch.py create mode 100644 qinling/orchestrator/kubernetes/utils.py create mode 100644 qinling_tempest_plugin/tests/utils.py diff --git a/devstack/plugin.sh b/devstack/plugin.sh index f6e0227f..2e828efb 100755 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -80,7 +80,7 @@ function configure_qinling { # Setup keystone_authtoken section configure_auth_token_middleware $QINLING_CONF_FILE qinling $QINLING_AUTH_CACHE_DIR - iniset $QINLING_CONF_FILE keystone_authtoken auth_uri $KEYSTONE_AUTH_URI_V3 + iniset $QINLING_CONF_FILE keystone_authtoken www_authenticate_uri $KEYSTONE_AUTH_URI_V3 # Setup RabbitMQ credentials iniset_rpc_backend qinling $QINLING_CONF_FILE @@ -99,8 +99,8 @@ function init_qinling { function start_qinling { - run_process qinling-engine "$QINLING_BIN_DIR/qinling-server --server engine --config-file $QINLING_CONF_FILE" - run_process qinling-api "$QINLING_BIN_DIR/qinling-server --server api --config-file $QINLING_CONF_FILE" + run_process qinling-engine "$QINLING_BIN_DIR/qinling-engine --config-file $QINLING_CONF_FILE" + run_process qinling-api "$QINLING_BIN_DIR/qinling-api --config-file $QINLING_CONF_FILE" } diff --git a/qinling/api/controllers/v1/function.py b/qinling/api/controllers/v1/function.py index 635f2dc7..a21a483d 100644 --- a/qinling/api/controllers/v1/function.py +++ b/qinling/api/controllers/v1/function.py @@ -76,7 +76,6 @@ class FunctionsController(rest.RestController): def __init__(self, *args, **kwargs): self.storage_provider = storage_base.load_storage_provider(CONF) self.engine_client = rpc.get_engine_client() - self.type = 'function' super(FunctionsController, self).__init__(*args, **kwargs) @@ -92,7 +91,7 @@ class FunctionsController(rest.RestController): @rest_utils.wrap_pecan_controller_exception @pecan.expose() def get(self, id): - LOG.info("Get resource.", resource={'type': self.type, 'id': id}) + LOG.info("Get function %s.", id) download = strutils.bool_from_string( pecan.request.GET.get('download', False) @@ -104,6 +103,7 @@ class FunctionsController(rest.RestController): pecan.override_template('json') return resources.Function.from_dict(func_db.to_dict()).to_dict() else: + LOG.info("Downloading function %s", id) source = func_db.code['source'] if source == 'package': @@ -126,11 +126,12 @@ class FunctionsController(rest.RestController): pecan.response.headers['Content-Disposition'] = ( 'attachment; filename="%s"' % os.path.basename(func_db.name) ) + LOG.info("Downloaded function %s", id) @rest_utils.wrap_pecan_controller_exception @pecan.expose('json') def post(self, **kwargs): - LOG.info("Creating %s, params: %s", self.type, kwargs) + LOG.info("Creating function, params: %s", kwargs) # When using image to create function, runtime_id is not a required # param. @@ -221,7 +222,7 @@ class FunctionsController(rest.RestController): filters = rest_utils.get_filters( project_id=project_id, ) - LOG.info("Get all %ss. filters=%s", self.type, filters) + LOG.info("Get all functions. filters=%s", filters) db_functions = db_api.get_functions(insecure=all_projects, **filters) functions = [resources.Function.from_dict(db_model.to_dict()) for db_model in db_functions] @@ -232,7 +233,7 @@ class FunctionsController(rest.RestController): @wsme_pecan.wsexpose(None, types.uuid, status_code=204) def delete(self, id): """Delete the specified function.""" - LOG.info("Delete resource.", resource={'type': self.type, 'id': id}) + LOG.info("Delete function %s.", id) with db_api.transaction(): func_db = db_api.get_function(id) @@ -280,9 +281,7 @@ class FunctionsController(rest.RestController): if kwargs.get(key) is not None: values.update({key: kwargs[key]}) - LOG.info('Update resource, params: %s', values, - resource={'type': self.type, 'id': id}) - + LOG.info('Update function %s, params: %s', id, values) ctx = context.get_ctx() if set(values.keys()).issubset(set(['name', 'description'])): diff --git a/qinling/api/service.py b/qinling/api/service.py index 937fb04e..17f479d5 100644 --- a/qinling/api/service.py +++ b/qinling/api/service.py @@ -14,25 +14,34 @@ from oslo_concurrency import processutils from oslo_config import cfg +from oslo_log import log as logging from oslo_service import service from oslo_service import wsgi from qinling.api import app +CONF = cfg.CONF +LOG = logging.getLogger(__name__) + class WSGIService(service.ServiceBase): """Provides ability to launch Mistral API from wsgi app.""" - def __init__(self, name): - self.name = name + def __init__(self): self.app = app.setup_app() - self.workers = ( - cfg.CONF.api.api_workers or processutils.get_worker_count() - ) + + self.workers = CONF.api.api_workers + if self.workers is not None and self.workers < 1: + LOG.warning( + "Value of config option api_workers must be integer " + "greater than 1. Input value ignored." + ) + self.workers = None + self.workers = self.workers or processutils.get_worker_count() self.server = wsgi.Server( cfg.CONF, - name, + "qinling_api", self.app, host=cfg.CONF.api.host, port=cfg.CONF.api.port, diff --git a/qinling/cmd/api.py b/qinling/cmd/api.py new file mode 100644 index 00000000..616366ca --- /dev/null +++ b/qinling/cmd/api.py @@ -0,0 +1,49 @@ +# Copyright 2017 - Catalyst IT Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import eventlet +eventlet.monkey_patch() + +import sys + +from oslo_config import cfg +from oslo_log import log as logging +from oslo_service import service + +from qinling.api import service as api_service +from qinling import config +from qinling import rpc +from qinling.utils import common + +CONF = cfg.CONF + + +def main(): + try: + config.parse_args(args=common.get_properly_ordered_parameters()) + common.print_server_info("api") + logging.setup(CONF, 'qinling') + # Initialize RPC configuration. + rpc.get_transport() + + api_server = api_service.WSGIService() + launcher = service.launch(CONF, api_server, workers=api_server.workers) + launcher.wait() + except RuntimeError as excp: + sys.stderr.write("ERROR: %s\n" % excp) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/qinling/cmd/engine.py b/qinling/cmd/engine.py new file mode 100644 index 00000000..ff7116cb --- /dev/null +++ b/qinling/cmd/engine.py @@ -0,0 +1,49 @@ +# Copyright 2017 - Catalyst IT Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +import cotyledon +from oslo_config import cfg +from oslo_log import log as logging + +from qinling import config +from qinling.engine import service as eng_service +from qinling import rpc +from qinling.utils import common + +CONF = cfg.CONF + + +def main(): + try: + config.parse_args(args=common.get_properly_ordered_parameters()) + common.print_server_info("engine") + logging.setup(CONF, 'qinling') + # Initialize RPC configuration. + rpc.get_transport() + + sm = cotyledon.ServiceManager() + sm.add( + eng_service.EngineService, + workers=1, + ) + sm.run() + except RuntimeError as excp: + sys.stderr.write("ERROR: %s\n" % excp) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/qinling/cmd/launch.py b/qinling/cmd/launch.py deleted file mode 100644 index 6a6aa154..00000000 --- a/qinling/cmd/launch.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright 2017 - Catalyst IT Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys - -import eventlet - -eventlet.monkey_patch( - os=True, - select=True, - socket=True, - thread=False if '--use-debugger' in sys.argv else True, - time=True) - -import os - -# If ../qinling/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'qinling', '__init__.py')): - sys.path.insert(0, POSSIBLE_TOPDIR) - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import service - -from qinling.api import service as api_service -from qinling import config -from qinling.engine import service as eng_service -from qinling import rpc -from qinling import version - -CONF = cfg.CONF - - -def launch_api(): - try: - server = api_service.WSGIService('qinling_api') - launcher = service.launch(CONF, server, workers=server.workers) - return launcher - except Exception as e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) - - -def launch_engine(): - try: - server = eng_service.EngineService() - launcher = service.launch(CONF, server) - return launcher - except Exception as e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) - - -def launch_any(options): - launchers = [LAUNCH_OPTIONS[option]() for option in options] - for l in launchers: - l.wait() - - -LAUNCH_OPTIONS = { - 'api': launch_api, - 'engine': launch_engine -} - -QINLING_TITLE = r""" - /^L_ ,."\ - /~\ __ /~ \ ./ \ - / _\ _/ \ /T~\|~\_\ / \_ /~| _^ - / \ /W \ / V^\/X /~ T . \/ \ ,v-./ - ,'`-. /~ ^ H , . \/ ; . \ `. \-' / - M ~ | . ; / , _ : . ~\_,-' - / ~ . \ / : ' \ ,/` - I o. ^ oP '98b - _ 9.` `\9b. - 8oO888. oO888P d888b9bo. .8o 888o. 8bo. o 988o. - 88888888888888888888888888bo.98888888bo. 98888bo. .d888P - 88888888888888888888888888888888888888888888888888888888888 - _ __ _ - ___ _ (_) ___ / / (_) ___ ___ _ - / _ `/ / / / _ \ / / / / / _ \ / _ `/ - \_, / /_/ /_//_//_/ /_/ /_//_/ \_, / - /_/ /___/ - -Function as a Service in OpenStack, version: %s -""" % version.version_string() - - -def print_server_info(): - print(QINLING_TITLE) - - comp_str = ("[%s]" % ','.join(LAUNCH_OPTIONS) - if cfg.CONF.server == ['all'] else cfg.CONF.server) - - print('Launching server components %s...' % comp_str) - - -def get_properly_ordered_parameters(): - """Orders launch parameters in the right order. - - In oslo it's important the order of the launch parameters. - if --config-file came after the command line parameters the command - line parameters are ignored. - So to make user command line parameters are never ignored this method - moves --config-file to be always first. - """ - args = sys.argv[1:] - - for arg in sys.argv[1:]: - if arg == '--config-file' or arg.startswith('--config-file='): - if "=" in arg: - conf_file_value = arg.split("=", 1)[1] - else: - conf_file_value = args[args.index(arg) + 1] - args.remove(conf_file_value) - args.remove(arg) - args.insert(0, "--config-file") - args.insert(1, conf_file_value) - - return args - - -def main(): - try: - config.parse_args(get_properly_ordered_parameters()) - print_server_info() - - logging.setup(CONF, 'Qinling') - - # Initialize RPC configuration. - rpc.get_transport() - - if cfg.CONF.server == ['all']: - launch_any(LAUNCH_OPTIONS.keys()) - else: - if set(cfg.CONF.server) - set(LAUNCH_OPTIONS.keys()): - raise Exception('Valid options are all or any combination of ' - ', '.join(LAUNCH_OPTIONS.keys())) - - launch_any(set(cfg.CONF.server)) - - except RuntimeError as excp: - sys.stderr.write("ERROR: %s\n" % excp) - sys.exit(1) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/qinling/config.py b/qinling/config.py index f1a2f023..8ff834a9 100644 --- a/qinling/config.py +++ b/qinling/config.py @@ -38,6 +38,7 @@ api_opts = [ ), cfg.IntOpt( 'api_workers', + default=1, help='Number of workers for Qinling API service ' 'default is equal to the number of CPUs available if that can ' 'be determined, else a default worker count of 1 is returned.' @@ -134,7 +135,7 @@ kubernetes_opts = [ ), cfg.StrOpt( 'kube_host', - default='127.0.0.1:8001', + default='http://127.0.0.1:8001', help='Kubernetes server address, e.g. you can start a proxy to the ' 'Kubernetes API server by using "kubectl proxy" command.' ), @@ -194,6 +195,7 @@ def parse_args(args=None, usage=None, default_config_files=None): 'keystoneclient=INFO', 'requests.packages.urllib3.connectionpool=CRITICAL', 'urllib3.connectionpool=CRITICAL', + 'cotyledon=INFO' ] default_log_levels = log.get_default_log_levels() default_log_levels.extend(_DEFAULT_LOG_LEVELS) diff --git a/qinling/engine/service.py b/qinling/engine/service.py index 21062e2b..935ecc91 100644 --- a/qinling/engine/service.py +++ b/qinling/engine/service.py @@ -12,11 +12,11 @@ # License for the specific language governing permissions and limitations # under the License. +import cotyledon from oslo_config import cfg from oslo_log import log as logging import oslo_messaging as messaging from oslo_messaging.rpc import dispatcher -from oslo_service import service from qinling.db import api as db_api from qinling.engine import default_engine as engine @@ -28,12 +28,12 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF -class EngineService(service.Service): - def __init__(self): - super(EngineService, self).__init__() +class EngineService(cotyledon.Service): + def __init__(self, worker_id): + super(EngineService, self).__init__(worker_id) self.server = None - def start(self): + def run(self): orchestrator = orchestra_base.load_orchestrator(CONF) db_api.setup_db() @@ -47,11 +47,10 @@ class EngineService(service.Service): transport, target, [endpoint], - executor='eventlet', + executor='threading', access_policy=access_policy, serializer=rpc.ContextSerializer( - messaging.serializer.JsonPayloadSerializer() - ) + messaging.serializer.JsonPayloadSerializer()) ) LOG.info('Starting function mapping periodic task...') @@ -60,25 +59,10 @@ class EngineService(service.Service): LOG.info('Starting engine...') self.server.start() - super(EngineService, self).start() - - def stop(self, graceful=False): + def terminate(self): periodics.stop() if self.server: LOG.info('Stopping engine...') self.server.stop() - if graceful: - LOG.info( - 'Consumer successfully stopped. Waiting for final ' - 'messages to be processed...' - ) - self.server.wait() - - super(EngineService, self).stop(graceful=graceful) - - def reset(self): - if self.server: - self.server.reset() - - super(EngineService, self).reset() + self.server.wait() diff --git a/qinling/engine/utils.py b/qinling/engine/utils.py index 24a4787b..8d9a3bff 100644 --- a/qinling/engine/utils.py +++ b/qinling/engine/utils.py @@ -82,7 +82,7 @@ def get_request_data(conf, function_id, execution_id, input, entry, trust_id): data.update( { 'token': ctx.auth_token, - 'auth_url': conf.keystone_authtoken.auth_uri, + 'auth_url': conf.keystone_authtoken.www_authenticate_uri, 'username': conf.keystone_authtoken.username, 'password': conf.keystone_authtoken.password, 'trust_id': trust_id diff --git a/qinling/orchestrator/kubernetes/manager.py b/qinling/orchestrator/kubernetes/manager.py index b5fc8e6f..36ba04a4 100644 --- a/qinling/orchestrator/kubernetes/manager.py +++ b/qinling/orchestrator/kubernetes/manager.py @@ -18,7 +18,6 @@ import os import time import jinja2 -from kubernetes import client from oslo_log import log as logging import requests import tenacity @@ -27,6 +26,7 @@ import yaml from qinling.engine import utils from qinling import exceptions as exc from qinling.orchestrator import base +from qinling.orchestrator.kubernetes import utils as k8s_util from qinling.utils import common LOG = logging.getLogger(__name__) @@ -38,9 +38,9 @@ class KubernetesManager(base.OrchestratorBase): def __init__(self, conf): self.conf = conf - client.Configuration().host = self.conf.kubernetes.kube_host - self.v1 = client.CoreV1Api() - self.v1extention = client.ExtensionsV1beta1Api() + clients = k8s_util.get_k8s_clients(self.conf) + self.v1 = clients['v1'] + self.v1extention = clients['v1extention'] # Create namespace if not exists self._ensure_namespace() diff --git a/qinling/orchestrator/kubernetes/utils.py b/qinling/orchestrator/kubernetes/utils.py new file mode 100644 index 00000000..14e58e8a --- /dev/null +++ b/qinling/orchestrator/kubernetes/utils.py @@ -0,0 +1,34 @@ +# Copyright 2018 Catalyst IT Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from kubernetes.client import api_client +from kubernetes.client.apis import core_v1_api +from kubernetes.client.apis import extensions_v1beta1_api +from kubernetes.client import configuration as k8s_config + + +def get_k8s_clients(conf): + config = k8s_config.Configuration() + config.host = conf.kubernetes.kube_host + config.verify_ssl = False + client = api_client.ApiClient(configuration=config) + v1 = core_v1_api.CoreV1Api(client) + v1extention = extensions_v1beta1_api.ExtensionsV1beta1Api(client) + + clients = { + 'v1': v1, + 'v1extention': v1extention + } + + return clients diff --git a/qinling/storage/file_system.py b/qinling/storage/file_system.py index f85dd9b6..270f0698 100644 --- a/qinling/storage/file_system.py +++ b/qinling/storage/file_system.py @@ -39,7 +39,7 @@ class FileSystemStorage(base.PackageStorage): :param function: Function ID. :param data: Package data. """ - LOG.info( + LOG.debug( 'Store package, function: %s, project: %s', function, project_id ) @@ -64,7 +64,7 @@ class FileSystemStorage(base.PackageStorage): :param function: Function ID. :return: File descriptor that needs to close outside. """ - LOG.info( + LOG.debug( 'Get package data, function: %s, project: %s', function, project_id ) @@ -80,11 +80,12 @@ class FileSystemStorage(base.PackageStorage): ) f = open(func_zip, 'rb') + LOG.debug('Found package data') return f def delete(self, project_id, function): - LOG.info( + LOG.debug( 'Delete package data, function: %s, project: %s', function, project_id ) diff --git a/qinling/utils/common.py b/qinling/utils/common.py index 7609b9c4..b09887a7 100644 --- a/qinling/utils/common.py +++ b/qinling/utils/common.py @@ -12,11 +12,65 @@ # See the License for the specific language governing permissions and # limitations under the License. import functools +import sys import warnings from oslo_utils import uuidutils import six +from qinling import version + + +def print_server_info(service): + QINLING_TITLE = r""" + /^L_ ,."\ + /~\ __ /~ \ ./ \ + / _\ _/ \ /T~\|~\_\ / \_ /~| _^ + / \ /W \ / V^\/X /~ T . \/ \ ,v-./ + ,'`-. /~ ^ H , . \/ ; . \ `. \-' / + M ~ | . ; / , _ : . ~\_,-' + / ~ . \ / : ' \ ,/` + I o. ^ oP '98b - _ 9.` `\9b. + 8oO888. oO888P d888b9bo. .8o 888o. 8bo. o 988o. + 88888888888888888888888888bo.98888888bo. 98888bo. .d888P + 88888888888888888888888888888888888888888888888888888888888 + _ __ _ + ___ _ (_) ___ / / (_) ___ ___ _ + / _ `/ / / / _ \ / / / / / _ \ / _ `/ + \_, / /_/ /_//_//_/ /_/ /_//_/ \_, / + /_/ /___/ + + Function as a Service in OpenStack, version: %s + """ % version.version_string() + + print(QINLING_TITLE) + print('Launching server components %s...' % service) + + +def get_properly_ordered_parameters(): + """Orders launch parameters in the right order. + + In oslo it's important the order of the launch parameters. + if --config-file came after the command line parameters the command + line parameters are ignored. + So to make user command line parameters are never ignored this method + moves --config-file to be always first. + """ + args = sys.argv[1:] + + for arg in sys.argv[1:]: + if arg == '--config-file' or arg.startswith('--config-file='): + if "=" in arg: + conf_file_value = arg.split("=", 1)[1] + else: + conf_file_value = args[args.index(arg) + 1] + args.remove(conf_file_value) + args.remove(arg) + args.insert(0, "--config-file") + args.insert(1, conf_file_value) + + return args + def convert_dict_to_string(d): temp_list = ['%s=%s' % (k, v) for k, v in d.items()] diff --git a/qinling/utils/openstack/keystone.py b/qinling/utils/openstack/keystone.py index f609746f..6a99917c 100644 --- a/qinling/utils/openstack/keystone.py +++ b/qinling/utils/openstack/keystone.py @@ -30,7 +30,7 @@ def _get_user_keystone_session(): ctx = context.get_ctx() auth = v3.Token( - auth_url=CONF.keystone_authtoken.auth_uri, + auth_url=CONF.keystone_authtoken.www_authenticate_uri, token=ctx.auth_token, ) @@ -49,7 +49,7 @@ def get_swiftclient(): @common.disable_ssl_warnings def get_user_client(): ctx = context.get_ctx() - auth_url = CONF.keystone_authtoken.auth_uri + auth_url = CONF.keystone_authtoken.www_authenticate_uri client = ks_client.Client( user_id=ctx.user, token=ctx.auth_token, @@ -67,7 +67,7 @@ def get_service_client(): username=CONF.keystone_authtoken.username, password=CONF.keystone_authtoken.password, project_name=CONF.keystone_authtoken.project_name, - auth_url=CONF.keystone_authtoken.auth_uri, + auth_url=CONF.keystone_authtoken.www_authenticate_uri, user_domain_name=CONF.keystone_authtoken.user_domain_name, project_domain_name=CONF.keystone_authtoken.project_domain_name ) @@ -80,7 +80,7 @@ def get_trust_client(trust_id): client = ks_client.Client( username=CONF.keystone_authtoken.username, password=CONF.keystone_authtoken.password, - auth_url=CONF.keystone_authtoken.auth_uri, + auth_url=CONF.keystone_authtoken.www_authenticate_uri, trust_id=trust_id ) diff --git a/qinling_tempest_plugin/config.py b/qinling_tempest_plugin/config.py index ec82b1b0..ae422aad 100644 --- a/qinling_tempest_plugin/config.py +++ b/qinling_tempest_plugin/config.py @@ -41,6 +41,6 @@ QinlingGroup = [ 'publicURL', 'adminURL', 'internalURL'], help="The endpoint type to use for the qinling service."), cfg.StrOpt('kube_host', - default='127.0.0.1:8001', + default='http://127.0.0.1:8001', help="The Kubernetes service address."), ] diff --git a/qinling_tempest_plugin/tests/base.py b/qinling_tempest_plugin/tests/base.py index 5b9c04dc..292ef4ad 100644 --- a/qinling_tempest_plugin/tests/base.py +++ b/qinling_tempest_plugin/tests/base.py @@ -13,12 +13,13 @@ # limitations under the License. import os -from kubernetes import client as k8s_client from tempest import config from tempest.lib.common.utils import data_utils from tempest import test import tenacity +from qinling_tempest_plugin.tests import utils + CONF = config.CONF @@ -41,9 +42,9 @@ class BaseQinlingTest(test.BaseTestCase): cls.admin_client = cls.os_admin.qinling.QinlingClient() # Initilize k8s client - k8s_client.Configuration().host = CONF.qinling.kube_host - cls.k8s_v1 = k8s_client.CoreV1Api() - cls.k8s_v1extention = k8s_client.ExtensionsV1beta1Api() + clients = utils.get_k8s_clients(CONF) + cls.k8s_v1 = clients['v1'] + cls.k8s_v1extention = clients['v1extention'] cls.namespace = 'qinling' @tenacity.retry( diff --git a/qinling_tempest_plugin/tests/utils.py b/qinling_tempest_plugin/tests/utils.py new file mode 100644 index 00000000..54a87416 --- /dev/null +++ b/qinling_tempest_plugin/tests/utils.py @@ -0,0 +1,34 @@ +# Copyright 2018 Catalyst IT Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from kubernetes.client import api_client +from kubernetes.client.apis import core_v1_api +from kubernetes.client.apis import extensions_v1beta1_api +from kubernetes.client import configuration as k8s_config + + +def get_k8s_clients(conf): + config = k8s_config.Configuration() + config.host = conf.qinling.kube_host + config.verify_ssl = False + client = api_client.ApiClient(configuration=config) + v1 = core_v1_api.CoreV1Api(client) + v1extention = extensions_v1beta1_api.ExtensionsV1beta1Api(client) + + clients = { + 'v1': v1, + 'v1extention': v1extention + } + + return clients diff --git a/requirements.txt b/requirements.txt index 76dc4a45..3b6834a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,30 +4,30 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 Babel!=2.4.0,>=2.3.4 # BSD -eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT -keystoneauth1>=2.21.0 # Apache-2.0 -keystonemiddleware>=4.12.0 # Apache-2.0 -oslo.concurrency>=3.8.0 # Apache-2.0 -oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0 -oslo.db>=4.23.0 # Apache-2.0 -oslo.messaging!=5.25.0,>=5.24.2 # Apache-2.0 -oslo.policy>=1.23.0 # Apache-2.0 -oslo.utils>=3.20.0 # Apache-2.0 -oslo.log>=3.22.0 # Apache-2.0 -oslo.serialization>=1.10.0 # Apache-2.0 -oslo.service>=1.10.0 # Apache-2.0 +keystoneauth1>=3.3.0 # Apache-2.0 +keystonemiddleware>=4.17.0 # Apache-2.0 +oslo.concurrency>=3.20.0 # Apache-2.0 +oslo.config>=5.1.0 # Apache-2.0 +oslo.db>=4.27.0 # Apache-2.0 +oslo.messaging>=5.29.0 # Apache-2.0 +oslo.policy>=1.30.0 # Apache-2.0 +oslo.utils>=3.33.0 # Apache-2.0 +oslo.log>=3.30.0 # Apache-2.0 +oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 +oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD -setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,>=16.0 # PSF/ZPL -six>=1.9.0 # MIT +setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,>=16.0 # PSF/ZPL +six>=1.10.0 # MIT SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT sqlalchemy-migrate>=0.11.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 -WSME>=0.8 # MIT -kubernetes>=1.0.0b1 # Apache-2.0 -PyYAML>=3.10.0 # MIT +WSME>=0.8.0 # MIT +kubernetes>=4.0.0 # Apache-2.0 +PyYAML>=3.10 # MIT python-swiftclient>=3.2.0 # Apache-2.0 croniter>=0.3.4 # MIT License -python-dateutil>=2.4.2 # BSD -tenacity>=3.2.1 # Apache-2.0 -PyMySQL>=0.7.6 # MIT License -etcd3gw>=0.2.0 # Apache-2.0 +python-dateutil>=2.4.2 # BSD +tenacity>=3.2.1 # Apache-2.0 +PyMySQL>=0.7.6 # MIT License +etcd3gw>=0.2.0 # Apache-2.0 +cotyledon>=1.3.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index b7133124..320c9b5e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,7 +25,8 @@ packages = [entry_points] console_scripts = - qinling-server = qinling.cmd.launch:main + qinling-api = qinling.cmd.api:main + qinling-engine = qinling.cmd.engine:main qinling-db-manage = qinling.db.sqlalchemy.migration.cli:main qinling.storage.provider: diff --git a/setup.py b/setup.py index 566d8443..056c16c2 100644 --- a/setup.py +++ b/setup.py @@ -25,5 +25,5 @@ except ImportError: pass setuptools.setup( - setup_requires=['pbr>=2.0.0'], + setup_requires=['pbr'], pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt index c0dcdc8a..b8f95300 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,11 +8,10 @@ sphinx>=1.6.2 # BSD oslotest>=1.10.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD -testtools>=1.4.0 # MIT -tempest>=16.1.0 # Apache-2.0 -futurist>=1.2.0 # Apache-2.0 +testtools>=2.2.0 # MIT +tempest>=17.1.0 # Apache-2.0 +futurist>=1.2.0 # Apache-2.0 +openstackdocstheme>=1.17.0 # Apache-2.0 +reno>=2.5.0 # Apache-2.0 -openstackdocstheme>=1.16.0 # Apache-2.0 - -# releasenotes -reno!=2.3.1,>=1.8.0 # Apache-2.0 +kubernetes>=4.0.0 # Apache-2.0