Refactor proxy-server conf loading to a helper function

There were two middlewares using a common pattern to load
the proxy-server app config section. The existing pattern
fails to recognise option overrides that are declared using
paste-deploy's 'set' notation, as illustrated by the change
to test_dlo.py in this patch.

This patch replaces the existing code with a helper function
that loads the proxy-server config using the paste-deploy loader.
The resulting config dict is therefore exactly the same as that
used to initialise the proxy-server app.

Change-Id: Ib58ce03e2010f41e7eb11f1a6dc78b0b7f55d466
This commit is contained in:
Alistair Coles 2017-12-05 10:36:38 -08:00
parent b808c806b4
commit dd113ab25a
6 changed files with 132 additions and 46 deletions

View File

@ -114,20 +114,18 @@ greater than 5GB.
"""
import os
from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError
from six.moves.urllib.parse import quote, unquote
from swift.common import utils
from swift.common.utils import get_logger, \
config_true_value, FileLikeIter, read_conf_dir, close_if_possible
config_true_value, FileLikeIter, close_if_possible
from swift.common.swob import Request, HTTPPreconditionFailed, \
HTTPRequestEntityTooLarge, HTTPBadRequest, HTTPException
from swift.common.http import HTTP_MULTIPLE_CHOICES, is_success, HTTP_OK
from swift.common.constraints import check_account_format, MAX_FILE_SIZE
from swift.common.request_helpers import copy_header_subset, remove_items, \
is_sys_meta, is_sys_or_user_meta, is_object_transient_sysmeta
from swift.common.wsgi import WSGIContext, make_subrequest
from swift.common.wsgi import WSGIContext, make_subrequest, load_app_config
def _check_path_header(req, name, length, error_msg):
@ -263,25 +261,9 @@ class ServerSideCopyMiddleware(object):
# This takes preference over the one set in proxy app
return
cp = ConfigParser()
if os.path.isdir(conf['__file__']):
read_conf_dir(cp, conf['__file__'])
else:
cp.read(conf['__file__'])
try:
pipe = cp.get("pipeline:main", "pipeline")
except (NoSectionError, NoOptionError):
return
proxy_name = pipe.rsplit(None, 1)[-1]
proxy_section = "app:" + proxy_name
try:
conf['object_post_as_copy'] = cp.get(proxy_section,
'object_post_as_copy')
except (NoSectionError, NoOptionError):
pass
proxy_conf = load_app_config(conf['__file__'])
if 'object_post_as_copy' in proxy_conf:
conf['object_post_as_copy'] = proxy_conf['object_post_as_copy']
def __call__(self, env, start_response):
req = Request(env)

View File

@ -119,10 +119,8 @@ Here's an example using ``curl`` with tiny 1-byte segments::
"""
import json
import os
import six
from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError
from six.moves.urllib.parse import unquote
from hashlib import md5
@ -132,10 +130,9 @@ from swift.common.http import is_success
from swift.common.swob import Request, Response, \
HTTPRequestedRangeNotSatisfiable, HTTPBadRequest, HTTPConflict
from swift.common.utils import get_logger, \
RateLimitedIterator, read_conf_dir, quote, close_if_possible, \
closing_if_possible
RateLimitedIterator, quote, close_if_possible, closing_if_possible
from swift.common.request_helpers import SegmentedIterable
from swift.common.wsgi import WSGIContext, make_subrequest
from swift.common.wsgi import WSGIContext, make_subrequest, load_app_config
class GetContext(WSGIContext):
@ -381,26 +378,12 @@ class DynamicLargeObject(object):
'__file__' not in conf):
return
cp = ConfigParser()
if os.path.isdir(conf['__file__']):
read_conf_dir(cp, conf['__file__'])
else:
cp.read(conf['__file__'])
try:
pipe = cp.get("pipeline:main", "pipeline")
except (NoSectionError, NoOptionError):
return
proxy_name = pipe.rsplit(None, 1)[-1]
proxy_section = "app:" + proxy_name
proxy_conf = load_app_config(conf['__file__'])
for setting in ('rate_limit_after_segment',
'rate_limit_segments_per_sec',
'max_get_time'):
try:
conf[setting] = cp.get(proxy_section, setting)
except (NoSectionError, NoOptionError):
pass
if setting in proxy_conf:
conf[setting] = proxy_conf[setting]
def __call__(self, env, start_response):
"""

View File

@ -397,6 +397,24 @@ def loadapp(conf_file, global_conf=None, allow_modify_pipeline=True):
return ctx.create()
def load_app_config(conf_file):
"""
Read the app config section from a config file.
:param conf_file: path to a config file
:return: a dict
"""
app_conf = {}
try:
ctx = loadcontext(loadwsgi.APP, conf_file)
except LookupError:
pass
else:
app_conf.update(ctx.app_context.global_conf)
app_conf.update(ctx.app_context.local_conf)
return app_conf
def run_server(conf, logger, sock, global_conf=None):
# Ensure TZ environment variable exists to avoid stat('/etc/localtime') on
# some platforms. This locks in reported times to UTC.

View File

@ -1309,6 +1309,9 @@ class TestServerSideCopyConfiguration(unittest.TestCase):
[pipeline:main]
pipeline = catch_errors copy ye-olde-proxy-server
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:copy]
use = egg:swift#copy

View File

@ -811,6 +811,9 @@ class TestDloConfiguration(unittest.TestCase):
[pipeline:main]
pipeline = catch_errors dlo ye-olde-proxy-server
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:dlo]
use = egg:swift#dlo
max_get_time = 3600
@ -845,13 +848,16 @@ class TestDloConfiguration(unittest.TestCase):
[pipeline:main]
pipeline = catch_errors dlo ye-olde-proxy-server
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:dlo]
use = egg:swift#dlo
[app:ye-olde-proxy-server]
use = egg:swift#proxy
rate_limit_after_segment = 13
max_get_time = 2900
set max_get_time = 2900
""")
conffile = tempfile.NamedTemporaryFile()
@ -878,6 +884,9 @@ class TestDloConfiguration(unittest.TestCase):
""")
proxy_conf2 = dedent("""
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:dlo]
use = egg:swift#dlo

