py3: (Better) fix percentages in configs

We previously fixed a bunch of places, but not quite *all* the places;
at the very least, some account-layer services (like the replicator and
auditor IIRC) could still bomb out -- and it's important that
replicators still respect fallocate_reserve!

Now, do the NicerInterpolation thing every time we call readconf.
Additionally, clean up the original fix to avoid globally
monkey-patching configparser.

Related-Bug: #1844368
Closes-Bug: #1872553
Change-Id: I4512e686cde37930f0482909f537220a57fef76b
This commit is contained in:
Tim Burke 2020-06-09 16:45:21 -07:00
parent f58791f376
commit 2854eddb44
2 changed files with 28 additions and 20 deletions

View File

@ -76,6 +76,7 @@ if not six.PY2:
utf16_decoder = codecs.getdecoder('utf-16') utf16_decoder = codecs.getdecoder('utf-16')
utf16_encoder = codecs.getencoder('utf-16') utf16_encoder = codecs.getencoder('utf-16')
from six.moves import cPickle as pickle from six.moves import cPickle as pickle
from six.moves import configparser
from six.moves.configparser import (ConfigParser, NoSectionError, from six.moves.configparser import (ConfigParser, NoSectionError,
NoOptionError, RawConfigParser) NoOptionError, RawConfigParser)
from six.moves import range, http_client from six.moves import range, http_client
@ -2985,6 +2986,17 @@ def read_conf_dir(parser, conf_dir):
return parser.read(sorted(conf_files)) return parser.read(sorted(conf_files))
if six.PY2:
NicerInterpolation = None # just don't cause ImportErrors over in wsgi.py
else:
class NicerInterpolation(configparser.BasicInterpolation):
def before_get(self, parser, section, option, value, defaults):
if '%(' not in value:
return value
return super(NicerInterpolation, self).before_get(
parser, section, option, value, defaults)
def readconf(conf_path, section_name=None, log_name=None, defaults=None, def readconf(conf_path, section_name=None, log_name=None, defaults=None,
raw=False): raw=False):
""" """
@ -3006,7 +3018,19 @@ def readconf(conf_path, section_name=None, log_name=None, defaults=None,
if raw: if raw:
c = RawConfigParser(defaults) c = RawConfigParser(defaults)
else: else:
c = ConfigParser(defaults) if six.PY2:
c = ConfigParser(defaults)
else:
# In general, we haven't really thought much about interpolation
# in configs. Python's default ConfigParser has always supported
# it, though, so *we* got it "for free". Unfortunatley, since we
# "supported" interpolation, we have to assume there are
# deployments in the wild that use it, and try not to break them.
# So, do what we can to mimic the py2 behavior of passing through
# values like "1%" (which we want to support for
# fallocate_reserve).
c = ConfigParser(defaults, interpolation=NicerInterpolation())
if hasattr(conf_path, 'readline'): if hasattr(conf_path, 'readline'):
if hasattr(conf_path, 'seek'): if hasattr(conf_path, 'seek'):
conf_path.seek(0) conf_path.seek(0)

View File

@ -36,7 +36,6 @@ from io import BytesIO
import six import six
from six import StringIO from six import StringIO
from six.moves import configparser
from swift.common import utils, constraints from swift.common import utils, constraints
from swift.common.storage_policy import BindPortsCache from swift.common.storage_policy import BindPortsCache
@ -45,7 +44,7 @@ from swift.common.swob import Request, wsgi_quote, wsgi_unquote, \
from swift.common.utils import capture_stdio, disable_fallocate, \ from swift.common.utils import capture_stdio, disable_fallocate, \
drop_privileges, get_logger, NullLogger, config_true_value, \ drop_privileges, get_logger, NullLogger, config_true_value, \
validate_configuration, get_hub, config_auto_int_value, \ validate_configuration, get_hub, config_auto_int_value, \
reiterate, clean_up_daemon_hygiene, systemd_notify reiterate, clean_up_daemon_hygiene, systemd_notify, NicerInterpolation
SIGNUM_TO_NAME = {getattr(signal, n): n for n in dir(signal) SIGNUM_TO_NAME = {getattr(signal, n): n for n in dir(signal)
if n.startswith('SIG') and '_' not in n} if n.startswith('SIG') and '_' not in n}
@ -61,23 +60,6 @@ except (ImportError, NotImplementedError):
CPU_COUNT = 1 CPU_COUNT = 1
if not six.PY2:
# In general, we haven't really thought much about interpolation in
# configs. Python's default ConfigParser has always supported it, though,
# so *we* got it "for free". Unfortunatley, since we "supported"
# interpolation, we have to assume there are deployments in the wild that
# use it, and try not to break them. So, do what we can to mimic the py2
# behavior of passing through values like "1%" (which we want to support
# for fallocate_reserve).
class NicerInterpolation(configparser.BasicInterpolation):
def before_get(self, parser, section, option, value, defaults):
if '%(' not in value:
return value
return super(NicerInterpolation, self).before_get(
parser, section, option, value, defaults)
configparser.ConfigParser._DEFAULT_INTERPOLATION = NicerInterpolation()
class NamedConfigLoader(loadwsgi.ConfigLoader): class NamedConfigLoader(loadwsgi.ConfigLoader):
""" """
Patch paste.deploy's ConfigLoader so each context object will know what Patch paste.deploy's ConfigLoader so each context object will know what
@ -85,6 +67,8 @@ class NamedConfigLoader(loadwsgi.ConfigLoader):
""" """
def get_context(self, object_type, name=None, global_conf=None): def get_context(self, object_type, name=None, global_conf=None):
if not six.PY2:
self.parser._interpolation = NicerInterpolation()
context = super(NamedConfigLoader, self).get_context( context = super(NamedConfigLoader, self).get_context(
object_type, name=name, global_conf=global_conf) object_type, name=name, global_conf=global_conf)
context.name = name context.name = name