summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--doc/source/cors.rst22
-rw-r--r--doc/source/oslo_config.rst4
-rw-r--r--oslo_middleware/catch_errors.py2
-rw-r--r--oslo_middleware/correlation_id.py4
-rw-r--r--oslo_middleware/cors.py5
-rw-r--r--oslo_middleware/debug.py3
-rw-r--r--oslo_middleware/healthcheck/__init__.py91
-rw-r--r--oslo_middleware/healthcheck/disable_by_file.py42
-rw-r--r--oslo_middleware/healthcheck/opts.py47
-rw-r--r--oslo_middleware/healthcheck/pluginbase.py11
-rw-r--r--oslo_middleware/http_proxy_to_wsgi.py8
-rw-r--r--oslo_middleware/opts.py31
-rw-r--r--oslo_middleware/ssl.py5
-rw-r--r--oslo_middleware/tests/test_catch_errors.py3
-rw-r--r--oslo_middleware/tests/test_cors.py6
-rw-r--r--oslo_middleware/tests/test_healthcheck.py5
-rw-r--r--oslo_middleware/tests/test_http_proxy_to_wsgi.py23
-rw-r--r--oslo_middleware/version.py18
-rw-r--r--releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml3
-rw-r--r--releasenotes/source/_static/.placeholder0
-rw-r--r--releasenotes/source/_templates/.placeholder0
-rw-r--r--releasenotes/source/conf.py276
-rw-r--r--releasenotes/source/index.rst8
-rw-r--r--releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po27
-rw-r--r--releasenotes/source/unreleased.rst5
-rw-r--r--requirements.txt12
-rw-r--r--setup.cfg8
-rw-r--r--test-requirements.txt7
-rwxr-xr-xtools/tox_install.sh30
-rw-r--r--tox.ini12
31 files changed, 628 insertions, 93 deletions
diff --git a/.gitignore b/.gitignore
index ed88334..baab79c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,3 +50,6 @@ ChangeLog
# Editors
*~
.*.swp
+
+# reno build
+releasenotes/build
diff --git a/doc/source/cors.rst b/doc/source/cors.rst
index 765a681..77bb6ed 100644
--- a/doc/source/cors.rst
+++ b/doc/source/cors.rst
@@ -55,18 +55,6 @@ something like this::
allow_headers=X-Custom-Header
expose_headers=X-Custom-Header
-If your software requires specific headers or methods for proper operation, you
-may include these as latent properties. These will be evaluated in addition
-to any found in configuration::
-
- from oslo_middleware import cors
-
- app = cors.CORS(your_wsgi_application)
- app.set_latent(allow_headers=['X-System-Header'],
- expose_headers=['X-System-Header'],
- allow_methods=['GET','PATCH'])
-
-
Configuration for pastedeploy
-----------------------------
@@ -74,7 +62,7 @@ If your application is using pastedeploy, the following configuration block
will add CORS support.::
[filter:cors]
- paste.filter_factory = oslo_middleware.cors:filter_factory
+ use = egg:oslo.middleware#cors
allowed_origin=https://website.example.com:443,https://website2.example.com:443
max_age=3600
allow_methods=GET,POST,PUT,DELETE
@@ -86,18 +74,12 @@ existing configuration from oslo_config in order to simplify the points of
configuration, this may be done as follows.::
[filter:cors]
- paste.filter_factory = oslo_middleware.cors:filter_factory
+ use = egg:oslo.middleware#cors
oslo_config_project = oslo_project_name
# Optional field, in case the program name is different from the project:
oslo_config_program = oslo_project_name-api
- # This method also permits setting latent properties, for any origins set
- # in oslo config.
- latent_allow_headers=X-Auth-Token
- latent_expose_headers=X-Auth-Token
- latent_methods=GET,PUT,POST
-
Configuration Options
---------------------
diff --git a/doc/source/oslo_config.rst b/doc/source/oslo_config.rst
index 6e772c6..e6134a5 100644
--- a/doc/source/oslo_config.rst
+++ b/doc/source/oslo_config.rst
@@ -23,7 +23,7 @@ Configuration with paste-deploy and the oslo.config
The paste filter (in /etc/my_app/api-paste.ini) will looks like::
[filter:sizelimit]
- paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
+ use = egg:oslo.middleware#sizelimit
# In case of the application doesn't use the global oslo.config
# object. The middleware must known the app name to load
# the application configuration, by setting this:
@@ -45,7 +45,7 @@ Configuration with pastedeploy only
The paste filter (in /etc/my_app/api-paste.ini) will looks like::
[filter:sizelimit]
- paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
+ use = egg:oslo.middleware#sizelimit
max_request_body_size=1000
This will override any configuration done via oslo.config
diff --git a/oslo_middleware/catch_errors.py b/oslo_middleware/catch_errors.py
index 43d085f..782713b 100644
--- a/oslo_middleware/catch_errors.py
+++ b/oslo_middleware/catch_errors.py
@@ -37,6 +37,8 @@ class CatchErrors(base.ConfigurableMiddleware):
try:
response = req.get_response(self.application)
except Exception:
+ if hasattr(req, 'environ') and 'HTTP_X_AUTH_TOKEN' in req.environ:
+ req.environ['HTTP_X_AUTH_TOKEN'] = '*****'
LOG.exception(_LE('An error occurred during '
'processing the request: %s'), req)
response = webob.exc.HTTPInternalServerError()
diff --git a/oslo_middleware/correlation_id.py b/oslo_middleware/correlation_id.py
index 773dcba..0892ccd 100644
--- a/oslo_middleware/correlation_id.py
+++ b/oslo_middleware/correlation_id.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
+from oslo_utils import uuidutils
from oslo_middleware import base
@@ -23,5 +23,5 @@ class CorrelationId(base.ConfigurableMiddleware):
def process_request(self, req):
correlation_id = (req.headers.get("X_CORRELATION_ID") or
- str(uuid.uuid4()))
+ uuidutils.generate_uuid())
req.headers['X_CORRELATION_ID'] = correlation_id
diff --git a/oslo_middleware/cors.py b/oslo_middleware/cors.py
index 9da5d24..fb4f857 100644
--- a/oslo_middleware/cors.py
+++ b/oslo_middleware/cors.py
@@ -13,6 +13,7 @@
# limitations under the License.
import copy
+from debtcollector import moves
import logging
import debtcollector
@@ -26,7 +27,6 @@ LOG = logging.getLogger(__name__)
CORS_OPTS = [
cfg.ListOpt('allowed_origin',
- default=None,
help='Indicate whether this resource may be shared with the '
'domain received in the requests "origin" header. '
'Format: "<protocol>://<host>[:<port>]", no trailing '
@@ -245,6 +245,9 @@ class CORS(base.ConfigurableMiddleware):
'allow_headers': allow_headers
}
+ @moves.moved_method('set_defaults',
+ message='CORS.set_latent has been deprecated in favor '
+ 'of oslo_middleware.cors.set_defaults')
def set_latent(self, allow_headers=None, allow_methods=None,
expose_headers=None):
'''Add a new latent property for this middleware.
diff --git a/oslo_middleware/debug.py b/oslo_middleware/debug.py
index fb2fc82..08eb0a0 100644
--- a/oslo_middleware/debug.py
+++ b/oslo_middleware/debug.py
@@ -19,7 +19,6 @@ from __future__ import print_function
import sys
-import six
import webob.dec
from oslo_middleware import base
@@ -41,7 +40,7 @@ class Debug(base.ConfigurableMiddleware):
resp = req.get_response(self.application)
print(("*" * 40) + " RESPONSE HEADERS")
- for (key, value) in six.iteritems(resp.headers):
+ for (key, value) in resp.headers.items():
print(key, "=", value)
print()
diff --git a/oslo_middleware/healthcheck/__init__.py b/oslo_middleware/healthcheck/__init__.py
index ce2b03a..c85e1ac 100644
--- a/oslo_middleware/healthcheck/__init__.py
+++ b/oslo_middleware/healthcheck/__init__.py
@@ -21,6 +21,7 @@ import socket
import sys
import traceback
+from debtcollector import removals
import jinja2
from oslo_utils import reflection
from oslo_utils import strutils
@@ -37,6 +38,7 @@ except ImportError:
greenlet = None
from oslo_middleware import base
+from oslo_middleware.healthcheck import opts
def _find_objects(t):
@@ -50,11 +52,10 @@ def _expand_template(contents, params):
class Healthcheck(base.ConfigurableMiddleware):
- """Healthcheck middleware used for monitoring.
+ """Healthcheck application used for monitoring.
- If the path is ``/healthcheck``, it will respond 200 with "OK" as
- the body. Or a 503 with the reason as the body if one of the backends
- reports an application issue.
+ It will respond 200 with "OK" as the body. Or a 503 with the reason as the
+ body if one of the backends reports an application issue.
This is useful for the following reasons:
@@ -238,9 +239,8 @@ class Healthcheck(base.ConfigurableMiddleware):
.. code-block:: ini
- [filter:healthcheck]
- paste.filter_factory = oslo_middleware:Healthcheck.factory
- path = /healthcheck
+ [app:healthcheck]
+ use = egg:oslo.middleware:healthcheck
backends = disable_by_file
disable_by_file_path = /var/run/nova/healthcheck_disable
@@ -252,26 +252,31 @@ class Healthcheck(base.ConfigurableMiddleware):
.. code-block:: ini
- [pipeline:public_api]
- pipeline = healthcheck_public sizelimit [...] public_service
+ [composite:public_api]
+ use = egg:Paste#urlmap
+ / = public_api_pipeline
+ /healthcheck = healthcheck_public
+
+ [composite:admin_api]
+ use = egg:Paste#urlmap
+ / = admin_api_pipeline
+ /healthcheck = healthcheck_admin
- [pipeline:admin_api]
- pipeline = healthcheck_admin sizelimit [...] admin_service
+ [pipeline:public_api_pipeline]
+ pipeline = sizelimit [...] public_service
- [filter:healthcheck_public]
- paste.filter_factory = oslo_middleware:Healthcheck.factory
- path = /healthcheck_public
+ [pipeline:admin_api_pipeline]
+ pipeline = sizelimit [...] admin_service
+
+ [app:healthcheck_public]
+ use = egg:oslo.middleware:healthcheck
backends = disable_by_file
disable_by_file_path = /var/run/nova/healthcheck_public_disable
[filter:healthcheck_admin]
- paste.filter_factory = oslo_middleware:Healthcheck.factory
- path = /healthcheck_admin
+ use = egg:oslo.middleware:healthcheck
backends = disable_by_file
disable_by_file_path = /var/run/nova/healthcheck_admin_disable
-
- More details on available backends and their configuration can be found
- on this page: :doc:`healthcheck_plugins`.
"""
NAMESPACE = "oslo.middleware.healthcheck"
@@ -374,18 +379,16 @@ Reason
</HTML>
"""
- def __init__(self, application, conf):
- super(Healthcheck, self).__init__(application)
- self._path = conf.get('path', '/healthcheck')
- self._show_details = strutils.bool_from_string(conf.get('detailed'))
- self._backend_names = []
- backends = conf.get('backends')
- if backends:
- self._backend_names = backends.split(',')
+ def __init__(self, *args, **kwargs):
+ super(Healthcheck, self).__init__(*args, **kwargs)
+ self.oslo_conf.register_opts(opts.HEALTHCHECK_OPTS,
+ group='healthcheck')
+ self._path = self._conf_get('path')
+ self._show_details = self._conf_get('detailed')
self._backends = stevedore.NamedExtensionManager(
- self.NAMESPACE, self._backend_names,
+ self.NAMESPACE, self._conf_get('backends'),
name_order=True, invoke_on_load=True,
- invoke_args=(conf,))
+ invoke_args=(self.oslo_conf, self.conf))
self._accept_to_functor = collections.OrderedDict([
# Order here matters...
('text/plain', self._make_text_response),
@@ -397,6 +400,34 @@ Reason
# always return text/plain (because sending an error from this
# middleware actually can cause issues).
self._default_accept = 'text/plain'
+ self._ignore_path = False
+
+ def _conf_get(self, key, group='healthcheck'):
+ return super(Healthcheck, self)._conf_get(key, group=group)
+
+ @removals.remove(
+ message="The healthcheck middleware must now be configured as "
+ "an application, not as a filter")
+ @classmethod
+ def factory(cls, global_conf, **local_conf):
+ return super(Healthcheck, cls).factory(global_conf, **local_conf)
+
+ @classmethod
+ def app_factory(cls, global_conf, **local_conf):
+ """Factory method for paste.deploy.
+
+ :param global_conf: dict of options for all middlewares
+ (usually the [DEFAULT] section of the paste deploy
+ configuration file)
+ :param local_conf: options dedicated to this middleware
+ (usually the option defined in the middleware
+ section of the paste deploy configuration file)
+ """
+ conf = global_conf.copy() if global_conf else {}
+ conf.update(local_conf)
+ o = cls(application=None, conf=conf)
+ o._ignore_path = True
+ return o
@staticmethod
def _get_threadstacks():
@@ -511,7 +542,7 @@ Reason
@webob.dec.wsgify
def process_request(self, req):
- if req.path != self._path:
+ if not self._ignore_path and req.path != self._path:
return None
results = [ext.obj.healthcheck(req.server_port)
for ext in self._backends]
diff --git a/oslo_middleware/healthcheck/disable_by_file.py b/oslo_middleware/healthcheck/disable_by_file.py
index 7fbb14b..c1eafa8 100644
--- a/oslo_middleware/healthcheck/disable_by_file.py
+++ b/oslo_middleware/healthcheck/disable_by_file.py
@@ -17,6 +17,7 @@ import logging
import os
from oslo_middleware._i18n import _LW
+from oslo_middleware.healthcheck import opts
from oslo_middleware.healthcheck import pluginbase
LOG = logging.getLogger(__name__)
@@ -39,24 +40,28 @@ class DisableByFilesPortsHealthcheck(pluginbase.HealthcheckBaseExtension):
backends = disable_by_files_ports
disable_by_file_paths = 5000:/var/run/keystone/healthcheck_disable, \
35357:/var/run/keystone/admin_healthcheck_disable
+ # set to True to enable detailed output, False is the default
+ detailed = False
"""
- def __init__(self, conf):
- super(DisableByFilesPortsHealthcheck, self).__init__(conf)
+
+ def __init__(self, *args, **kwargs):
+ super(DisableByFilesPortsHealthcheck, self).__init__(*args, **kwargs)
+ self.oslo_conf.register_opts(opts.DISABLE_BY_FILES_OPTS,
+ group='healthcheck')
self.status_files = {}
- self.status_files.update(
- self._iter_paths_ports(self.conf.get('disable_by_file_paths')))
+ paths = self._conf_get('disable_by_file_paths')
+ self.status_files.update(self._iter_paths_ports(paths))
@staticmethod
def _iter_paths_ports(paths):
- if paths:
- for port_path in paths.split(","):
- port_path = port_path.strip()
- if port_path:
- # On windows, drive letters are followed by colons,
- # which makes split() return 3 elements in this case
- port, path = port_path.split(":", 1)
- port = int(port)
- yield (port, path)
+ for port_path in paths:
+ port_path = port_path.strip()
+ if port_path:
+ # On windows, drive letters are followed by colons,
+ # which makes split() return 3 elements in this case
+ port, path = port_path.split(":", 1)
+ port = int(port)
+ yield (port, path)
def healthcheck(self, server_port):
path = self.status_files.get(server_port)
@@ -90,11 +95,18 @@ class DisableByFileHealthcheck(pluginbase.HealthcheckBaseExtension):
path = /healthcheck
backends = disable_by_file
disable_by_file_path = /var/run/nova/healthcheck_disable
+ # set to True to enable detailed output, False is the default
+ detailed = False
"""
+ def __init__(self, *args, **kwargs):
+ super(DisableByFileHealthcheck, self).__init__(*args, **kwargs)
+ self.oslo_conf.register_opts(opts.DISABLE_BY_FILE_OPTS,
+ group='healthcheck')
+
def healthcheck(self, server_port):
- path = self.conf.get('disable_by_file_path')
- if path is None:
+ path = self._conf_get('disable_by_file_path')
+ if not path:
LOG.warning(_LW('DisableByFile healthcheck middleware enabled '
'without disable_by_file_path set'))
return pluginbase.HealthcheckResult(
diff --git a/oslo_middleware/healthcheck/opts.py b/oslo_middleware/healthcheck/opts.py
new file mode 100644
index 0000000..ff39e98
--- /dev/null
+++ b/oslo_middleware/healthcheck/opts.py
@@ -0,0 +1,47 @@
+# 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
+
+
+HEALTHCHECK_OPTS = [
+ cfg.StrOpt('path',
+ default='/healthcheck',
+ deprecated_for_removal=True,
+ help='The path to respond to healtcheck requests on.'),
+ cfg.BoolOpt('detailed',
+ default=False,
+ help='Show more detailed information as part of the response'),
+ cfg.ListOpt('backends',
+ default=[],
+ help='Additional backends that can perform health checks and '
+ 'report that information back as part of a request.'),
+]
+
+
+DISABLE_BY_FILE_OPTS = [
+ cfg.StrOpt('disable_by_file_path',
+ default=None,
+ help='Check the presence of a file to determine if an '
+ 'application is running on a port. Used by '
+ 'DisableByFileHealthcheck plugin.'),
+]
+
+
+DISABLE_BY_FILES_OPTS = [
+ cfg.ListOpt('disable_by_file_paths',
+ default=[],
+ help='Check the presence of a file based on a port to '
+ 'determine if an application is running on a port. '
+ 'Expects a "port:path" list of strings. Used by '
+ 'DisableByFilesPortsHealthcheck plugin.'),
+]
diff --git a/oslo_middleware/healthcheck/pluginbase.py b/oslo_middleware/healthcheck/pluginbase.py
index e370967..eb8013d 100644
--- a/oslo_middleware/healthcheck/pluginbase.py
+++ b/oslo_middleware/healthcheck/pluginbase.py
@@ -29,7 +29,9 @@ class HealthcheckResult(object):
@six.add_metaclass(abc.ABCMeta)
class HealthcheckBaseExtension(object):
- def __init__(self, conf):
+
+ def __init__(self, oslo_conf, conf):
+ self.oslo_conf = oslo_conf
self.conf = conf
@abc.abstractmethod
@@ -38,3 +40,10 @@ class HealthcheckBaseExtension(object):
return: HealthcheckResult object
"""
+
+ def _conf_get(self, key, group='healthcheck'):
+ if key in self.conf:
+ # Validate value type
+ self.oslo_conf.set_override(key, self.conf[key], group=group,
+ enforce_type=True)
+ return getattr(getattr(self.oslo_conf, group), key)
diff --git a/oslo_middleware/http_proxy_to_wsgi.py b/oslo_middleware/http_proxy_to_wsgi.py
index 84bc32b..4d68bcf 100644
--- a/oslo_middleware/http_proxy_to_wsgi.py
+++ b/oslo_middleware/http_proxy_to_wsgi.py
@@ -71,6 +71,10 @@ class HTTPProxyToWSGI(base.ConfigurableMiddleware):
if forwarded_host:
req.environ['HTTP_HOST'] = forwarded_host
+ forwarded_for = proxy.get("for")
+ if forwarded_for:
+ req.environ['REMOTE_ADDR'] = forwarded_for
+
else:
# World before RFC7239
forwarded_proto = req.environ.get("HTTP_X_FORWARDED_PROTO")
@@ -81,6 +85,10 @@ class HTTPProxyToWSGI(base.ConfigurableMiddleware):
if forwarded_host:
req.environ['HTTP_HOST'] = forwarded_host
+ forwarded_for = req.environ.get("HTTP_X_FORWARDED_FOR")
+ if forwarded_for:
+ req.environ['REMOTE_ADDR'] = forwarded_for
+
v = req.environ.get("HTTP_X_FORWARDED_PREFIX")
if v:
req.environ['SCRIPT_NAME'] = v + req.environ['SCRIPT_NAME']
diff --git a/oslo_middleware/opts.py b/oslo_middleware/opts.py
index e66e723..1584f9b 100644
--- a/oslo_middleware/opts.py
+++ b/oslo_middleware/opts.py
@@ -19,6 +19,7 @@ __all__ = [
'list_opts_ssl',
'list_opts_cors',
'list_opts_http_proxy_to_wsgi',
+ 'list_opts_healthcheck',
]
@@ -26,6 +27,7 @@ import copy
import itertools
from oslo_middleware import cors
+from oslo_middleware.healthcheck import opts as healthcheck_opts
from oslo_middleware import http_proxy_to_wsgi
from oslo_middleware import sizelimit
from oslo_middleware import ssl
@@ -56,6 +58,7 @@ def list_opts():
list_opts_ssl(),
list_opts_cors(),
list_opts_http_proxy_to_wsgi(),
+ list_opts_healthcheck(),
)
)
@@ -155,3 +158,31 @@ def list_opts_http_proxy_to_wsgi():
return [
('oslo_middleware', copy.deepcopy(http_proxy_to_wsgi.OPTS)),
]
+
+
+def list_opts_healthcheck():
+ """Return a list of oslo.config options for healthcheck.
+
+ The returned list includes all oslo.config options which may be registered
+ at runtime by the library.
+
+ Each element of the list is a tuple. The first element is the name of the
+ group under which the list of elements in the second element will be
+ registered. A group name of None corresponds to the [DEFAULT] group in
+ config files.
+
+ This function is also discoverable via the 'oslo.middleware' entry point
+ under the 'oslo.config.opts' namespace.
+
+ The purpose of this is to allow tools like the Oslo sample config file
+ generator to discover the options exposed to users by this library.
+
+ :returns: a list of (group_name, opts) tuples
+ """
+ # standard opts and the most common plugin to turn up in sample config.
+ # can figure out a better way of exposing plugin opts later if required.
+ return [
+ ('healthcheck', copy.deepcopy(healthcheck_opts.HEALTHCHECK_OPTS +
+ healthcheck_opts.DISABLE_BY_FILE_OPTS +
+ healthcheck_opts.DISABLE_BY_FILES_OPTS))
+ ]
diff --git a/oslo_middleware/ssl.py b/oslo_middleware/ssl.py
index f90e954..aa96b11 100644
--- a/oslo_middleware/ssl.py
+++ b/oslo_middleware/ssl.py
@@ -24,10 +24,6 @@ OPTS = [
]
-removals.removed_module(__name__,
- "oslo_middleware.http_proxy_to_wsgi")
-
-
class SSLMiddleware(base.ConfigurableMiddleware):
"""SSL termination proxies middleware.
@@ -37,6 +33,7 @@ class SSLMiddleware(base.ConfigurableMiddleware):
"""
def __init__(self, application, *args, **kwargs):
+ removals.removed_module(__name__, "oslo_middleware.http_proxy_to_wsgi")
super(SSLMiddleware, self).__init__(application, *args, **kwargs)
self.oslo_conf.register_opts(OPTS, group='oslo_middleware')
diff --git a/oslo_middleware/tests/test_catch_errors.py b/oslo_middleware/tests/test_catch_errors.py
index 920bbe2..66351e5 100644
--- a/oslo_middleware/tests/test_catch_errors.py
+++ b/oslo_middleware/tests/test_catch_errors.py
@@ -26,6 +26,7 @@ class CatchErrorsTest(test_base.BaseTestCase):
def _test_has_request_id(self, application, expected_code=None):
app = catch_errors.CatchErrors(application)
req = webob.Request.blank('/test')
+ req.environ['HTTP_X_AUTH_TOKEN'] = 'hello=world'
res = req.get_response(app)
self.assertEqual(expected_code, res.status_int)
@@ -45,3 +46,5 @@ class CatchErrorsTest(test_base.BaseTestCase):
self._test_has_request_id(application,
webob.exc.HTTPInternalServerError.code)
self.assertEqual(1, log_exc.call_count)
+ req_log = log_exc.call_args[0][1]
+ self.assertIn('X-Auth-Token: *****', str(req_log))
diff --git a/oslo_middleware/tests/test_cors.py b/oslo_middleware/tests/test_cors.py
index e8276f3..8efa2c8 100644
--- a/oslo_middleware/tests/test_cors.py
+++ b/oslo_middleware/tests/test_cors.py
@@ -361,7 +361,7 @@ class CORSRegularRequestTest(CORSTestBase):
self.assertEqual(['http://valid.example.com'], gc.allowed_origin)
self.assertEqual(False, gc.allow_credentials)
self.assertEqual([], gc.expose_headers)
- self.assertEqual(None, gc.max_age)
+ self.assertIsNone(gc.max_age)
self.assertEqual(['GET'], gc.allow_methods)
self.assertEqual([], gc.allow_headers)
@@ -678,7 +678,7 @@ class CORSPreflightRequestTest(CORSTestBase):
self.assertEqual(gc.allowed_origin, ['http://valid.example.com'])
self.assertEqual(gc.allow_credentials, False)
self.assertEqual(gc.expose_headers, [])
- self.assertEqual(gc.max_age, None)
+ self.assertIsNone(gc.max_age)
self.assertEqual(gc.allow_methods, ['GET'])
self.assertEqual(gc.allow_headers, [])
@@ -1195,7 +1195,7 @@ class CORSTestWildcard(CORSTestBase):
self.assertEqual(['http://default.example.com'], gc.allowed_origin)
self.assertEqual(True, gc.allow_credentials)
self.assertEqual([], gc.expose_headers)
- self.assertEqual(None, gc.max_age)
+ self.assertIsNone(gc.max_age)
self.assertEqual(['GET', 'PUT', 'POST', 'DELETE', 'HEAD'],
gc.allow_methods)
self.assertEqual([], gc.allow_headers)
diff --git a/oslo_middleware/tests/test_healthcheck.py b/oslo_middleware/tests/test_healthcheck.py
index df73969..793a895 100644
--- a/oslo_middleware/tests/test_healthcheck.py
+++ b/oslo_middleware/tests/test_healthcheck.py
@@ -17,6 +17,7 @@ import threading
import time
import mock
+from oslo_config import fixture as config
from oslotest import base as test_base
import requests
import webob.dec
@@ -50,6 +51,10 @@ class HealthcheckMainTests(test_base.BaseTestCase):
class HealthcheckTests(test_base.BaseTestCase):
+ def setUp(self):
+ super(HealthcheckTests, self).setUp()
+ self.useFixture(config.Config())
+
@staticmethod
@webob.dec.wsgify
def application(req):
diff --git a/oslo_middleware/tests/test_http_proxy_to_wsgi.py b/oslo_middleware/tests/test_http_proxy_to_wsgi.py
index 26baa77..1554ece 100644
--- a/oslo_middleware/tests/test_http_proxy_to_wsgi.py
+++ b/oslo_middleware/tests/test_http_proxy_to_wsgi.py
@@ -103,6 +103,29 @@ class TestHTTPProxyToWSGI(test_base.BaseTestCase):
response = self.request.get_response(self.middleware)
self.assertEqual(b"https://example.com:8043/bla", response.body)
+ def test_forwarded_for_headers(self):
+ @webob.dec.wsgify()
+ def fake_app(req):
+ return req.environ['REMOTE_ADDR']
+
+ self.middleware = http_proxy_to_wsgi.HTTPProxyToWSGIMiddleware(
+ fake_app)
+ forwarded_for_addr = '1.2.3.4'
+ forwarded_addr = '8.8.8.8'
+
+ # If both X-Forwarded-For and Fowarded headers are present, it should
+ # use the Forwarded header and ignore the X-Forwarded-For header.
+ self.request.headers['Forwarded'] = (
+ "for=%s;proto=https;host=example.com:8043" % (forwarded_addr))
+ self.request.headers['X-Forwarded-For'] = forwarded_for_addr
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(forwarded_addr.encode(), response.body)
+
+ # Now if only X-Forwarded-For header is present, it should be used.
+ del self.request.headers['Forwarded']
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(forwarded_for_addr.encode(), response.body)
+
class TestHTTPProxyToWSGIDisabled(test_base.BaseTestCase):
diff --git a/oslo_middleware/version.py b/oslo_middleware/version.py
new file mode 100644
index 0000000..f73224f
--- /dev/null
+++ b/oslo_middleware/version.py
@@ -0,0 +1,18 @@
+# Copyright 2016 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 pbr.version
+
+version_info = pbr.version.VersionInfo('oslo_middleware')
diff --git a/releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml b/releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml
new file mode 100644
index 0000000..46a2da6
--- /dev/null
+++ b/releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml
@@ -0,0 +1,3 @@
+---
+other:
+ - Switch to reno for managing release notes. \ No newline at end of file
diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/releasenotes/source/_static/.placeholder
diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/releasenotes/source/_templates/.placeholder
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
new file mode 100644
index 0000000..a53fc2c
--- /dev/null
+++ b/releasenotes/source/conf.py
@@ -0,0 +1,276 @@
+# -*- coding: utf-8 -*-
+# 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.
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'oslosphinx',
+ 'reno.sphinxext',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'oslo.middleware Release Notes'
+copyright = u'2016, oslo.middleware Developers'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+from oslo_middleware.version import version_info as oslo_middleware_version
+
+# The full version, including alpha/beta/rc tags.
+release = oslo_middleware_version.version_string_with_vcs()
+# The short X.Y version.
+version = oslo_middleware_version.canonical_version_string()
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+# language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+# html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+# html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+# html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+# html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+# html_domain_indices = True
+
+# If false, no index is generated.
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'oslo.middlewareReleaseNotesDoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ # 'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'oslo.middlewareReleaseNotes.tex',
+ u'oslo.middleware Release Notes Documentation',
+ u'oslo.middleware Developers', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+# latex_appendices = []
+
+# If false, no module index is generated.
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'oslo.middlewareReleaseNotes',
+ u'oslo.middleware Release Notes Documentation',
+ [u'oslo.middleware Developers'], 1)
+]
+
+# If true, show URL addresses after external links.
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'oslo.middlewareReleaseNotes',
+ u'oslo.middleware Release Notes Documentation',
+ u'oslo.middleware Developers', 'oslo.middlewareReleaseNotes',
+ 'The library includes components that can be injected into wsgi pipelines'
+ ' to intercept request/response flows.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+# texinfo_no_detailmenu = False
+
+# -- Options for Internationalization output ------------------------------
+locale_dirs = ['locale/']
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
new file mode 100644
index 0000000..d19561d
--- /dev/null
+++ b/releasenotes/source/index.rst
@@ -0,0 +1,8 @@
+=============================
+oslo.middleware Release Notes
+=============================
+
+ .. toctree::
+ :maxdepth: 1
+
+ unreleased
diff --git a/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po
new file mode 100644
index 0000000..7e82aef
--- /dev/null
+++ b/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po
@@ -0,0 +1,27 @@
+# Gérald LONLAS <g.lonlas@gmail.com>, 2016. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: oslo.middleware Release Notes 3.20.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-02 02:56+0000\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2016-10-22 06:02+0000\n"
+"Last-Translator: Gérald LONLAS <g.lonlas@gmail.com>\n"
+"Language-Team: French\n"
+"Language: fr\n"
+"X-Generator: Zanata 3.7.3\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+msgid "Other Notes"
+msgstr "Autres notes"
+
+msgid "Switch to reno for managing release notes."
+msgstr "Commence à utiliser reno pour la gestion des notes de release"
+
+msgid "Unreleased Release Notes"
+msgstr "Note de release pour les changements non déployées"
+
+msgid "oslo.middleware Release Notes"
+msgstr "Note de release pour oslo.middleware"
diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst
new file mode 100644
index 0000000..5860a46
--- /dev/null
+++ b/releasenotes/source/unreleased.rst
@@ -0,0 +1,5 @@
+==========================
+ Unreleased Release Notes
+==========================
+
+.. release-notes::
diff --git a/requirements.txt b/requirements.txt
index 384efd3..11fc3e5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,14 +2,14 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-pbr>=1.6 # Apache-2.0
-Jinja2>=2.8 # BSD License (3 clause)
-oslo.config>=3.14.0 # Apache-2.0
+pbr>=1.8 # Apache-2.0
+Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 # BSD License (3 clause)
+oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
oslo.context>=2.9.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
-oslo.utils>=3.16.0 # Apache-2.0
+oslo.utils>=3.18.0 # Apache-2.0
six>=1.9.0 # MIT
-stevedore>=1.16.0 # Apache-2.0
-WebOb>=1.2.3 # MIT
+stevedore>=1.17.1 # Apache-2.0
+WebOb>=1.6.0 # MIT
debtcollector>=1.2.0 # Apache-2.0
statsd>=3.2.1 # MIT
diff --git a/setup.cfg b/setup.cfg
index d2a7686..e97d776 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@ description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
-home-page = http://wiki.openstack.org/wiki/Oslo#oslo.middleware
+home-page = http://docs.openstack.org/developer/oslo.middleware
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -16,7 +16,7 @@ classifier =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.4
+ Programming Language :: Python :: 3.5
[files]
packages =
@@ -29,11 +29,15 @@ oslo.config.opts =
oslo.middleware.sizelimit = oslo_middleware.opts:list_opts_sizelimit
oslo.middleware.ssl = oslo_middleware.opts:list_opts_ssl
oslo.middleware.http_proxy_to_wsgi = oslo_middleware.opts:list_opts_http_proxy_to_wsgi
+ oslo.middleware.healthcheck = oslo_middleware.opts:list_opts_healthcheck
oslo.middleware.healthcheck =
disable_by_file = oslo_middleware.healthcheck.disable_by_file:DisableByFileHealthcheck
disable_by_files_ports = oslo_middleware.healthcheck.disable_by_file:DisableByFilesPortsHealthcheck
+paste.app_factory =
+ healthcheck = oslo_middleware:Healthcheck.app_factory
+
paste.filter_factory =
catch_errors = oslo_middleware:CatchErrors.factory
correlation_id = oslo_middleware:CorrelationId.factory
diff --git a/test-requirements.txt b/test-requirements.txt
index 5f53419..26b5790 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,8 +5,9 @@
fixtures>=3.0.0 # Apache-2.0/BSD
hacking<0.11,>=0.10.0
mock>=2.0 # BSD
-oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
+oslosphinx>=4.7.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
-sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
+sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
testtools>=1.4.0 # MIT
-coverage>=3.6 # Apache-2.0
+coverage>=4.0 # Apache-2.0
+reno>=1.8.0 # Apache-2.0
diff --git a/tools/tox_install.sh b/tools/tox_install.sh
new file mode 100755
index 0000000..e61b63a
--- /dev/null
+++ b/tools/tox_install.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+# Client constraint file contains this client version pin that is in conflict
+# with installing the client from source. We should remove the version pin in
+# the constraints file before applying it for from-source installation.
+
+CONSTRAINTS_FILE="$1"
+shift 1
+
+set -e
+
+# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
+# published to logs.openstack.org for easy debugging.
+localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
+
+if [[ "$CONSTRAINTS_FILE" != http* ]]; then
+ CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
+fi
+# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
+curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
+
+pip install -c"$localfile" openstack-requirements
+
+# This is the main purpose of the script: Allow local installation of
+# the current repo. It is listed in constraints file and thus any
+# install will be constrained and we need to unconstrain it.
+edit-constraints "$localfile" -- "$CLIENT_NAME"
+
+pip install -c"$localfile" -U "$@"
+exit $?
diff --git a/tox.ini b/tox.ini
index 9ff7d03..d5dcaa7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,13 @@
[tox]
-minversion = 1.6
-envlist = py34,py27,pypy,pep8
+minversion = 2.0
+envlist = py35,py27,pypy,pep8
[testenv]
+setenv =
+ VIRTUAL_ENV={envdir}
+ BRANCH_NAME=master
+ CLIENT_NAME=oslo.middleware
+install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
deps = -r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
@@ -35,3 +40,6 @@ import_exceptions = oslo_middleware._i18n
# of the requirements.txt files
deps = pip_missing_reqs
commands = pip-missing-reqs -d --ignore-module=oslo_middleware* --ignore-module=pkg_resources --ignore-file=oslo_middleware/tests/* oslo_middleware
+
+[testenv:releasenotes]
+commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html