Merge "Refactor memcache config and MemcacheRing loading"
This commit is contained in:
commit
32600ee56b
@ -44,18 +44,20 @@ version is at:
|
|||||||
http://github.com/memcached/memcached/blob/1.4.2/doc/protocol.txt
|
http://github.com/memcached/memcached/blob/1.4.2/doc/protocol.txt
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import six
|
import six
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from bisect import bisect
|
from bisect import bisect
|
||||||
|
|
||||||
from eventlet.green import socket
|
from eventlet.green import socket, ssl
|
||||||
from eventlet.pools import Pool
|
from eventlet.pools import Pool
|
||||||
from eventlet import Timeout
|
from eventlet import Timeout
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError
|
||||||
from swift.common import utils
|
from swift.common import utils
|
||||||
from swift.common.utils import md5, human_readable
|
from swift.common.utils import md5, human_readable, config_true_value
|
||||||
|
|
||||||
DEFAULT_MEMCACHED_PORT = 11211
|
DEFAULT_MEMCACHED_PORT = 11211
|
||||||
|
|
||||||
@ -204,6 +206,10 @@ class MemcacheRing(object):
|
|||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.item_size_warning_threshold = item_size_warning_threshold
|
self.item_size_warning_threshold = item_size_warning_threshold
|
||||||
|
|
||||||
|
@property
|
||||||
|
def memcache_servers(self):
|
||||||
|
return list(self._client_cache.keys())
|
||||||
|
|
||||||
def _exception_occurred(self, server, e, action='talking',
|
def _exception_occurred(self, server, e, action='talking',
|
||||||
sock=None, fp=None, got_connection=True):
|
sock=None, fp=None, got_connection=True):
|
||||||
if isinstance(e, Timeout):
|
if isinstance(e, Timeout):
|
||||||
@ -554,3 +560,96 @@ class MemcacheRing(object):
|
|||||||
return values
|
return values
|
||||||
except (Exception, Timeout) as e:
|
except (Exception, Timeout) as e:
|
||||||
self._exception_occurred(server, e, sock=sock, fp=fp)
|
self._exception_occurred(server, e, sock=sock, fp=fp)
|
||||||
|
|
||||||
|
|
||||||
|
def load_memcache(conf, logger):
|
||||||
|
"""
|
||||||
|
Build a MemcacheRing object from the given config. It will also use the
|
||||||
|
passed in logger.
|
||||||
|
|
||||||
|
:param conf: a dict, the config options
|
||||||
|
:param logger: a logger
|
||||||
|
"""
|
||||||
|
memcache_servers = conf.get('memcache_servers')
|
||||||
|
try:
|
||||||
|
# Originally, while we documented using memcache_max_connections
|
||||||
|
# we only accepted max_connections
|
||||||
|
max_conns = int(conf.get('memcache_max_connections',
|
||||||
|
conf.get('max_connections', 0)))
|
||||||
|
except ValueError:
|
||||||
|
max_conns = 0
|
||||||
|
|
||||||
|
memcache_options = {}
|
||||||
|
if (not memcache_servers
|
||||||
|
or max_conns <= 0):
|
||||||
|
path = os.path.join(conf.get('swift_dir', '/etc/swift'),
|
||||||
|
'memcache.conf')
|
||||||
|
memcache_conf = ConfigParser()
|
||||||
|
if memcache_conf.read(path):
|
||||||
|
# if memcache.conf exists we'll start with those base options
|
||||||
|
try:
|
||||||
|
memcache_options = dict(memcache_conf.items('memcache'))
|
||||||
|
except NoSectionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not memcache_servers:
|
||||||
|
try:
|
||||||
|
memcache_servers = \
|
||||||
|
memcache_conf.get('memcache', 'memcache_servers')
|
||||||
|
except (NoSectionError, NoOptionError):
|
||||||
|
pass
|
||||||
|
if max_conns <= 0:
|
||||||
|
try:
|
||||||
|
new_max_conns = \
|
||||||
|
memcache_conf.get('memcache',
|
||||||
|
'memcache_max_connections')
|
||||||
|
max_conns = int(new_max_conns)
|
||||||
|
except (NoSectionError, NoOptionError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# while memcache.conf options are the base for the memcache
|
||||||
|
# middleware, if you set the same option also in the filter
|
||||||
|
# section of the proxy config it is more specific.
|
||||||
|
memcache_options.update(conf)
|
||||||
|
connect_timeout = float(memcache_options.get(
|
||||||
|
'connect_timeout', CONN_TIMEOUT))
|
||||||
|
pool_timeout = float(memcache_options.get(
|
||||||
|
'pool_timeout', POOL_TIMEOUT))
|
||||||
|
tries = int(memcache_options.get('tries', TRY_COUNT))
|
||||||
|
io_timeout = float(memcache_options.get('io_timeout', IO_TIMEOUT))
|
||||||
|
if config_true_value(memcache_options.get('tls_enabled', 'false')):
|
||||||
|
tls_cafile = memcache_options.get('tls_cafile')
|
||||||
|
tls_certfile = memcache_options.get('tls_certfile')
|
||||||
|
tls_keyfile = memcache_options.get('tls_keyfile')
|
||||||
|
tls_context = ssl.create_default_context(
|
||||||
|
cafile=tls_cafile)
|
||||||
|
if tls_certfile:
|
||||||
|
tls_context.load_cert_chain(tls_certfile, tls_keyfile)
|
||||||
|
else:
|
||||||
|
tls_context = None
|
||||||
|
error_suppression_interval = float(memcache_options.get(
|
||||||
|
'error_suppression_interval', ERROR_LIMIT_TIME))
|
||||||
|
error_suppression_limit = float(memcache_options.get(
|
||||||
|
'error_suppression_limit', ERROR_LIMIT_COUNT))
|
||||||
|
item_size_warning_threshold = int(memcache_options.get(
|
||||||
|
'item_size_warning_threshold', DEFAULT_ITEM_SIZE_WARNING_THRESHOLD))
|
||||||
|
|
||||||
|
if not memcache_servers:
|
||||||
|
memcache_servers = '127.0.0.1:11211'
|
||||||
|
if max_conns <= 0:
|
||||||
|
max_conns = 2
|
||||||
|
|
||||||
|
return MemcacheRing(
|
||||||
|
[s.strip() for s in memcache_servers.split(',')
|
||||||
|
if s.strip()],
|
||||||
|
connect_timeout=connect_timeout,
|
||||||
|
pool_timeout=pool_timeout,
|
||||||
|
tries=tries,
|
||||||
|
io_timeout=io_timeout,
|
||||||
|
max_conns=max_conns,
|
||||||
|
tls_context=tls_context,
|
||||||
|
logger=logger,
|
||||||
|
error_limit_count=error_suppression_limit,
|
||||||
|
error_limit_time=error_suppression_interval,
|
||||||
|
error_limit_duration=error_suppression_interval,
|
||||||
|
item_size_warning_threshold=item_size_warning_threshold)
|
||||||
|
@ -13,15 +13,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
from swift.common.memcached import load_memcache
|
||||||
|
from swift.common.utils import get_logger
|
||||||
from eventlet.green import ssl
|
|
||||||
from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError
|
|
||||||
|
|
||||||
from swift.common.memcached import (
|
|
||||||
MemcacheRing, CONN_TIMEOUT, POOL_TIMEOUT, IO_TIMEOUT, TRY_COUNT,
|
|
||||||
ERROR_LIMIT_COUNT, ERROR_LIMIT_TIME, DEFAULT_ITEM_SIZE_WARNING_THRESHOLD)
|
|
||||||
from swift.common.utils import get_logger, config_true_value
|
|
||||||
|
|
||||||
|
|
||||||
class MemcacheMiddleware(object):
|
class MemcacheMiddleware(object):
|
||||||
@ -32,90 +25,7 @@ class MemcacheMiddleware(object):
|
|||||||
def __init__(self, app, conf):
|
def __init__(self, app, conf):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.logger = get_logger(conf, log_route='memcache')
|
self.logger = get_logger(conf, log_route='memcache')
|
||||||
self.memcache_servers = conf.get('memcache_servers')
|
self.memcache = load_memcache(conf, self.logger)
|
||||||
try:
|
|
||||||
# Originally, while we documented using memcache_max_connections
|
|
||||||
# we only accepted max_connections
|
|
||||||
max_conns = int(conf.get('memcache_max_connections',
|
|
||||||
conf.get('max_connections', 0)))
|
|
||||||
except ValueError:
|
|
||||||
max_conns = 0
|
|
||||||
|
|
||||||
memcache_options = {}
|
|
||||||
if (not self.memcache_servers
|
|
||||||
or max_conns <= 0):
|
|
||||||
path = os.path.join(conf.get('swift_dir', '/etc/swift'),
|
|
||||||
'memcache.conf')
|
|
||||||
memcache_conf = ConfigParser()
|
|
||||||
if memcache_conf.read(path):
|
|
||||||
# if memcache.conf exists we'll start with those base options
|
|
||||||
try:
|
|
||||||
memcache_options = dict(memcache_conf.items('memcache'))
|
|
||||||
except NoSectionError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not self.memcache_servers:
|
|
||||||
try:
|
|
||||||
self.memcache_servers = \
|
|
||||||
memcache_conf.get('memcache', 'memcache_servers')
|
|
||||||
except (NoSectionError, NoOptionError):
|
|
||||||
pass
|
|
||||||
if max_conns <= 0:
|
|
||||||
try:
|
|
||||||
new_max_conns = \
|
|
||||||
memcache_conf.get('memcache',
|
|
||||||
'memcache_max_connections')
|
|
||||||
max_conns = int(new_max_conns)
|
|
||||||
except (NoSectionError, NoOptionError, ValueError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# while memcache.conf options are the base for the memcache
|
|
||||||
# middleware, if you set the same option also in the filter
|
|
||||||
# section of the proxy config it is more specific.
|
|
||||||
memcache_options.update(conf)
|
|
||||||
connect_timeout = float(memcache_options.get(
|
|
||||||
'connect_timeout', CONN_TIMEOUT))
|
|
||||||
pool_timeout = float(memcache_options.get(
|
|
||||||
'pool_timeout', POOL_TIMEOUT))
|
|
||||||
tries = int(memcache_options.get('tries', TRY_COUNT))
|
|
||||||
io_timeout = float(memcache_options.get('io_timeout', IO_TIMEOUT))
|
|
||||||
if config_true_value(memcache_options.get('tls_enabled', 'false')):
|
|
||||||
tls_cafile = memcache_options.get('tls_cafile')
|
|
||||||
tls_certfile = memcache_options.get('tls_certfile')
|
|
||||||
tls_keyfile = memcache_options.get('tls_keyfile')
|
|
||||||
self.tls_context = ssl.create_default_context(
|
|
||||||
cafile=tls_cafile)
|
|
||||||
if tls_certfile:
|
|
||||||
self.tls_context.load_cert_chain(tls_certfile,
|
|
||||||
tls_keyfile)
|
|
||||||
else:
|
|
||||||
self.tls_context = None
|
|
||||||
error_suppression_interval = float(memcache_options.get(
|
|
||||||
'error_suppression_interval', ERROR_LIMIT_TIME))
|
|
||||||
error_suppression_limit = float(memcache_options.get(
|
|
||||||
'error_suppression_limit', ERROR_LIMIT_COUNT))
|
|
||||||
item_size_warning_threshold = int(memcache_options.get(
|
|
||||||
'item_size_warning_threshold',
|
|
||||||
DEFAULT_ITEM_SIZE_WARNING_THRESHOLD))
|
|
||||||
|
|
||||||
if not self.memcache_servers:
|
|
||||||
self.memcache_servers = '127.0.0.1:11211'
|
|
||||||
if max_conns <= 0:
|
|
||||||
max_conns = 2
|
|
||||||
|
|
||||||
self.memcache = MemcacheRing(
|
|
||||||
[s.strip() for s in self.memcache_servers.split(',') if s.strip()],
|
|
||||||
connect_timeout=connect_timeout,
|
|
||||||
pool_timeout=pool_timeout,
|
|
||||||
tries=tries,
|
|
||||||
io_timeout=io_timeout,
|
|
||||||
max_conns=max_conns,
|
|
||||||
tls_context=self.tls_context,
|
|
||||||
logger=self.logger,
|
|
||||||
error_limit_count=error_suppression_limit,
|
|
||||||
error_limit_time=error_suppression_interval,
|
|
||||||
error_limit_duration=error_suppression_interval,
|
|
||||||
item_size_warning_threshold=item_size_warning_threshold)
|
|
||||||
|
|
||||||
def __call__(self, env, start_response):
|
def __call__(self, env, start_response):
|
||||||
env['swift.cache'] = self.memcache
|
env['swift.cache'] = self.memcache
|
||||||
|
@ -19,7 +19,6 @@ import unittest
|
|||||||
|
|
||||||
from eventlet.green import ssl
|
from eventlet.green import ssl
|
||||||
import mock
|
import mock
|
||||||
from six.moves.configparser import NoSectionError, NoOptionError
|
|
||||||
|
|
||||||
from swift.common.middleware import memcache
|
from swift.common.middleware import memcache
|
||||||
from swift.common.memcached import MemcacheRing
|
from swift.common.memcached import MemcacheRing
|
||||||
@ -34,63 +33,6 @@ class FakeApp(object):
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
class ExcConfigParser(object):
|
|
||||||
|
|
||||||
def read(self, path):
|
|
||||||
raise RuntimeError('read called with %r' % path)
|
|
||||||
|
|
||||||
|
|
||||||
class EmptyConfigParser(object):
|
|
||||||
|
|
||||||
def read(self, path):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_config_parser(memcache_servers='1.2.3.4:5',
|
|
||||||
memcache_max_connections='4',
|
|
||||||
section='memcache',
|
|
||||||
item_size_warning_threshold='75'):
|
|
||||||
_srvs = memcache_servers
|
|
||||||
_maxc = memcache_max_connections
|
|
||||||
_section = section
|
|
||||||
_warn_threshold = item_size_warning_threshold
|
|
||||||
|
|
||||||
class SetConfigParser(object):
|
|
||||||
|
|
||||||
def items(self, section_name):
|
|
||||||
if section_name != section:
|
|
||||||
raise NoSectionError(section_name)
|
|
||||||
return {
|
|
||||||
'memcache_servers': memcache_servers,
|
|
||||||
'memcache_max_connections': memcache_max_connections
|
|
||||||
}
|
|
||||||
|
|
||||||
def read(self, path):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get(self, section, option):
|
|
||||||
if _section == section:
|
|
||||||
if option == 'memcache_servers':
|
|
||||||
if _srvs == 'error':
|
|
||||||
raise NoOptionError(option, section)
|
|
||||||
return _srvs
|
|
||||||
elif option in ('memcache_max_connections',
|
|
||||||
'max_connections'):
|
|
||||||
if _maxc == 'error':
|
|
||||||
raise NoOptionError(option, section)
|
|
||||||
return _maxc
|
|
||||||
elif option == 'item_size_warning_threshold':
|
|
||||||
if _warn_threshold == 'error':
|
|
||||||
raise NoOptionError(option, section)
|
|
||||||
return _warn_threshold
|
|
||||||
else:
|
|
||||||
raise NoOptionError(option, section)
|
|
||||||
else:
|
|
||||||
raise NoSectionError(option)
|
|
||||||
|
|
||||||
return SetConfigParser
|
|
||||||
|
|
||||||
|
|
||||||
def start_response(*args):
|
def start_response(*args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -106,165 +48,13 @@ class TestCacheMiddleware(unittest.TestCase):
|
|||||||
self.assertTrue('swift.cache' in resp)
|
self.assertTrue('swift.cache' in resp)
|
||||||
self.assertTrue(isinstance(resp['swift.cache'], MemcacheRing))
|
self.assertTrue(isinstance(resp['swift.cache'], MemcacheRing))
|
||||||
|
|
||||||
def test_conf_default_read(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', ExcConfigParser):
|
|
||||||
for d in ({},
|
|
||||||
{'memcache_servers': '6.7.8.9:10'},
|
|
||||||
{'memcache_max_connections': '30'},
|
|
||||||
{'item_size_warning_threshold': 75},
|
|
||||||
{'memcache_servers': '6.7.8.9:10',
|
|
||||||
'item_size_warning_threshold': '75'},
|
|
||||||
{'item_size_warning_threshold': '75',
|
|
||||||
'memcache_max_connections': '30'},
|
|
||||||
):
|
|
||||||
with self.assertRaises(RuntimeError) as catcher:
|
|
||||||
memcache.MemcacheMiddleware(FakeApp(), d)
|
|
||||||
self.assertEqual(
|
|
||||||
str(catcher.exception),
|
|
||||||
"read called with '/etc/swift/memcache.conf'")
|
|
||||||
|
|
||||||
def test_conf_set_no_read(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', ExcConfigParser):
|
|
||||||
memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(), {'memcache_servers': '1.2.3.4:5',
|
|
||||||
'memcache_max_connections': '30',
|
|
||||||
'item_size_warning_threshold': '80'})
|
|
||||||
|
|
||||||
def test_conf_default(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', EmptyConfigParser):
|
|
||||||
app = memcache.MemcacheMiddleware(FakeApp(), {})
|
|
||||||
self.assertEqual(app.memcache_servers, '127.0.0.1:11211')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['127.0.0.1:11211'].max_size, 2)
|
|
||||||
self.assertEqual(app.memcache.item_size_warning_threshold, -1)
|
|
||||||
|
|
||||||
def test_conf_inline(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'memcache_servers': '6.7.8.9:10',
|
|
||||||
'memcache_max_connections': '5',
|
|
||||||
'item_size_warning_threshold': '75'})
|
|
||||||
self.assertEqual(app.memcache_servers, '6.7.8.9:10')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['6.7.8.9:10'].max_size, 5)
|
|
||||||
self.assertEqual(app.memcache.item_size_warning_threshold, 75)
|
|
||||||
|
|
||||||
def test_conf_inline_ratelimiting(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'error_suppression_limit': '5',
|
|
||||||
'error_suppression_interval': '2.5'})
|
|
||||||
self.assertEqual(app.memcache._error_limit_count, 5)
|
|
||||||
self.assertEqual(app.memcache._error_limit_time, 2.5)
|
|
||||||
self.assertEqual(app.memcache._error_limit_duration, 2.5)
|
|
||||||
|
|
||||||
def test_conf_inline_tls(self):
|
|
||||||
fake_context = mock.Mock()
|
|
||||||
with mock.patch.object(ssl, 'create_default_context',
|
|
||||||
return_value=fake_context):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser',
|
|
||||||
get_config_parser()):
|
|
||||||
memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'tls_enabled': 'true',
|
|
||||||
'tls_cafile': 'cafile',
|
|
||||||
'tls_certfile': 'certfile',
|
|
||||||
'tls_keyfile': 'keyfile'})
|
|
||||||
ssl.create_default_context.assert_called_with(cafile='cafile')
|
|
||||||
fake_context.load_cert_chain.assert_called_with('certfile',
|
|
||||||
'keyfile')
|
|
||||||
|
|
||||||
def test_conf_extra_no_section(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser',
|
|
||||||
get_config_parser(section='foobar')):
|
|
||||||
app = memcache.MemcacheMiddleware(FakeApp(), {})
|
|
||||||
self.assertEqual(app.memcache_servers, '127.0.0.1:11211')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['127.0.0.1:11211'].max_size, 2)
|
|
||||||
|
|
||||||
def test_conf_extra_no_option(self):
|
|
||||||
replacement_parser = get_config_parser(
|
|
||||||
memcache_servers='error',
|
|
||||||
memcache_max_connections='error')
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', replacement_parser):
|
|
||||||
app = memcache.MemcacheMiddleware(FakeApp(), {})
|
|
||||||
self.assertEqual(app.memcache_servers, '127.0.0.1:11211')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['127.0.0.1:11211'].max_size, 2)
|
|
||||||
|
|
||||||
def test_conf_inline_other_max_conn(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'memcache_servers': '6.7.8.9:10',
|
|
||||||
'max_connections': '5'})
|
|
||||||
self.assertEqual(app.memcache_servers, '6.7.8.9:10')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['6.7.8.9:10'].max_size, 5)
|
|
||||||
|
|
||||||
def test_conf_inline_bad_max_conn(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'memcache_servers': '6.7.8.9:10',
|
|
||||||
'max_connections': 'bad42'})
|
|
||||||
self.assertEqual(app.memcache_servers, '6.7.8.9:10')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['6.7.8.9:10'].max_size, 4)
|
|
||||||
|
|
||||||
def test_conf_inline_bad_item_warning_threshold(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
with self.assertRaises(ValueError) as err:
|
|
||||||
memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'memcache_servers': '6.7.8.9:10',
|
|
||||||
'memcache_serialization_support': '0',
|
|
||||||
'item_size_warning_threshold': 'bad42'})
|
|
||||||
self.assertIn('invalid literal for int() with base 10:',
|
|
||||||
str(err.exception))
|
|
||||||
|
|
||||||
def test_conf_from_extra_conf(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(FakeApp(), {})
|
|
||||||
self.assertEqual(app.memcache_servers, '1.2.3.4:5')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['1.2.3.4:5'].max_size, 4)
|
|
||||||
|
|
||||||
def test_conf_from_extra_conf_bad_max_conn(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser(
|
|
||||||
memcache_max_connections='bad42')):
|
|
||||||
app = memcache.MemcacheMiddleware(FakeApp(), {})
|
|
||||||
self.assertEqual(app.memcache_servers, '1.2.3.4:5')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['1.2.3.4:5'].max_size, 2)
|
|
||||||
|
|
||||||
def test_conf_from_inline_and_maxc_from_extra_conf(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'memcache_servers': '6.7.8.9:10'})
|
|
||||||
self.assertEqual(app.memcache_servers, '6.7.8.9:10')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['6.7.8.9:10'].max_size, 4)
|
|
||||||
|
|
||||||
def test_conf_from_inline_and_sers_from_extra_conf(self):
|
|
||||||
with mock.patch.object(memcache, 'ConfigParser', get_config_parser()):
|
|
||||||
app = memcache.MemcacheMiddleware(
|
|
||||||
FakeApp(),
|
|
||||||
{'memcache_servers': '6.7.8.9:10',
|
|
||||||
'memcache_max_connections': '42'})
|
|
||||||
self.assertEqual(app.memcache_servers, '6.7.8.9:10')
|
|
||||||
self.assertEqual(
|
|
||||||
app.memcache._client_cache['6.7.8.9:10'].max_size, 42)
|
|
||||||
|
|
||||||
def test_filter_factory(self):
|
def test_filter_factory(self):
|
||||||
factory = memcache.filter_factory({'max_connections': '3'},
|
factory = memcache.filter_factory({'max_connections': '3'},
|
||||||
memcache_servers='10.10.10.10:10')
|
memcache_servers='10.10.10.10:10')
|
||||||
thefilter = factory('myapp')
|
thefilter = factory('myapp')
|
||||||
self.assertEqual(thefilter.app, 'myapp')
|
self.assertEqual(thefilter.app, 'myapp')
|
||||||
self.assertEqual(thefilter.memcache_servers, '10.10.10.10:10')
|
self.assertEqual(thefilter.memcache.memcache_servers,
|
||||||
|
['10.10.10.10:10'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
thefilter.memcache._client_cache['10.10.10.10:10'].max_size, 3)
|
thefilter.memcache._client_cache['10.10.10.10:10'].max_size, 3)
|
||||||
|
|
||||||
|
@ -27,9 +27,11 @@ from uuid import uuid4
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
from six.moves.configparser import NoSectionError, NoOptionError
|
||||||
|
|
||||||
from eventlet import GreenPool, sleep, Queue
|
from eventlet import GreenPool, sleep, Queue
|
||||||
from eventlet.pools import Pool
|
from eventlet.pools import Pool
|
||||||
|
from eventlet.green import ssl
|
||||||
|
|
||||||
from swift.common import memcached
|
from swift.common import memcached
|
||||||
from swift.common.memcached import MemcacheConnectionError
|
from swift.common.memcached import MemcacheConnectionError
|
||||||
@ -1047,5 +1049,229 @@ class TestMemcached(unittest.TestCase):
|
|||||||
do_test('1' * 2048576, 1000000, True)
|
do_test('1' * 2048576, 1000000, True)
|
||||||
|
|
||||||
|
|
||||||
|
class ExcConfigParser(object):
|
||||||
|
|
||||||
|
def read(self, path):
|
||||||
|
raise Exception('read called with %r' % path)
|
||||||
|
|
||||||
|
|
||||||
|
class EmptyConfigParser(object):
|
||||||
|
|
||||||
|
def read(self, path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_parser(memcache_servers='1.2.3.4:5',
|
||||||
|
memcache_max_connections='4',
|
||||||
|
section='memcache',
|
||||||
|
item_size_warning_threshold='75'):
|
||||||
|
_srvs = memcache_servers
|
||||||
|
_maxc = memcache_max_connections
|
||||||
|
_section = section
|
||||||
|
_warn_threshold = item_size_warning_threshold
|
||||||
|
|
||||||
|
class SetConfigParser(object):
|
||||||
|
|
||||||
|
def items(self, section_name):
|
||||||
|
if section_name != section:
|
||||||
|
raise NoSectionError(section_name)
|
||||||
|
return {
|
||||||
|
'memcache_servers': memcache_servers,
|
||||||
|
'memcache_max_connections': memcache_max_connections
|
||||||
|
}
|
||||||
|
|
||||||
|
def read(self, path):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get(self, section, option):
|
||||||
|
if _section == section:
|
||||||
|
if option == 'memcache_servers':
|
||||||
|
if _srvs == 'error':
|
||||||
|
raise NoOptionError(option, section)
|
||||||
|
return _srvs
|
||||||
|
elif option in ('memcache_max_connections',
|
||||||
|
'max_connections'):
|
||||||
|
if _maxc == 'error':
|
||||||
|
raise NoOptionError(option, section)
|
||||||
|
return _maxc
|
||||||
|
elif option == 'item_size_warning_threshold':
|
||||||
|
if _warn_threshold == 'error':
|
||||||
|
raise NoOptionError(option, section)
|
||||||
|
return _warn_threshold
|
||||||
|
else:
|
||||||
|
raise NoOptionError(option, section)
|
||||||
|
else:
|
||||||
|
raise NoSectionError(option)
|
||||||
|
|
||||||
|
return SetConfigParser
|
||||||
|
|
||||||
|
|
||||||
|
def start_response(*args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestLoadMemcache(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.logger = debug_logger()
|
||||||
|
|
||||||
|
def test_conf_default_read(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', ExcConfigParser):
|
||||||
|
for d in ({},
|
||||||
|
{'memcache_servers': '6.7.8.9:10'},
|
||||||
|
{'memcache_max_connections': '30'},
|
||||||
|
{'item_size_warning_threshold': 75},
|
||||||
|
{'memcache_servers': '6.7.8.9:10',
|
||||||
|
'item_size_warning_threshold': '75'},
|
||||||
|
{'item_size_warning_threshold': '75',
|
||||||
|
'memcache_max_connections': '30'},
|
||||||
|
):
|
||||||
|
with self.assertRaises(Exception) as catcher:
|
||||||
|
memcached.load_memcache(d, self.logger)
|
||||||
|
self.assertEqual(
|
||||||
|
str(catcher.exception),
|
||||||
|
"read called with '/etc/swift/memcache.conf'")
|
||||||
|
|
||||||
|
def test_conf_set_no_read(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', ExcConfigParser):
|
||||||
|
exc = None
|
||||||
|
try:
|
||||||
|
memcached.load_memcache({
|
||||||
|
'memcache_servers': '1.2.3.4:5',
|
||||||
|
'memcache_max_connections': '30',
|
||||||
|
'item_size_warning_threshold': '80'
|
||||||
|
|
||||||
|
}, self.logger)
|
||||||
|
except Exception as err:
|
||||||
|
exc = err
|
||||||
|
self.assertIsNone(exc)
|
||||||
|
|
||||||
|
def test_conf_default(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', EmptyConfigParser):
|
||||||
|
memcache = memcached.load_memcache({}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['127.0.0.1:11211'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['127.0.0.1:11211'].max_size, 2)
|
||||||
|
self.assertEqual(memcache.item_size_warning_threshold, -1)
|
||||||
|
|
||||||
|
def test_conf_inline(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({
|
||||||
|
'memcache_servers': '6.7.8.9:10',
|
||||||
|
'memcache_max_connections': '5',
|
||||||
|
'item_size_warning_threshold': '75'
|
||||||
|
}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['6.7.8.9:10'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['6.7.8.9:10'].max_size, 5)
|
||||||
|
self.assertEqual(memcache.item_size_warning_threshold, 75)
|
||||||
|
|
||||||
|
def test_conf_inline_ratelimiting(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({
|
||||||
|
'error_suppression_limit': '5',
|
||||||
|
'error_suppression_interval': '2.5',
|
||||||
|
}, self.logger)
|
||||||
|
self.assertEqual(memcache._error_limit_count, 5)
|
||||||
|
self.assertEqual(memcache._error_limit_time, 2.5)
|
||||||
|
self.assertEqual(memcache._error_limit_duration, 2.5)
|
||||||
|
|
||||||
|
def test_conf_inline_tls(self):
|
||||||
|
fake_context = mock.Mock()
|
||||||
|
with mock.patch.object(ssl, 'create_default_context',
|
||||||
|
return_value=fake_context):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser',
|
||||||
|
get_config_parser()):
|
||||||
|
memcached.load_memcache({
|
||||||
|
'tls_enabled': 'true',
|
||||||
|
'tls_cafile': 'cafile',
|
||||||
|
'tls_certfile': 'certfile',
|
||||||
|
'tls_keyfile': 'keyfile',
|
||||||
|
}, self.logger)
|
||||||
|
ssl.create_default_context.assert_called_with(cafile='cafile')
|
||||||
|
fake_context.load_cert_chain.assert_called_with('certfile',
|
||||||
|
'keyfile')
|
||||||
|
|
||||||
|
def test_conf_extra_no_section(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser',
|
||||||
|
get_config_parser(section='foobar')):
|
||||||
|
memcache = memcached.load_memcache({}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['127.0.0.1:11211'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['127.0.0.1:11211'].max_size, 2)
|
||||||
|
|
||||||
|
def test_conf_extra_no_option(self):
|
||||||
|
replacement_parser = get_config_parser(
|
||||||
|
memcache_servers='error',
|
||||||
|
memcache_max_connections='error')
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', replacement_parser):
|
||||||
|
memcache = memcached.load_memcache({}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['127.0.0.1:11211'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['127.0.0.1:11211'].max_size, 2)
|
||||||
|
|
||||||
|
def test_conf_inline_other_max_conn(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({
|
||||||
|
'memcache_servers': '6.7.8.9:10',
|
||||||
|
'max_connections': '5'
|
||||||
|
}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['6.7.8.9:10'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['6.7.8.9:10'].max_size, 5)
|
||||||
|
|
||||||
|
def test_conf_inline_bad_max_conn(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({
|
||||||
|
'memcache_servers': '6.7.8.9:10',
|
||||||
|
'max_connections': 'bad42',
|
||||||
|
}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['6.7.8.9:10'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['6.7.8.9:10'].max_size, 4)
|
||||||
|
|
||||||
|
def test_conf_inline_bad_item_warning_threshold(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
with self.assertRaises(ValueError) as err:
|
||||||
|
memcached.load_memcache({
|
||||||
|
'memcache_servers': '6.7.8.9:10',
|
||||||
|
'item_size_warning_threshold': 'bad42',
|
||||||
|
}, self.logger)
|
||||||
|
self.assertIn('invalid literal for int() with base 10:',
|
||||||
|
str(err.exception))
|
||||||
|
|
||||||
|
def test_conf_from_extra_conf(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['1.2.3.4:5'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['1.2.3.4:5'].max_size, 4)
|
||||||
|
|
||||||
|
def test_conf_from_extra_conf_bad_max_conn(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser(
|
||||||
|
memcache_max_connections='bad42')):
|
||||||
|
memcache = memcached.load_memcache({}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['1.2.3.4:5'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['1.2.3.4:5'].max_size, 2)
|
||||||
|
|
||||||
|
def test_conf_from_inline_and_maxc_from_extra_conf(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({
|
||||||
|
'memcache_servers': '6.7.8.9:10'}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['6.7.8.9:10'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['6.7.8.9:10'].max_size, 4)
|
||||||
|
|
||||||
|
def test_conf_from_inline_and_sers_from_extra_conf(self):
|
||||||
|
with mock.patch.object(memcached, 'ConfigParser', get_config_parser()):
|
||||||
|
memcache = memcached.load_memcache({
|
||||||
|
'memcache_servers': '6.7.8.9:10',
|
||||||
|
'memcache_max_connections': '42',
|
||||||
|
}, self.logger)
|
||||||
|
self.assertEqual(memcache.memcache_servers, ['6.7.8.9:10'])
|
||||||
|
self.assertEqual(
|
||||||
|
memcache._client_cache['6.7.8.9:10'].max_size, 42)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user