View File

@ -198,6 +198,97 @@ class TestWSGI(unittest.TestCase):
app = wsgi.loadapp(wsgi.ConfigString(conf_body))
self.assertTrue(isinstance(app, obj_server.ObjectController))
@with_tempdir
def test_load_app_config(self, tempdir):
conf_file = os.path.join(tempdir, 'file.conf')
def _write_and_load_conf_file(conf):
with open(conf_file, 'wb') as fd:
fd.write(dedent(conf))
return wsgi.load_app_config(conf_file)
# typical case - DEFAULT options override same option in other sections
conf_str = """
[DEFAULT]
dflt_option = dflt-value
[pipeline:main]
pipeline = proxy-logging proxy-server
[filter:proxy-logging]
use = egg:swift#proxy_logging
[app:proxy-server]
use = egg:swift#proxy
proxy_option = proxy-value
dflt_option = proxy-dflt-value
"""
proxy_conf = _write_and_load_conf_file(conf_str)
self.assertEqual('proxy-value', proxy_conf['proxy_option'])
self.assertEqual('dflt-value', proxy_conf['dflt_option'])
# 'set' overrides DEFAULT option
conf_str = """
[DEFAULT]
dflt_option = dflt-value
[pipeline:main]
pipeline = proxy-logging proxy-server
[filter:proxy-logging]
use = egg:swift#proxy_logging
[app:proxy-server]
use = egg:swift#proxy
proxy_option = proxy-value
set dflt_option = proxy-dflt-value
"""
proxy_conf = _write_and_load_conf_file(conf_str)
self.assertEqual('proxy-value', proxy_conf['proxy_option'])
self.assertEqual('proxy-dflt-value', proxy_conf['dflt_option'])
# actual proxy server app name is dereferenced
conf_str = """
[pipeline:main]
pipeline = proxy-logging proxyserverapp
[filter:proxy-logging]
use = egg:swift#proxy_logging
[app:proxyserverapp]
use = egg:swift#proxy
proxy_option = proxy-value
dflt_option = proxy-dflt-value
"""
proxy_conf = _write_and_load_conf_file(conf_str)
self.assertEqual('proxy-value', proxy_conf['proxy_option'])
self.assertEqual('proxy-dflt-value', proxy_conf['dflt_option'])
# no pipeline
conf_str = """
[filter:proxy-logging]
use = egg:swift#proxy_logging
[app:proxy-server]
use = egg:swift#proxy
proxy_option = proxy-value
"""
proxy_conf = _write_and_load_conf_file(conf_str)
self.assertEqual({}, proxy_conf)
# no matching section
conf_str = """
[pipeline:main]
pipeline = proxy-logging proxy-server
[filter:proxy-logging]
use = egg:swift#proxy_logging
"""
proxy_conf = _write_and_load_conf_file(conf_str)
self.assertEqual({}, proxy_conf)
def test_init_request_processor_from_conf_dir(self):
config_dir = {
'proxy-server.conf.d/pipeline.conf': """