From ac039414ce997cfcafa09efa9e089e09c3058b70 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Tue, 24 Nov 2015 19:08:14 -0500 Subject: [PATCH] Remove eventlet support Eventlet has been deprecated since the Kilo release and is being removed in Newton. A follow on patch will be proposed to remove the [ssl] section since it is now redundant. Co-Authored-By: Grzegorz Grasza Partially implements: bp removed-as-of-newton Change-Id: I963d94bbd188dbb6eba68623a42c5bc3f2289da4 --- config-generator/keystone.conf | 1 - doc/source/conf.py | 4 +- doc/source/configuration.rst | 66 +----- doc/source/http-api.rst | 5 +- doc/source/index.rst | 1 - doc/source/man/keystone-all.rst | 112 --------- keystone/cmd/all.py | 39 ---- keystone/cmd/manage.py | 3 - keystone/common/config.py | 76 +------ keystone/common/environment/__init__.py | 102 --------- .../common/environment/eventlet_server.py | 212 ------------------ keystone/common/openssl.py | 32 ++- keystone/federation/idp.py | 3 +- keystone/server/eventlet.py | 158 ------------- keystone/server/wsgi.py | 3 - keystone/tests/unit/core.py | 8 +- keystone/tests/unit/ksfixtures/appserver.py | 79 ------- keystone/tests/unit/test_cert_setup.py | 68 +----- keystone/tests/unit/test_ipv6.py | 51 ----- keystone/tests/unit/test_ssl.py | 186 --------------- keystone/tests/unit/test_token_provider.py | 17 -- keystone/tests/unit/test_v3_federation.py | 4 +- keystone/tests/unit/test_versions.py | 64 ++---- keystone/tests/unit/test_wsgi.py | 102 --------- keystone/token/providers/pki.py | 5 +- keystone/token/providers/pkiz.py | 5 +- ...removed-as-of-newton-721c06b5dcb1b34a.yaml | 10 +- requirements.txt | 3 - setup.cfg | 1 - tox.ini | 2 +- 30 files changed, 82 insertions(+), 1340 deletions(-) delete mode 100644 doc/source/man/keystone-all.rst delete mode 100644 keystone/cmd/all.py delete mode 100644 keystone/common/environment/__init__.py delete mode 100644 keystone/common/environment/eventlet_server.py delete mode 100644 keystone/server/eventlet.py delete mode 100644 keystone/tests/unit/ksfixtures/appserver.py delete mode 100644 keystone/tests/unit/test_ipv6.py delete mode 100644 keystone/tests/unit/test_ssl.py diff --git a/config-generator/keystone.conf b/config-generator/keystone.conf index 8713c0258d..74d3e2b6da 100644 --- a/config-generator/keystone.conf +++ b/config-generator/keystone.conf @@ -9,7 +9,6 @@ namespace = oslo.messaging namespace = oslo.policy namespace = oslo.db namespace = oslo.middleware -namespace = oslo.service.service namespace = osprofiler # We don't use oslo.concurrency config options in # keystone now, just in case it slips through unnoticed. diff --git a/doc/source/conf.py b/doc/source/conf.py index 1037c39ec3..6bc618856a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -111,9 +111,7 @@ modindex_common_prefix = ['keystone.'] man_pages = [ ('man/keystone-manage', 'keystone-manage', u'Keystone Management Utility', - [u'OpenStack'], 1), - ('man/keystone-all', 'keystone-all', u'Keystone Startup Command', - [u'OpenStack'], 1), + [u'OpenStack'], 1) ] diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index e78c0ac639..bb5257f610 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -22,7 +22,6 @@ Configuring Keystone :maxdepth: 1 man/keystone-manage - man/keystone-all Once Keystone is installed, it is configured via a primary configuration file (``etc/keystone.conf``), a PasteDeploy configuration file @@ -45,48 +44,6 @@ To make the above change persistent, ``net.ipv4.ip_local_reserved_ports = 35357`` should be added to ``/etc/sysctl.conf`` or to ``/etc/sysctl.d/keystone.conf``. -Starting and Stopping Keystone under Eventlet -============================================= - -.. WARNING:: - - Running keystone under eventlet has been deprecated as of the Kilo release. - Support for utilizing eventlet will be removed as of the M-release. The - recommended deployment is to run keystone in a WSGI server such as Apache - httpd with ``mod_wsgi``. - -Keystone can be run using either its built-in eventlet server or it can be run -embedded in a web server. While the eventlet server is convenient and easy to -use, it's lacking in security features that have been developed into Internet- -based web servers over the years. As such, running the eventlet server as -described in this section is not recommended. - -Start Keystone services using the command: - -.. code-block:: bash - - $ keystone-all - -Invoking this command starts up two ``wsgi.Server`` instances, ``admin`` (the -administration API) and ``main`` (the primary/public API interface). Both -services are configured to run in a single process. - -.. NOTE:: - - The separation into ``admin`` and ``main`` interfaces is a historical - anomaly. The new V3 API provides the same interface on both the admin and - main interfaces (this can be configured in ``keystone-paste.ini``, but the - default is to have both the same). The V2.0 API provides a limited public - API (getting and validating tokens) on ``main``, and an administrative API - (which can include creating users and such) on the ``admin`` interface. - -Stop the process using ``Control-C``. - -.. NOTE:: - - If you have not already configured Keystone, it may not start as expected. - - Configuration Files =================== @@ -115,8 +72,6 @@ The primary configuration file is organized into the following sections: * ``[credential]`` - Credential system driver configuration * ``[endpoint_filter]`` - Endpoint filtering configuration * ``[endpoint_policy]`` - Endpoint policy configuration -* ``[eventlet_server]`` - Eventlet server configuration -* ``[eventlet_server_ssl]`` - Eventlet server SSL configuration * ``[federation]`` - Federation driver configuration * ``[identity]`` - Identity system driver configuration * ``[identity_mapping]`` - Identity mapping system driver configuration @@ -977,32 +932,19 @@ certificates are just provided as an example. Configuration ^^^^^^^^^^^^^ -To enable SSL modify the ``etc/keystone.conf`` file under the ``[ssl]`` and -``[eventlet_server_ssl]`` sections. The following is an SSL configuration -example using the included sample certificates: +To enable SSL a deployment should configure a web server (such as Apache) to +use SSL. Keystone is able to generate SSL certificates by modifying the +``[ssl]`` section in the ``etc/keystone.conf`` file. The following is an SSL +configuration example using the included sample certificates: .. code-block:: ini - [eventlet_server_ssl] - enable = True - certfile = - keyfile = - ca_certs = - cert_required = False - [ssl] ca_key = key_size = 1024 valid_days=3650 cert_subject=/C=US/ST=Unset/L=Unset/O=Unset/CN=localhost -* ``enable``: True enables SSL. Defaults to False. -* ``certfile``: Path to Keystone public certificate file. -* ``keyfile``: Path to Keystone private certificate file. If the private key is - included in the certfile, the keyfile may be omitted. -* ``ca_certs``: Path to CA trust chain. -* ``cert_required``: Requires client certificate. Defaults to False. - When generating SSL certificates the following values are read * ``key_size``: Key size to create. Defaults to 1024. diff --git a/doc/source/http-api.rst b/doc/source/http-api.rst index 3b91588125..2666f48329 100644 --- a/doc/source/http-api.rst +++ b/doc/source/http-api.rst @@ -222,6 +222,5 @@ HTTP/1.1 Chunked Encoding Running Keystone under HTTPD in the recommended (and tested) configuration does not support the use of ``Transfer-Encoding: chunked``. This is due to a limitation with the WSGI spec - and the implementation used by ``mod_wsgi``. Support for chunked encoding under ``eventlet`` - may or may not continue. It is recommended that all clients assume Keystone will not support - ``Transfer-Encoding: chunked``. + and the implementation used by ``mod_wsgi``. It is recommended that all + clients assume Keystone will not support ``Transfer-Encoding: chunked``. diff --git a/doc/source/index.rst b/doc/source/index.rst index 402662d2a7..94fb22185a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -78,7 +78,6 @@ Man Pages .. toctree:: :maxdepth: 1 - man/keystone-all man/keystone-manage Developers Documentation diff --git a/doc/source/man/keystone-all.rst b/doc/source/man/keystone-all.rst deleted file mode 100644 index b9c219b36e..0000000000 --- a/doc/source/man/keystone-all.rst +++ /dev/null @@ -1,112 +0,0 @@ -============ -keystone-all -============ - ------------------------- -Keystone Startup Command ------------------------- - -:Author: openstack@lists.openstack.org -:Date: 2015-10-15 -:Copyright: OpenStack Foundation -:Version: 8.0.0 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - -:: - - keystone-all [-h] [--config-dir DIR] [--config-file PATH] [--debug] - [--log-config-append PATH] [--log-date-format DATE_FORMAT] - [--log-dir LOG_DIR] [--log-file PATH] - [--log-format FORMAT] [--nodebug] [--nostandard-threads] - [--nouse-syslog] [--nouse-syslog-rfc-format] [--noverbose] - [--pydev-debug-host PYDEV_DEBUG_HOST] - [--pydev-debug-port PYDEV_DEBUG_PORT] [--standard-threads] - [--syslog-log-facility SYSLOG_LOG_FACILITY] [--use-syslog] - [--use-syslog-rfc-format] [--verbose] [--version] - -DESCRIPTION -=========== - -keystone-all starts both the service and administrative APIs in a single -process to provide catalog, authorization, and authentication services for -OpenStack. - -OPTIONS -======= - - -h, --help show this help message and exit - --config-dir DIR Path to a config directory to pull \*.conf files from. - This file set is sorted, so as to provide a - predictable parse order if individual options are - over-ridden. The set is parsed after the file(s) - specified via previous --config-file, arguments hence - over-ridden options in the directory take precedence. - --config-file PATH Path to a config file to use. Multiple config files - can be specified, with values in later files taking - precedence. The default files used are: None. - --debug, -d Print debugging output (set logging level to DEBUG - instead of default WARNING level). - --log-config-append PATH, --log_config PATH - The name of a logging configuration file. This file is - appended to any existing logging configuration files. - For details about logging configuration files, see the - Python logging module documentation. - --log-date-format DATE_FORMAT - Format string for %(asctime)s in log records. Default: - None . - --log-dir LOG_DIR, --logdir LOG_DIR - (Optional) The base directory used for relative --log- - file paths. - --log-file PATH, --logfile PATH - (Optional) Name of log file to output to. If no - default is set, logging will go to stdout. - --log-format FORMAT DEPRECATED. A logging.Formatter log message format - string which may use any of the available - logging.LogRecord attributes. This option is - deprecated. Please use logging_context_format_string - and logging_default_format_string instead. - --nodebug The inverse of --debug - --nostandard-threads The inverse of --standard-threads - --nouse-syslog The inverse of --use-syslog - --nouse-syslog-rfc-format - The inverse of --use-syslog-rfc-format - --noverbose The inverse of --verbose - --pydev-debug-host PYDEV_DEBUG_HOST - Host to connect to for remote debugger. - --pydev-debug-port PYDEV_DEBUG_PORT - Port to connect to for remote debugger. - --standard-threads Do not monkey-patch threading system modules. - --syslog-log-facility SYSLOG_LOG_FACILITY - Syslog facility to receive log lines. - --use-syslog Use syslog for logging. Existing syslog format is - DEPRECATED during I, and will change in J to honor - RFC5424. - --use-syslog-rfc-format - (Optional) Enables or disables syslog rfc5424 format - for logging. If enabled, prefixes the MSG part of the - syslog message with APP-NAME (RFC5424). The format - without the APP-NAME is deprecated in I, and will be - removed in J. - --verbose, -v Print more verbose output (set logging level to INFO - instead of default WARNING level). - --version show program's version number and exit - -FILES -===== - -None - -SEE ALSO -======== - -* `OpenStack Keystone `__ - -SOURCE -====== - -* Keystone source is managed in Gerrit git `Keystone `__ -* Keystone bugs are managed at Launchpad `Keystone `__ diff --git a/keystone/cmd/all.py b/keystone/cmd/all.py deleted file mode 100644 index c583accd0f..0000000000 --- a/keystone/cmd/all.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2013 OpenStack Foundation -# -# 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 os -import sys - - -# If ../../keystone/__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(__file__), - os.pardir, - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, - 'keystone', - '__init__.py')): - sys.path.insert(0, possible_topdir) - - -from keystone.server import eventlet as eventlet_server - - -# entry point. -def main(): - eventlet_server.run(possible_topdir) diff --git a/keystone/cmd/manage.py b/keystone/cmd/manage.py index 707c9933df..383892207c 100644 --- a/keystone/cmd/manage.py +++ b/keystone/cmd/manage.py @@ -30,13 +30,10 @@ if os.path.exists(os.path.join(possible_topdir, sys.path.insert(0, possible_topdir) from keystone.cmd import cli -from keystone.common import environment # entry point. def main(): - environment.use_stdlib() - dev_conf = os.path.join(possible_topdir, 'etc', 'keystone.conf') diff --git a/keystone/common/config.py b/keystone/common/config.py index 620000d299..06e32a3f03 100644 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -49,6 +49,11 @@ _DEPRECATE_DII_MSG = ('The option to set domain_id_immutable to false ' 'has been deprecated in the M release and will ' 'be removed in the O release.') +_DEPRECATE_EVENTLET_MSG = ('Support for running keystone under eventlet has ' + 'been removed in the N release. These options ' + 'remain for backwards compatibility because they ' + 'are used for URL substitutions.') + FILE_OPTIONS = { None: [ cfg.StrOpt('admin_token', secret=True, default=None, @@ -999,20 +1004,6 @@ FILE_OPTIONS = { 'assertions.'), ], 'eventlet_server': [ - cfg.IntOpt('public_workers', - deprecated_name='public_workers', - deprecated_group='DEFAULT', - deprecated_for_removal=True, - help='The number of worker processes to serve the public ' - 'eventlet application. Defaults to number of CPUs ' - '(minimum of 2).'), - cfg.IntOpt('admin_workers', - deprecated_name='admin_workers', - deprecated_group='DEFAULT', - deprecated_for_removal=True, - help='The number of worker processes to serve the admin ' - 'eventlet application. Defaults to number of CPUs ' - '(minimum of 2).'), cfg.StrOpt('public_bind_host', default='0.0.0.0', # nosec : Bind to all interfaces by # default for backwards compatibility. @@ -1021,12 +1012,14 @@ FILE_OPTIONS = { cfg.DeprecatedOpt('public_bind_host', group='DEFAULT'), ], deprecated_for_removal=True, + deprecated_reason=_DEPRECATE_EVENTLET_MSG, help='The IP address of the network interface for the ' 'public service to listen on.'), cfg.PortOpt('public_port', default=5000, deprecated_name='public_port', deprecated_group='DEFAULT', deprecated_for_removal=True, + deprecated_reason=_DEPRECATE_EVENTLET_MSG, help='The port number which the public service listens ' 'on.'), cfg.StrOpt('admin_bind_host', @@ -1037,6 +1030,7 @@ FILE_OPTIONS = { cfg.DeprecatedOpt('admin_bind_host', group='DEFAULT')], deprecated_for_removal=True, + deprecated_reason=_DEPRECATE_EVENTLET_MSG, help='The IP address of the network interface for the ' 'admin service to listen on.'), cfg.PortOpt('admin_port', default=35357, @@ -1045,60 +1039,6 @@ FILE_OPTIONS = { deprecated_for_removal=True, help='The port number which the admin service listens ' 'on.'), - cfg.BoolOpt('wsgi_keep_alive', default=True, - help='If set to false, disables keepalives on the server; ' - 'all connections will be closed after serving one ' - 'request.'), - cfg.IntOpt('client_socket_timeout', default=900, - help='Timeout for socket operations on a client ' - 'connection. If an incoming connection is idle for ' - 'this number of seconds it will be closed. A value ' - 'of "0" means wait forever.'), - cfg.BoolOpt('tcp_keepalive', default=False, - deprecated_name='tcp_keepalive', - deprecated_group='DEFAULT', - deprecated_for_removal=True, - help='Set this to true if you want to enable ' - 'TCP_KEEPALIVE on server sockets, i.e. sockets used ' - 'by the Keystone wsgi server for client ' - 'connections.'), - cfg.IntOpt('tcp_keepidle', - default=600, - deprecated_name='tcp_keepidle', - deprecated_group='DEFAULT', - deprecated_for_removal=True, - help='Sets the value of TCP_KEEPIDLE in seconds for each ' - 'server socket. Only applies if tcp_keepalive is ' - 'true. Ignored if system does not support it.'), - ], - 'eventlet_server_ssl': [ - cfg.BoolOpt('enable', default=False, deprecated_name='enable', - deprecated_group='ssl', - deprecated_for_removal=True, - help='Toggle for SSL support on the Keystone ' - 'eventlet servers.'), - cfg.StrOpt('certfile', - default='/etc/keystone/ssl/certs/keystone.pem', - deprecated_name='certfile', deprecated_group='ssl', - deprecated_for_removal=True, - help='Path of the certfile for SSL. For non-production ' - 'environments, you may be interested in using ' - '`keystone-manage ssl_setup` to generate self-signed ' - 'certificates.'), - cfg.StrOpt('keyfile', - default='/etc/keystone/ssl/private/keystonekey.pem', - deprecated_name='keyfile', deprecated_group='ssl', - deprecated_for_removal=True, - help='Path of the keyfile for SSL.'), - cfg.StrOpt('ca_certs', - default='/etc/keystone/ssl/certs/ca.pem', - deprecated_name='ca_certs', deprecated_group='ssl', - deprecated_for_removal=True, - help='Path of the CA cert file for SSL.'), - cfg.BoolOpt('cert_required', default=False, - deprecated_name='cert_required', deprecated_group='ssl', - deprecated_for_removal=True, - help='Require client certificate.'), ], } diff --git a/keystone/common/environment/__init__.py b/keystone/common/environment/__init__.py deleted file mode 100644 index 6748f11542..0000000000 --- a/keystone/common/environment/__init__.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 functools -import os - -from oslo_log import log - - -LOG = log.getLogger(__name__) - - -__all__ = ('Server', 'httplib', 'subprocess') - -_configured = False - -Server = None -httplib = None -subprocess = None - - -def configure_once(name): - """Ensure that environment configuration is only run once. - - If environment is reconfigured in the same way then it is ignored. - It is an error to attempt to reconfigure environment in a different way. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - global _configured - if _configured: - if _configured == name: - return - else: - raise SystemError("Environment has already been " - "configured as %s" % _configured) - - LOG.debug("Environment configured as: %s", name) - _configured = name - return func(*args, **kwargs) - - return wrapper - return decorator - - -@configure_once('eventlet') -def use_eventlet(monkeypatch_thread=None): - global httplib, subprocess, Server - - # This must be set before the initial import of eventlet because if - # dnspython is present in your environment then eventlet monkeypatches - # socket.getaddrinfo() with an implementation which doesn't work for IPv6. - os.environ['EVENTLET_NO_GREENDNS'] = 'yes' - - import eventlet - from eventlet.green import httplib as _httplib - from eventlet.green import subprocess as _subprocess - - from keystone.common.environment import eventlet_server - - if monkeypatch_thread is None: - monkeypatch_thread = not os.getenv('STANDARD_THREADS') - - # Raise the default from 8192 to accommodate large tokens - eventlet.wsgi.MAX_HEADER_LINE = 16384 - - # NOTE(ldbragst): Explicitly declare what should be monkey patched and - # what shouldn't. Doing this allows for more readable code when - # understanding Eventlet in Keystone. The following is a complete list - # of what is monkey patched instead of passing all=False and then passing - # module=True to monkey patch a specific module. - eventlet.patcher.monkey_patch(os=False, select=True, socket=True, - thread=monkeypatch_thread, time=True, - psycopg=False, MySQLdb=False) - - Server = eventlet_server.Server - httplib = _httplib - subprocess = _subprocess - - -@configure_once('stdlib') -def use_stdlib(): - global httplib, subprocess - - import six.moves.http_client as _httplib - import subprocess as _subprocess # nosec : This is used in .federation.idp - # and .common.openssl. See there. - - httplib = _httplib - subprocess = _subprocess diff --git a/keystone/common/environment/eventlet_server.py b/keystone/common/environment/eventlet_server.py deleted file mode 100644 index 430ca3e446..0000000000 --- a/keystone/common/environment/eventlet_server.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2010 OpenStack Foundation -# All Rights Reserved. -# -# 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 errno -import re -import socket -import ssl -import sys - -import eventlet -import eventlet.wsgi -import greenlet -from oslo_config import cfg -from oslo_log import log -from oslo_service import service - -from keystone.i18n import _LE, _LI - - -CONF = cfg.CONF - - -LOG = log.getLogger(__name__) - -# The size of a pool that is used to spawn a single green thread in which -# a wsgi server is then started. The size of one is enough, because in case -# of several workers the parent process forks and each child gets a copy -# of a pool, which does not include any greenthread object as the spawn is -# done after the fork. -POOL_SIZE = 1 - - -class EventletFilteringLogger(object): - # NOTE(morganfainberg): This logger is designed to filter out specific - # Tracebacks to limit the amount of data that eventlet can log. In the - # case of broken sockets (EPIPE and ECONNRESET), we are seeing a huge - # volume of data being written to the logs due to ~14 lines+ per traceback. - # The traceback in these cases are, at best, useful for limited debugging - # cases. - def __init__(self, logger, level=log.INFO): - self.logger = logger - self.level = level - self.regex = re.compile(r'errno (%d|%d)' % - (errno.EPIPE, errno.ECONNRESET), re.IGNORECASE) - - def write(self, msg): - m = self.regex.search(msg) - if m: - self.logger.log(log.logging.DEBUG, 'Error(%s) writing to socket.', - m.group(1)) - else: - self.logger.log(self.level, msg.rstrip()) - - -class Server(service.ServiceBase): - """Server class to manage multiple WSGI sockets and applications.""" - - def __init__(self, application, host=None, port=None, keepalive=False, - keepidle=None): - self.application = application - self.host = host or '0.0.0.0' # nosec : Bind to all interfaces by - # default for backwards compatibility. - self.port = port or 0 - # Pool for a green thread in which wsgi server will be running - self.pool = eventlet.GreenPool(POOL_SIZE) - self.socket_info = {} - self.greenthread = None - self.do_ssl = False - self.cert_required = False - self.keepalive = keepalive - self.keepidle = keepidle - self.socket = None - - def listen(self, key=None, backlog=128): - """Create and start listening on socket. - - Call before forking worker processes. - - Raises Exception if this has already been called. - """ - # TODO(dims): eventlet's green dns/socket module does not actually - # support IPv6 in getaddrinfo(). We need to get around this in the - # future or monitor upstream for a fix. - # Please refer below link - # (https://bitbucket.org/eventlet/eventlet/ - # src/e0f578180d7d82d2ed3d8a96d520103503c524ec/eventlet/support/ - # greendns.py?at=0.12#cl-163) - info = socket.getaddrinfo(self.host, - self.port, - socket.AF_UNSPEC, - socket.SOCK_STREAM)[0] - - try: - self.socket = eventlet.listen(info[-1], family=info[0], - backlog=backlog) - except EnvironmentError: - LOG.error(_LE("Could not bind to %(host)s:%(port)s"), - {'host': self.host, 'port': self.port}) - raise - - LOG.info(_LI('Starting %(arg0)s on %(host)s:%(port)s'), - {'arg0': sys.argv[0], - 'host': self.host, - 'port': self.port}) - - def start(self, key=None, backlog=128): - """Run a WSGI server with the given application.""" - if self.socket is None: - self.listen(key=key, backlog=backlog) - - dup_socket = self.socket.dup() - if key: - self.socket_info[key] = self.socket.getsockname() - # SSL is enabled - if self.do_ssl: - if self.cert_required: - cert_reqs = ssl.CERT_REQUIRED - else: - cert_reqs = ssl.CERT_NONE - - dup_socket = eventlet.wrap_ssl(dup_socket, certfile=self.certfile, - keyfile=self.keyfile, - server_side=True, - cert_reqs=cert_reqs, - ca_certs=self.ca_certs) - - # Optionally enable keepalive on the wsgi socket. - if self.keepalive: - dup_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - - if self.keepidle is not None: - if hasattr(socket, 'TCP_KEEPIDLE'): - dup_socket.setsockopt(socket.IPPROTO_TCP, - socket.TCP_KEEPIDLE, - self.keepidle) - else: - LOG.warning("System does not support TCP_KEEPIDLE but " - "tcp_keepidle has been set. Ignoring.") - - self.greenthread = self.pool.spawn(self._run, - self.application, - dup_socket) - - def set_ssl(self, certfile, keyfile=None, ca_certs=None, - cert_required=True): - self.certfile = certfile - self.keyfile = keyfile - self.ca_certs = ca_certs - self.cert_required = cert_required - self.do_ssl = True - - def stop(self): - if self.greenthread is not None: - self.greenthread.kill() - - def wait(self): - """Wait until all servers have completed running.""" - try: - self.pool.waitall() - except KeyboardInterrupt: # nosec - # If CTRL-C, just break out of the loop. - pass - except greenlet.GreenletExit: # nosec - # If exiting, break out of the loop. - pass - - def reset(self): - """Required by the service interface. - - The service interface is used by the launcher when receiving a - SIGHUP. The service interface is defined in - oslo_service.service.Service. - - Keystone does not need to do anything here. - """ - pass - - def _run(self, application, socket): - """Start a WSGI server with a new green thread pool.""" - logger = log.getLogger('eventlet.wsgi.server') - - # NOTE(dolph): [eventlet_server] client_socket_timeout is required to - # be an integer in keystone.conf, but in order to make - # eventlet.wsgi.server() wait forever, we pass None instead of 0. - socket_timeout = CONF.eventlet_server.client_socket_timeout or None - - try: - eventlet.wsgi.server( - socket, application, log=EventletFilteringLogger(logger), - debug=False, keepalive=CONF.eventlet_server.wsgi_keep_alive, - socket_timeout=socket_timeout) - except greenlet.GreenletExit: # nosec - # Wait until all servers have completed running - pass - except Exception: - LOG.exception(_LE('Server error')) - raise diff --git a/keystone/common/openssl.py b/keystone/common/openssl.py index 0bea6d8e71..c5b1aad5ac 100644 --- a/keystone/common/openssl.py +++ b/keystone/common/openssl.py @@ -14,11 +14,11 @@ # import os +import subprocess # nosec : see comments in the code below from oslo_config import cfg from oslo_log import log -from keystone.common import environment from keystone.common import utils from keystone.i18n import _LI, _LE, _LW @@ -42,22 +42,22 @@ class BaseCertificateConfigure(object): """ - def __init__(self, conf_obj, server_conf_obj, keystone_user, + def __init__(self, conf_obj, keystone_user, keystone_group, rebuild, **kwargs): - self.conf_dir = os.path.dirname(server_conf_obj.ca_certs) + self.conf_dir = os.path.dirname(conf_obj.ca_certs) self.use_keystone_user = keystone_user self.use_keystone_group = keystone_group self.rebuild = rebuild self.ssl_config_file_name = os.path.join(self.conf_dir, "openssl.conf") self.request_file_name = os.path.join(self.conf_dir, "req.pem") self.ssl_dictionary = {'conf_dir': self.conf_dir, - 'ca_cert': server_conf_obj.ca_certs, + 'ca_cert': conf_obj.ca_certs, 'default_md': 'default', 'ssl_config': self.ssl_config_file_name, 'ca_private_key': conf_obj.ca_key, 'request_file': self.request_file_name, - 'signing_key': server_conf_obj.keyfile, - 'signing_cert': server_conf_obj.certfile, + 'signing_key': conf_obj.keyfile, + 'signing_cert': conf_obj.certfile, 'key_size': int(conf_obj.key_size), 'valid_days': int(conf_obj.valid_days), 'cert_subject': conf_obj.cert_subject} @@ -65,12 +65,12 @@ class BaseCertificateConfigure(object): try: # OpenSSL 1.0 and newer support default_md = default, # older versions do not - openssl_ver = environment.subprocess.check_output( # the arguments + openssl_ver = subprocess.check_output( # nosec : the arguments # are hardcoded and just check the openssl version ['openssl', 'version']) if b'OpenSSL 0.' in openssl_ver: self.ssl_dictionary['default_md'] = 'sha1' - except environment.subprocess.CalledProcessError: + except subprocess.CalledProcessError: LOG.warning(_LW('Failed to invoke ``openssl version``, ' 'assuming is v1.0 or newer')) self.ssl_dictionary.update(kwargs) @@ -81,12 +81,12 @@ class BaseCertificateConfigure(object): try: # NOTE(shaleh): use check_output instead of the simpler # `check_call()` in order to log any output from an error. - environment.subprocess.check_output( # the arguments being passed + subprocess.check_output( # nosec : the arguments being passed # in are defined in this file and trusted to build CAs, keys # and certs to_exec, - stderr=environment.subprocess.STDOUT) - except environment.subprocess.CalledProcessError as e: + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: LOG.error(_LE('Command %(to_exec)s exited with %(retcode)s ' '- %(output)s'), {'to_exec': to_exec, @@ -249,9 +249,8 @@ class ConfigurePKI(BaseCertificateConfigure): """ def __init__(self, keystone_user, keystone_group, rebuild=False): - super(ConfigurePKI, self).__init__(CONF.signing, CONF.signing, - keystone_user, keystone_group, - rebuild=rebuild) + super(ConfigurePKI, self).__init__(CONF.signing, keystone_user, + keystone_group, rebuild=rebuild) class ConfigureSSL(BaseCertificateConfigure): @@ -262,9 +261,8 @@ class ConfigureSSL(BaseCertificateConfigure): """ def __init__(self, keystone_user, keystone_group, rebuild=False): - super(ConfigureSSL, self).__init__(CONF.ssl, CONF.eventlet_server_ssl, - keystone_user, keystone_group, - rebuild=rebuild) + super(ConfigureSSL, self).__init__(CONF.ssl, keystone_user, + keystone_group, rebuild=rebuild) BaseCertificateConfigure.sslconfig = """ diff --git a/keystone/federation/idp.py b/keystone/federation/idp.py index 494d58b9dd..17de562a4f 100644 --- a/keystone/federation/idp.py +++ b/keystone/federation/idp.py @@ -12,6 +12,7 @@ import datetime import os +import subprocess # nosec : see comments in the code below import uuid from oslo_config import cfg @@ -31,7 +32,6 @@ xmldsig = importutils.try_import("saml2.xmldsig") if not xmldsig: xmldsig = importutils.try_import("xmldsig") -from keystone.common import environment from keystone.common import utils from keystone import exception from keystone.i18n import _, _LE @@ -422,7 +422,6 @@ def _sign_assertion(assertion): nspair={'saml': saml2.NAMESPACE, 'xmldsig': xmldsig.NAMESPACE})) command_list.append(file_path) - subprocess = environment.subprocess stdout = subprocess.check_output(command_list, # nosec : The contents # of the command list are coming from # a trusted source because the diff --git a/keystone/server/eventlet.py b/keystone/server/eventlet.py deleted file mode 100644 index 446be26cc3..0000000000 --- a/keystone/server/eventlet.py +++ /dev/null @@ -1,158 +0,0 @@ - -# Copyright 2013 OpenStack Foundation -# -# 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 logging -import os -import socket - -from oslo_concurrency import processutils -from oslo_config import cfg -import oslo_i18n -from oslo_service import service -from oslo_service import systemd -import pbr.version - - -# NOTE(dstanek): i18n.enable_lazy() must be called before -# keystone.i18n._() is called to ensure it has the desired lazy lookup -# behavior. This includes cases, like keystone.exceptions, where -# keystone.i18n._() is called at import time. -oslo_i18n.enable_lazy() - - -from keystone.common import config -from keystone.common import environment -from keystone.common import profiler -from keystone.common import utils -from keystone.i18n import _ -from keystone.server import common -from keystone.version import service as keystone_service - - -CONF = cfg.CONF - - -class ServerWrapper(object): - """Wrap a Server with some launching info & capabilities.""" - - def __init__(self, server, workers): - self.server = server - self.workers = workers - - def launch_with(self, launcher): - self.server.listen() - if self.workers > 1: - # Use multi-process launcher - launcher.launch_service(self.server, self.workers) - else: - # Use single process launcher - launcher.launch_service(self.server) - - -def create_server(conf, name, host, port, workers): - app = keystone_service.loadapp('config:%s' % conf, name) - server = environment.Server(app, host=host, port=port, - keepalive=CONF.eventlet_server.tcp_keepalive, - keepidle=CONF.eventlet_server.tcp_keepidle) - profiler.setup(name, host) - if CONF.eventlet_server_ssl.enable: - server.set_ssl(CONF.eventlet_server_ssl.certfile, - CONF.eventlet_server_ssl.keyfile, - CONF.eventlet_server_ssl.ca_certs, - CONF.eventlet_server_ssl.cert_required) - return name, ServerWrapper(server, workers) - - -def serve(*servers): - logging.warning(_('Running keystone via eventlet is deprecated as of Kilo ' - 'in favor of running in a WSGI server (e.g. mod_wsgi). ' - 'Support for keystone under eventlet will be removed in ' - 'the "M"-Release.')) - if max([server[1].workers for server in servers]) > 1: - launcher = service.ProcessLauncher(CONF) - else: - launcher = service.ServiceLauncher(CONF) - - for name, server in servers: - try: - server.launch_with(launcher) - except socket.error: - logging.exception(_('Failed to start the %(name)s server') % { - 'name': name}) - raise - - # notify calling process we are ready to serve - systemd.notify_once() - - for name, server in servers: - launcher.wait() - - -def _get_workers(worker_type_config_opt): - # Get the value from config, if the config value is None (not set), return - # the number of cpus with a minimum of 2. - worker_count = CONF.eventlet_server.get(worker_type_config_opt) - if not worker_count: - worker_count = max(2, processutils.get_worker_count()) - return worker_count - - -def configure_threading(): - monkeypatch_thread = not CONF.standard_threads - pydev_debug_url = utils.setup_remote_pydev_debug() - if pydev_debug_url: - # in order to work around errors caused by monkey patching we have to - # set the thread to False. An explanation is here: - # http://lists.openstack.org/pipermail/openstack-dev/2012-August/ - # 000794.html - monkeypatch_thread = False - environment.use_eventlet(monkeypatch_thread) - - -def run(possible_topdir): - dev_conf = os.path.join(possible_topdir, - 'etc', - 'keystone.conf') - config_files = None - if os.path.exists(dev_conf): - config_files = [dev_conf] - - common.configure( - version=pbr.version.VersionInfo('keystone').version_string(), - config_files=config_files, - pre_setup_logging_fn=configure_threading) - - paste_config = config.find_paste_config() - - def create_servers(): - admin_worker_count = _get_workers('admin_workers') - public_worker_count = _get_workers('public_workers') - - servers = [] - servers.append(create_server(paste_config, - 'admin', - CONF.eventlet_server.admin_bind_host, - CONF.eventlet_server.admin_port, - admin_worker_count)) - servers.append(create_server(paste_config, - 'main', - CONF.eventlet_server.public_bind_host, - CONF.eventlet_server.public_port, - public_worker_count)) - return servers - - _unused, servers = common.setup_backends( - startup_application_fn=create_servers) - serve(*servers) diff --git a/keystone/server/wsgi.py b/keystone/server/wsgi.py index 256e936df7..d611b66e9d 100644 --- a/keystone/server/wsgi.py +++ b/keystone/server/wsgi.py @@ -28,7 +28,6 @@ oslo_i18n.enable_lazy() from keystone.common import config -from keystone.common import environment from keystone.server import common from keystone.version import service as keystone_service @@ -43,8 +42,6 @@ def initialize_application(name, post_log_configured_function=lambda: None): if CONF.debug: CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG) - environment.use_stdlib() - post_log_configured_function() def loadapp(): diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index 8343ec2cf5..206d1c2ede 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -42,12 +42,6 @@ from sqlalchemy import exc import testtools from testtools import testcase -# NOTE(ayoung) -# environment.use_eventlet must run before any of the code that will -# call the eventlet monkeypatching. -from keystone.common import environment # noqa -environment.use_eventlet() - from keystone import auth from keystone.common import config from keystone.common import dependency @@ -648,7 +642,7 @@ class TestCase(BaseTestCase): # log module. This is not in a function or otherwise available to use # without having a CONF object to setup logging. This should help to # reduce the log size by limiting what we log (similar to how Keystone - # would run under mod_wsgi or eventlet). + # would run under mod_wsgi). for pair in CONF.default_log_levels: mod, _sep, level_name = pair.partition('=') logger = logging.getLogger(mod) diff --git a/keystone/tests/unit/ksfixtures/appserver.py b/keystone/tests/unit/ksfixtures/appserver.py deleted file mode 100644 index 4bad277c56..0000000000 --- a/keystone/tests/unit/ksfixtures/appserver.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 __future__ import absolute_import - -import fixtures -from oslo_config import cfg -from paste import deploy - -from keystone.common import environment - - -CONF = cfg.CONF - -MAIN = 'main' -ADMIN = 'admin' - - -class AppServer(fixtures.Fixture): - """A fixture for managing an application server instance.""" - - def __init__(self, config, name, cert=None, key=None, ca=None, - cert_required=False, host='127.0.0.1', port=0): - super(AppServer, self).__init__() - self.config = config - self.name = name - self.cert = cert - self.key = key - self.ca = ca - self.cert_required = cert_required - self.host = host - self.port = port - - def setUp(self): - super(AppServer, self).setUp() - - app = deploy.loadapp(self.config, name=self.name) - self.server = environment.Server(app, self.host, self.port) - self._setup_SSL_if_requested() - self.server.start(key='socket') - - # some tests need to know the port we ran on. - self.port = self.server.socket_info['socket'][1] - self._update_config_opt() - - self.addCleanup(self.server.stop) - - def _setup_SSL_if_requested(self): - # TODO(dstanek): fix environment.Server to take a SSLOpts instance - # so that the params are either always set or not - if (self.cert is not None and - self.ca is not None and - self.key is not None): - self.server.set_ssl(certfile=self.cert, - keyfile=self.key, - ca_certs=self.ca, - cert_required=self.cert_required) - - def _update_config_opt(self): - """Update the config with the actual port used.""" - opt_name = self._get_config_option_for_section_name() - CONF.set_override(opt_name, self.port, group='eventlet_server', - enforce_type=True) - - def _get_config_option_for_section_name(self): - """Map Paster config section names to port option names.""" - return {'admin': 'admin_port', 'main': 'public_port'}[self.name] diff --git a/keystone/tests/unit/test_cert_setup.py b/keystone/tests/unit/test_cert_setup.py index debf87f5bf..0ac7f045f9 100644 --- a/keystone/tests/unit/test_cert_setup.py +++ b/keystone/tests/unit/test_cert_setup.py @@ -15,12 +15,12 @@ import os import shutil +import subprocess import mock from six.moves import http_client from testtools import matchers -from keystone.common import environment from keystone.common import openssl from keystone import exception from keystone.tests import unit @@ -60,14 +60,6 @@ class CertSetupTestCase(rest.RestfulTestCase): ca_certs=ca_certs, ca_key=ca_key, keyfile=os.path.join(KEYDIR, 'signing_key.pem')) - self.config_fixture.config( - group='ssl', - ca_key=ca_key) - self.config_fixture.config( - group='eventlet_server_ssl', - ca_certs=ca_certs, - certfile=os.path.join(CERTDIR, 'keystone.pem'), - keyfile=os.path.join(KEYDIR, 'keystonekey.pem')) self.config_fixture.config(group='token', provider='pkiz') def test_can_handle_missing_certs(self): @@ -93,13 +85,6 @@ class CertSetupTestCase(rest.RestfulTestCase): self.assertTrue(os.path.exists(CONF.signing.ca_certs)) self.assertTrue(os.path.exists(CONF.signing.keyfile)) - def test_create_ssl_certs(self, rebuild=False): - ssl = openssl.ConfigureSSL(None, None, rebuild=rebuild) - ssl.run() - self.assertTrue(os.path.exists(CONF.eventlet_server_ssl.ca_certs)) - self.assertTrue(os.path.exists(CONF.eventlet_server_ssl.certfile)) - self.assertTrue(os.path.exists(CONF.eventlet_server_ssl.keyfile)) - def test_fetch_signing_cert(self, rebuild=False): pki = openssl.ConfigurePKI(None, None, rebuild=rebuild) pki.run() @@ -156,17 +141,6 @@ class CertSetupTestCase(rest.RestfulTestCase): self.assertNotEqual(cert_file1, cert_file2) - def test_ssl_certs_rebuild(self): - self.test_create_ssl_certs() - with open(CONF.eventlet_server_ssl.certfile) as f: - cert_file1 = f.read() - - self.test_create_ssl_certs(rebuild=True) - with open(CONF.eventlet_server_ssl.certfile) as f: - cert_file2 = f.read() - - self.assertNotEqual(cert_file1, cert_file2) - @mock.patch.object(os, 'remove') def test_rebuild_pki_certs_remove_error(self, mock_remove): self.test_create_pki_certs() @@ -180,19 +154,6 @@ class CertSetupTestCase(rest.RestfulTestCase): self.assertEqual(cert_file1, cert_file2) - @mock.patch.object(os, 'remove') - def test_rebuild_ssl_certs_remove_error(self, mock_remove): - self.test_create_ssl_certs() - with open(CONF.eventlet_server_ssl.certfile) as f: - cert_file1 = f.read() - - mock_remove.side_effect = OSError() - self.test_create_ssl_certs(rebuild=True) - with open(CONF.eventlet_server_ssl.certfile) as f: - cert_file2 = f.read() - - self.assertEqual(cert_file1, cert_file2) - def test_create_pki_certs_twice_without_rebuild(self): self.test_create_pki_certs() with open(CONF.signing.certfile) as f: @@ -204,40 +165,29 @@ class CertSetupTestCase(rest.RestfulTestCase): self.assertEqual(cert_file1, cert_file2) - def test_create_ssl_certs_twice_without_rebuild(self): - self.test_create_ssl_certs() - with open(CONF.eventlet_server_ssl.certfile) as f: - cert_file1 = f.read() - - self.test_create_ssl_certs() - with open(CONF.eventlet_server_ssl.certfile) as f: - cert_file2 = f.read() - - self.assertEqual(cert_file1, cert_file2) - class TestExecCommand(unit.TestCase): - @mock.patch.object(environment.subprocess.Popen, 'poll') + @mock.patch.object(subprocess.Popen, 'poll') def test_running_a_successful_command(self, mock_poll): mock_poll.return_value = 0 - ssl = openssl.ConfigureSSL('keystone_user', 'keystone_group') + ssl = openssl.ConfigurePKI('keystone_user', 'keystone_group') ssl.exec_command(['ls']) - @mock.patch.object(environment.subprocess, 'check_output') + @mock.patch.object(subprocess, 'check_output') def test_running_an_invalid_command(self, mock_check_output): cmd = ['ls'] output = 'this is the output string' - error = environment.subprocess.CalledProcessError(returncode=1, - cmd=cmd, - output=output) + error = subprocess.CalledProcessError(returncode=1, + cmd=cmd, + output=output) mock_check_output.side_effect = error - ssl = openssl.ConfigureSSL('keystone_user', 'keystone_group') - e = self.assertRaises(environment.subprocess.CalledProcessError, + ssl = openssl.ConfigurePKI('keystone_user', 'keystone_group') + e = self.assertRaises(subprocess.CalledProcessError, ssl.exec_command, cmd) self.assertThat(e.output, matchers.Equals(output)) diff --git a/keystone/tests/unit/test_ipv6.py b/keystone/tests/unit/test_ipv6.py deleted file mode 100644 index df59429e96..0000000000 --- a/keystone/tests/unit/test_ipv6.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 oslo_config import cfg - -from keystone.common import environment -from keystone.tests import unit -from keystone.tests.unit.ksfixtures import appserver - - -CONF = cfg.CONF - - -class IPv6TestCase(unit.TestCase): - - def setUp(self): - self.skip_if_no_ipv6() - super(IPv6TestCase, self).setUp() - self.load_backends() - - def test_ipv6_ok(self): - """Make sure both public and admin API work with ipv6.""" - paste_conf = self._paste_config('keystone') - - # Verify Admin - with appserver.AppServer(paste_conf, appserver.ADMIN, host="::1"): - conn = environment.httplib.HTTPConnection( - '::1', CONF.eventlet_server.admin_port) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - # Verify Public - with appserver.AppServer(paste_conf, appserver.MAIN, host="::1"): - conn = environment.httplib.HTTPConnection( - '::1', CONF.eventlet_server.public_port) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) diff --git a/keystone/tests/unit/test_ssl.py b/keystone/tests/unit/test_ssl.py deleted file mode 100644 index 6a6d9ffb75..0000000000 --- a/keystone/tests/unit/test_ssl.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 os -import ssl - -from oslo_config import cfg - -from keystone.common import environment -from keystone.tests import unit -from keystone.tests.unit.ksfixtures import appserver - - -CONF = cfg.CONF - -CERTDIR = unit.dirs.root('examples', 'pki', 'certs') -KEYDIR = unit.dirs.root('examples', 'pki', 'private') -CERT = os.path.join(CERTDIR, 'ssl_cert.pem') -KEY = os.path.join(KEYDIR, 'ssl_key.pem') -CA = os.path.join(CERTDIR, 'cacert.pem') -CLIENT = os.path.join(CERTDIR, 'middleware.pem') - - -class SSLTestCase(unit.TestCase): - def setUp(self): - super(SSLTestCase, self).setUp() - raise self.skipTest('SSL Version and Ciphers cannot be configured ' - 'with eventlet, some platforms have disabled ' - 'SSLv3. See bug 1381365.') - # NOTE(morganfainberg): It has been determined that this - # will not be fixed. These tests should be re-enabled for the full - # functional test suite when run against an SSL terminated - # endpoint. Some distributions/environments have patched OpenSSL to - # not have SSLv3 at all due to POODLE and this causes differing - # behavior depending on platform. See bug 1381365 for more information. - - # NOTE(jamespage): - # Deal with more secure certificate chain verification - # introduced in python 2.7.9 under PEP-0476 - # https://github.com/python/peps/blob/master/pep-0476.txt - self.context = None - if hasattr(ssl, '_create_unverified_context'): - self.context = ssl._create_unverified_context() - self.load_backends() - - def get_HTTPSConnection(self, *args): - """Simple helper to configure HTTPSConnection objects.""" - if self.context: - return environment.httplib.HTTPSConnection( - *args, - context=self.context - ) - else: - return environment.httplib.HTTPSConnection(*args) - - def test_1way_ssl_ok(self): - """Make sure both public and admin API work with 1-way SSL.""" - paste_conf = self._paste_config('keystone') - ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA) - - # Verify Admin - with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '127.0.0.1', CONF.eventlet_server.admin_port) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - # Verify Public - with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '127.0.0.1', CONF.eventlet_server.public_port) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - def test_2way_ssl_ok(self): - """Make sure both public and admin API work with 2-way SSL. - - Requires client certificate. - """ - paste_conf = self._paste_config('keystone') - ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, cert_required=True) - - # Verify Admin - with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '127.0.0.1', CONF.eventlet_server.admin_port, CLIENT, CLIENT) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - # Verify Public - with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '127.0.0.1', CONF.eventlet_server.public_port, CLIENT, CLIENT) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - def test_1way_ssl_with_ipv6_ok(self): - """Make sure both public and admin API work with 1-way ipv6 & SSL.""" - self.skip_if_no_ipv6() - - paste_conf = self._paste_config('keystone') - ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, host="::1") - - # Verify Admin - with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '::1', CONF.eventlet_server.admin_port) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - # Verify Public - with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '::1', CONF.eventlet_server.public_port) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - def test_2way_ssl_with_ipv6_ok(self): - """Make sure both public and admin API work with 2-way ipv6 & SSL. - - Requires client certificate. - """ - self.skip_if_no_ipv6() - - paste_conf = self._paste_config('keystone') - ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, - cert_required=True, host="::1") - - # Verify Admin - with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '::1', CONF.eventlet_server.admin_port, CLIENT, CLIENT) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - # Verify Public - with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '::1', CONF.eventlet_server.public_port, CLIENT, CLIENT) - conn.request('GET', '/') - resp = conn.getresponse() - self.assertEqual(300, resp.status) - - def test_2way_ssl_fail(self): - """Expect to fail when client does not present proper certificate.""" - paste_conf = self._paste_config('keystone') - ssl_kwargs = dict(cert=CERT, key=KEY, ca=CA, cert_required=True) - - # Verify Admin - with appserver.AppServer(paste_conf, appserver.ADMIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '127.0.0.1', CONF.eventlet_server.admin_port) - try: - conn.request('GET', '/') - self.fail('Admin API shoulda failed with SSL handshake!') - except ssl.SSLError: - pass - - # Verify Public - with appserver.AppServer(paste_conf, appserver.MAIN, **ssl_kwargs): - conn = self.get_HTTPSConnection( - '127.0.0.1', CONF.eventlet_server.public_port) - try: - conn.request('GET', '/') - self.fail('Public API shoulda failed with SSL handshake!') - except ssl.SSLError: - pass diff --git a/keystone/tests/unit/test_token_provider.py b/keystone/tests/unit/test_token_provider.py index 5c71363bda..3042299530 100644 --- a/keystone/tests/unit/test_token_provider.py +++ b/keystone/tests/unit/test_token_provider.py @@ -798,17 +798,10 @@ class PKIProviderTests(object): from keystoneclient.common import cms self.cms = cms - from keystone.common import environment - self.environment = environment - old_cms_subprocess = cms.subprocess self.addCleanup(setattr, cms, 'subprocess', old_cms_subprocess) - old_env_subprocess = environment.subprocess - self.addCleanup(setattr, environment, 'subprocess', old_env_subprocess) - self.cms.subprocess = self.target_subprocess - self.environment.subprocess = self.target_subprocess # force module reload so the imports get re-evaluated reload_module(pki) @@ -825,16 +818,6 @@ class PKIProviderTests(object): token_data) -class TestPKIProviderWithEventlet(PKIProviderTests, unit.TestCase): - - def setUp(self): - # force keystoneclient.common.cms to use eventlet's subprocess - from eventlet.green import subprocess - self.target_subprocess = subprocess - - super(TestPKIProviderWithEventlet, self).setUp() - - class TestPKIProviderWithStdlib(PKIProviderTests, unit.TestCase): def setUp(self): diff --git a/keystone/tests/unit/test_v3_federation.py b/keystone/tests/unit/test_v3_federation.py index b0d0505867..80e4653e19 100644 --- a/keystone/tests/unit/test_v3_federation.py +++ b/keystone/tests/unit/test_v3_federation.py @@ -13,6 +13,7 @@ import copy import os import random +import subprocess from testtools import matchers import uuid @@ -33,7 +34,6 @@ if not xmldsig: xmldsig = importutils.try_import("xmldsig") from keystone.auth import controllers as auth_controllers -from keystone.common import environment from keystone.contrib.federation import routers from keystone import exception from keystone.federation import controllers as federation_controllers @@ -49,8 +49,6 @@ from keystone.tests.unit import utils from keystone.token.providers import common as token_common -subprocess = environment.subprocess - CONF = cfg.CONF ROOTDIR = os.path.dirname(os.path.abspath(__file__)) XMLDIR = os.path.join(ROOTDIR, 'saml2/') diff --git a/keystone/tests/unit/test_versions.py b/keystone/tests/unit/test_versions.py index 60adafa5ec..0ef5adef0f 100644 --- a/keystone/tests/unit/test_versions.py +++ b/keystone/tests/unit/test_versions.py @@ -672,17 +672,15 @@ class VersionTestCase(unit.TestCase): self.public_app = self.loadapp('keystone', 'main') self.admin_app = self.loadapp('keystone', 'admin') + self.admin_port = random.randint(10000, 30000) + self.public_port = random.randint(40000, 60000) + self.config_fixture.config( - public_endpoint='http://localhost:%(public_port)d', - admin_endpoint='http://localhost:%(admin_port)d') + public_endpoint='http://localhost:%d' % self.public_port, + admin_endpoint='http://localhost:%d' % self.admin_port) def config_overrides(self): super(VersionTestCase, self).config_overrides() - admin_port = random.randint(10000, 30000) - public_port = random.randint(40000, 60000) - self.config_fixture.config(group='eventlet_server', - public_port=public_port, - admin_port=admin_port) def _paste_in_port(self, response, port): for link in response['links']: @@ -698,12 +696,10 @@ class VersionTestCase(unit.TestCase): for version in expected['versions']['values']: if version['id'].startswith('v3'): self._paste_in_port( - version, 'http://localhost:%s/v3/' % - CONF.eventlet_server.public_port) + version, 'http://localhost:%s/v3/' % self.public_port) elif version['id'] == 'v2.0': self._paste_in_port( - version, 'http://localhost:%s/v2.0/' % - CONF.eventlet_server.public_port) + version, 'http://localhost:%s/v2.0/' % self.public_port) self.assertThat(data, _VersionsEqual(expected)) def test_admin_versions(self): @@ -715,12 +711,10 @@ class VersionTestCase(unit.TestCase): for version in expected['versions']['values']: if version['id'].startswith('v3'): self._paste_in_port( - version, 'http://localhost:%s/v3/' % - CONF.eventlet_server.admin_port) + version, 'http://localhost:%s/v3/' % self.admin_port) elif version['id'] == 'v2.0': self._paste_in_port( - version, 'http://localhost:%s/v2.0/' % - CONF.eventlet_server.admin_port) + version, 'http://localhost:%s/v2.0/' % self.admin_port) self.assertThat(data, _VersionsEqual(expected)) def test_use_site_url_if_endpoint_unset(self): @@ -749,8 +743,7 @@ class VersionTestCase(unit.TestCase): data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], - 'http://localhost:%s/v2.0/' % - CONF.eventlet_server.public_port) + 'http://localhost:%s/v2.0/' % self.public_port) self.assertEqual(expected, data) def test_admin_version_v2(self): @@ -760,8 +753,7 @@ class VersionTestCase(unit.TestCase): data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], - 'http://localhost:%s/v2.0/' % - CONF.eventlet_server.admin_port) + 'http://localhost:%s/v2.0/' % self.admin_port) self.assertEqual(expected, data) def test_use_site_url_if_endpoint_unset_v2(self): @@ -782,8 +774,7 @@ class VersionTestCase(unit.TestCase): data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], - 'http://localhost:%s/v3/' % - CONF.eventlet_server.public_port) + 'http://localhost:%s/v3/' % self.public_port) self.assertEqual(expected, data) @utils.wip('waiting on bug #1381961') @@ -794,8 +785,7 @@ class VersionTestCase(unit.TestCase): data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], - 'http://localhost:%s/v3/' % - CONF.eventlet_server.admin_port) + 'http://localhost:%s/v3/' % self.admin_port) self.assertEqual(expected, data) def test_use_site_url_if_endpoint_unset_v3(self): @@ -822,8 +812,7 @@ class VersionTestCase(unit.TestCase): data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], - 'http://localhost:%s/v3/' % - CONF.eventlet_server.public_port) + 'http://localhost:%s/v3/' % self.public_port) self.assertEqual(expected, data) # only v3 information should be displayed by requests to / @@ -835,8 +824,7 @@ class VersionTestCase(unit.TestCase): } } self._paste_in_port(v3_only_response['versions']['values'][0], - 'http://localhost:%s/v3/' % - CONF.eventlet_server.public_port) + 'http://localhost:%s/v3/' % self.public_port) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -855,8 +843,7 @@ class VersionTestCase(unit.TestCase): data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], - 'http://localhost:%s/v2.0/' % - CONF.eventlet_server.public_port) + 'http://localhost:%s/v2.0/' % self.public_port) self.assertEqual(expected, data) # only v2 information should be displayed by requests to / @@ -868,8 +855,7 @@ class VersionTestCase(unit.TestCase): } } self._paste_in_port(v2_only_response['versions']['values'][0], - 'http://localhost:%s/v2.0/' % - CONF.eventlet_server.public_port) + 'http://localhost:%s/v2.0/' % self.public_port) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -971,17 +957,15 @@ class VersionSingleAppTestCase(unit.TestCase): super(VersionSingleAppTestCase, self).setUp() self.load_backends() + self.admin_port = random.randint(10000, 30000) + self.public_port = random.randint(40000, 60000) + self.config_fixture.config( - public_endpoint='http://localhost:%(public_port)d', - admin_endpoint='http://localhost:%(admin_port)d') + public_endpoint='http://localhost:%d' % self.public_port, + admin_endpoint='http://localhost:%d' % self.admin_port) def config_overrides(self): super(VersionSingleAppTestCase, self).config_overrides() - admin_port = random.randint(10000, 30000) - public_port = random.randint(40000, 60000) - self.config_fixture.config(group='eventlet_server', - public_port=public_port, - admin_port=admin_port) def _paste_in_port(self, response, port): for link in response['links']: @@ -991,9 +975,9 @@ class VersionSingleAppTestCase(unit.TestCase): def _test_version(self, app_name): def app_port(): if app_name == 'admin': - return CONF.eventlet_server.admin_port + return self.admin_port else: - return CONF.eventlet_server.public_port + return self.public_port app = self.loadapp('keystone', app_name) client = TestClient(app) resp = client.get('/') diff --git a/keystone/tests/unit/test_wsgi.py b/keystone/tests/unit/test_wsgi.py index bd91113859..c69949f1ab 100644 --- a/keystone/tests/unit/test_wsgi.py +++ b/keystone/tests/unit/test_wsgi.py @@ -15,10 +15,8 @@ # under the License. import gettext -import socket import uuid -import eventlet import mock import oslo_i18n from oslo_serialization import jsonutils @@ -27,7 +25,6 @@ from six.moves import http_client from testtools import matchers import webob -from keystone.common import environment from keystone.common import wsgi from keystone import exception from keystone.tests import unit @@ -485,102 +482,3 @@ class LocalizedResponseTest(unit.TestCase): self.assertThat(resp.json['error']['message'], matchers.Equals(exp_msg)) self.assertThat(xlation_mock.called, matchers.Equals(True)) - - -class ServerTest(unit.TestCase): - - def setUp(self): - super(ServerTest, self).setUp() - self.host = '127.0.0.1' - self.port = '1234' - - @mock.patch('eventlet.listen') - @mock.patch('socket.getaddrinfo') - def test_keepalive_unset(self, mock_getaddrinfo, mock_listen): - mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)] - mock_sock_dup = mock_listen.return_value.dup.return_value - - server = environment.Server(mock.MagicMock(), host=self.host, - port=self.port) - server.start() - self.addCleanup(server.stop) - self.assertTrue(mock_listen.called) - self.assertFalse(mock_sock_dup.setsockopt.called) - - @mock.patch('eventlet.listen') - @mock.patch('socket.getaddrinfo') - def test_keepalive_set(self, mock_getaddrinfo, mock_listen): - mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)] - mock_sock_dup = mock_listen.return_value.dup.return_value - - server = environment.Server(mock.MagicMock(), host=self.host, - port=self.port, keepalive=True) - server.start() - self.addCleanup(server.stop) - mock_sock_dup.setsockopt.assert_called_once_with(socket.SOL_SOCKET, - socket.SO_KEEPALIVE, - 1) - self.assertTrue(mock_listen.called) - - @mock.patch('eventlet.listen') - @mock.patch('socket.getaddrinfo') - def test_keepalive_and_keepidle_set(self, mock_getaddrinfo, mock_listen): - mock_getaddrinfo.return_value = [(1, 2, 3, 4, 5)] - mock_sock_dup = mock_listen.return_value.dup.return_value - - server = environment.Server(mock.MagicMock(), host=self.host, - port=self.port, keepalive=True, - keepidle=1) - server.start() - self.addCleanup(server.stop) - - if hasattr(socket, 'TCP_KEEPIDLE'): - self.assertEqual(2, mock_sock_dup.setsockopt.call_count) - # Test the last set of call args i.e. for the keepidle - mock_sock_dup.setsockopt.assert_called_with(socket.IPPROTO_TCP, - socket.TCP_KEEPIDLE, - 1) - else: - self.assertEqual(1, mock_sock_dup.setsockopt.call_count) - - self.assertTrue(mock_listen.called) - - def test_client_socket_timeout(self): - # mocking server method of eventlet.wsgi to check it is called with - # configured 'client_socket_timeout' value. - for socket_timeout in range(1, 10): - self.config_fixture.config(group='eventlet_server', - client_socket_timeout=socket_timeout) - server = environment.Server(mock.MagicMock(), host=self.host, - port=self.port) - with mock.patch.object(eventlet.wsgi, 'server') as mock_server: - fake_application = uuid.uuid4().hex - fake_socket = uuid.uuid4().hex - server._run(fake_application, fake_socket) - mock_server.assert_called_once_with( - fake_socket, - fake_application, - debug=mock.ANY, - socket_timeout=socket_timeout, - log=mock.ANY, - keepalive=mock.ANY) - - def test_wsgi_keep_alive(self): - # mocking server method of eventlet.wsgi to check it is called with - # configured 'wsgi_keep_alive' value. - wsgi_keepalive = False - self.config_fixture.config(group='eventlet_server', - wsgi_keep_alive=wsgi_keepalive) - - server = environment.Server(mock.MagicMock(), host=self.host, - port=self.port) - with mock.patch.object(eventlet.wsgi, 'server') as mock_server: - fake_application = uuid.uuid4().hex - fake_socket = uuid.uuid4().hex - server._run(fake_application, fake_socket) - mock_server.assert_called_once_with(fake_socket, - fake_application, - debug=mock.ANY, - socket_timeout=mock.ANY, - log=mock.ANY, - keepalive=wsgi_keepalive) diff --git a/keystone/token/providers/pki.py b/keystone/token/providers/pki.py index 6a5a299985..f903124426 100644 --- a/keystone/token/providers/pki.py +++ b/keystone/token/providers/pki.py @@ -14,13 +14,14 @@ """Keystone PKI Token Provider""" +import subprocess # nosec : used to catch subprocess exceptions + from keystoneclient.common import cms from oslo_config import cfg from oslo_log import log from oslo_log import versionutils from oslo_serialization import jsonutils -from keystone.common import environment from keystone.common import utils from keystone import exception from keystone.i18n import _, _LE @@ -48,7 +49,7 @@ class Provider(common.BaseProvider): CONF.signing.certfile, CONF.signing.keyfile)) return token_id - except environment.subprocess.CalledProcessError: + except subprocess.CalledProcessError: LOG.exception(_LE('Unable to sign token')) raise exception.UnexpectedError(_( 'Unable to sign token.')) diff --git a/keystone/token/providers/pkiz.py b/keystone/token/providers/pkiz.py index 3e78d2e40c..0e178d2556 100644 --- a/keystone/token/providers/pkiz.py +++ b/keystone/token/providers/pkiz.py @@ -12,13 +12,14 @@ """Keystone Compressed PKI Token Provider""" +import subprocess # nosec : used to catch subprocess exceptions + from keystoneclient.common import cms from oslo_config import cfg from oslo_log import log from oslo_log import versionutils from oslo_serialization import jsonutils -from keystone.common import environment from keystone.common import utils from keystone import exception from keystone.i18n import _ @@ -47,7 +48,7 @@ class Provider(common.BaseProvider): CONF.signing.certfile, CONF.signing.keyfile)) return token_id - except environment.subprocess.CalledProcessError: + except subprocess.CalledProcessError: LOG.exception(ERROR_MESSAGE) raise exception.UnexpectedError(ERROR_MESSAGE) diff --git a/releasenotes/notes/removed-as-of-newton-721c06b5dcb1b34a.yaml b/releasenotes/notes/removed-as-of-newton-721c06b5dcb1b34a.yaml index 0cee451987..0da80a0f39 100644 --- a/releasenotes/notes/removed-as-of-newton-721c06b5dcb1b34a.yaml +++ b/releasenotes/notes/removed-as-of-newton-721c06b5dcb1b34a.yaml @@ -4,4 +4,12 @@ other: [`blueprint removed-as-of-newton `_] Removed the backend and route from ``keystone.contrib.endpoint_policy``. The package has been moved to ``keystone.endpoint_policy``. This was - deprecated in the Liberty release. \ No newline at end of file + deprecated in the Liberty release. + - > + [`blueprint removed-as-of-newton `_] + Removed ``[eventlet_server]`` and ``[eventlet_server_ssl]`` sections from + the `keystone.conf`. + - > + [`blueprint removed-as-of-newton `_] + Removed support for running keystone under eventlet. It is recommended to + run keystone in an HTTP server. diff --git a/requirements.txt b/requirements.txt index d6c68b35aa..211949717f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,8 +4,6 @@ pbr>=1.6 # Apache-2.0 WebOb>=1.2.3 # MIT -eventlet!=0.18.3,>=0.18.2 # MIT -greenlet>=0.3.2 # MIT PasteDeploy>=1.5.0 # MIT Paste # MIT Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3;python_version=='2.7' # MIT @@ -29,7 +27,6 @@ oslo.log>=1.14.0 # Apache-2.0 oslo.middleware>=3.0.0 # Apache-2.0 oslo.policy>=0.5.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 -oslo.service>=1.0.0 # Apache-2.0 oslo.utils>=3.5.0 # Apache-2.0 oauthlib>=0.6 # BSD pysaml2<4.0.3,>=2.4.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index cd3e712df5..204acf4178 100644 --- a/setup.cfg +++ b/setup.cfg @@ -69,7 +69,6 @@ autodoc_tree_index_modules = True [entry_points] console_scripts = - keystone-all = keystone.cmd.all:main keystone-manage = keystone.cmd.manage:main wsgi_scripts = diff --git a/tox.ini b/tox.ini index cf3cff97d1..5c35fc12b9 100644 --- a/tox.ini +++ b/tox.ini @@ -112,7 +112,7 @@ passenv = KSTEST_PROJECT_ID [flake8] -filename= *.py,keystone-all,keystone-manage +filename= *.py,keystone-manage show-source = true # D100: Missing docstring in public module