Allow to get option from paste-deploy

This change allows any middleware that use oslo.config to
be configured via paste-deploy, like the cors does.

Related-bug: #1482086
Change-Id: Ibb3e951b45b51c9bc602c9113df18a58226d92d1
This commit is contained in:
Mehdi Abaakouk 2015-08-06 10:53:48 +02:00
parent e744501c47
commit 41ac7aeec2
8 changed files with 101 additions and 37 deletions

View File

@ -2,5 +2,8 @@
Healthcheck middleware plugins
================================
.. automodule:: oslo_middleware.healthcheck
:members:
.. automodule:: oslo_middleware.healthcheck.disable_by_file
:members:

View File

@ -10,4 +10,5 @@ Contents
api
healthcheck_plugins
cors
oslo_config
contributing

View File

@ -0,0 +1,53 @@
=============================
Middlewares and configuration
=============================
Middlewares can be configured in multiple fashion depending of the
application needs. Here is some use-cases:
Configuration from the application
----------------------------------
The application code will looks like::
from oslo_middleware import sizelimit
from oslo_config import cfg
conf = cfg.ConfigOpts()
app = sizelimit.RequestBodySizeLimiter(your_wsgi_application, conf)
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
# 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:
# oslo_config_project = my_app
The oslo.config file of the application (eg: /etc/my_app/my_app.conf) will looks like::
[oslo_middleware]
max_request_body_size=1000
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
max_request_body_size=1000
This will override any configuration done via oslo.config
.. note::
healtcheck middleware does not yet use oslo.config, see :doc:`healthcheck_plugins`

View File

@ -63,6 +63,13 @@ class Middleware(object):
# Fallback to global object
self.oslo_conf = cfg.CONF
def _conf_get(self, key, group="oslo_middleware"):
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)
def process_request(self, req):
"""Called on each request.

View File

@ -76,19 +76,11 @@ class CORS(base.Middleware):
super(CORS, self).__init__(application, conf)
# Begin constructing our configuration hash.
self.allowed_origins = {}
self._init_from_oslo(self.oslo_conf)
self._init_from_conf()
self._init_conf()
@classmethod
def factory(cls, global_conf, allowed_origin, **local_conf):
# Ensures allowed_origin config exists
return super(CORS, cls).factory(global_conf,
allowed_origin=allowed_origin,
**local_conf)
def _init_from_conf(self):
"""Load configuration from paste.deploy
"""factory method for paste.deploy
allowed_origin: Protocol, host, and port for the allowed origin.
allow_credentials: Whether to permit credentials.
@ -98,20 +90,23 @@ class CORS(base.Middleware):
allow_headers: List of HTTP headers to permit from the client.
"""
if 'allowed_origin' in self.conf:
self.add_origin(
allowed_origin=self.conf['allowed_origin'],
allow_credentials=self.conf.get('allow_credentials', True),
expose_headers=self.conf.get('expose_headers'),
max_age=self.conf.get('max_age'),
allow_methods=self.conf.get('allow_methods'),
allow_headers=self.conf.get('allow_headers'))
# Ensures allowed_origin config exists
return super(CORS, cls).factory(global_conf,
allowed_origin=allowed_origin,
**local_conf)
def _init_from_oslo(self, conf):
def _init_conf(self):
'''Initialize this middleware from an oslo.config instance.'''
# First, check the configuration and register global options.
conf.register_opts(CORS_OPTS, 'cors')
self.oslo_conf.register_opts(CORS_OPTS, 'cors')
allowed_origin = self._conf_get('allowed_origin', 'cors')
allow_credentials = self._conf_get('allow_credentials', 'cors')
expose_headers = self._conf_get('expose_headers', 'cors')
max_age = self._conf_get('max_age', 'cors')
allow_methods = self._conf_get('allow_methods', 'cors')
allow_headers = self._conf_get('allow_headers', 'cors')
# Clone our original CORS_OPTS, and set the defaults to whatever is
# set in the global conf instance. This is done explicitly (instead
@ -119,24 +114,29 @@ class CORS(base.Middleware):
# allowed_origin.
subgroup_opts = copy.deepcopy(CORS_OPTS)
cfg.set_defaults(subgroup_opts,
allow_credentials=conf.cors.allow_credentials,
expose_headers=conf.cors.expose_headers,
max_age=conf.cors.max_age,
allow_methods=conf.cors.allow_methods,
allow_headers=conf.cors.allow_headers)
allow_credentials=allow_credentials,
expose_headers=expose_headers,
max_age=max_age,
allow_methods=allow_methods,
allow_headers=allow_headers)
# If the default configuration contains an allowed_origin, don't
# forget to register that.
if conf.cors.allowed_origin:
self.add_origin(**conf.cors)
if allowed_origin:
self.add_origin(allowed_origin=allowed_origin,
allow_credentials=allow_credentials,
expose_headers=expose_headers,
max_age=max_age,
allow_methods=allow_methods,
allow_headers=allow_headers)
# Iterate through all the loaded config sections, looking for ones
# prefixed with 'cors.'
for section in conf.list_all_sections():
for section in self.oslo_conf.list_all_sections():
if section.startswith('cors.'):
# Register with the preconstructed defaults
conf.register_opts(subgroup_opts, section)
self.add_origin(**conf[section])
self.oslo_conf.register_opts(subgroup_opts, section)
self.add_origin(**self.oslo_conf[section])
def add_origin(self, allowed_origin, allow_credentials=True,
expose_headers=None, max_age=None, allow_methods=None,

View File

@ -84,7 +84,7 @@ class RequestBodySizeLimiter(base.Middleware):
@webob.dec.wsgify
def __call__(self, req):
max_size = self.oslo_conf.oslo_middleware.max_request_body_size
max_size = self._conf_get('max_request_body_size')
if (req.content_length is not None and
req.content_length > max_size):
msg = _("Request is too large.")

View File

@ -37,7 +37,7 @@ class SSLMiddleware(base.Middleware):
def process_request(self, req):
self.header_name = 'HTTP_{0}'.format(
self.oslo_conf.oslo_middleware.secure_proxy_ssl_header.upper()
self._conf_get('secure_proxy_ssl_header').upper()
.replace('-', '_'))
req.environ['wsgi.url_scheme'] = req.environ.get(
self.header_name, req.environ['wsgi.url_scheme'])

View File

@ -131,11 +131,11 @@ class CORSTestFilterFactory(test_base.BaseTestCase):
self.assertIn('http://valid.example.com', application.allowed_origins)
config = application.allowed_origins['http://valid.example.com']
self.assertEqual('False', config['allow_credentials'])
self.assertEqual('', config['max_age'])
self.assertEqual('', config['expose_headers'])
self.assertEqual('GET', config['allow_methods'])
self.assertEqual('', config['allow_headers'])
self.assertEqual(False, config['allow_credentials'])
self.assertEqual(None, config['max_age'])
self.assertEqual([], config['expose_headers'])
self.assertEqual(['GET'], config['allow_methods'])
self.assertEqual([], config['allow_headers'])
def test_no_origin_fail(self):
'''Assert that a filter factory with no allowed_origin fails.'''