From a8516e15108893301f586143de902153646ba9fd Mon Sep 17 00:00:00 2001 From: Matthew Oliver Date: Tue, 7 Dec 2021 13:24:19 +1100 Subject: [PATCH] Refactor memcache config and MemcacheRing loading The loading and creation of the Memcache ring in the middleware is rather interesting. It not only reads the config file, but also may look for a `/etc/swift/memcache.conf`. Further, we are know are looking at using the MemcacheRing client in more places. So this patch moves the config reading from the middleware and into a `load_memcache` helper method in swift/common/memcached.py. Drive-by: cleanup unused stuff in middleware test module Change-Id: I028722facfbe3ff8092b6bdcc931887a169cc49a --- swift/common/memcached.py | 103 ++++++++- swift/common/middleware/memcache.py | 96 +------- test/unit/common/middleware/test_memcache.py | 214 +----------------- test/unit/common/test_memcached.py | 226 +++++++++++++++++++ 4 files changed, 332 insertions(+), 307 deletions(-) diff --git a/swift/common/memcached.py b/swift/common/memcached.py index 199e73066c..6a953852c6 100644 --- a/swift/common/memcached.py +++ b/swift/common/memcached.py @@ -44,18 +44,20 @@ version is at: http://github.com/memcached/memcached/blob/1.4.2/doc/protocol.txt """ +import os import six import json import logging import time from bisect import bisect -from eventlet.green import socket +from eventlet.green import socket, ssl from eventlet.pools import Pool from eventlet import Timeout from six.moves import range +from six.moves.configparser import ConfigParser, NoSectionError, NoOptionError 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 @@ -204,6 +206,10 @@ class MemcacheRing(object): self.logger = logger 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', sock=None, fp=None, got_connection=True): if isinstance(e, Timeout): @@ -554,3 +560,96 @@ class MemcacheRing(object): return values except (Exception, Timeout) as e: 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) diff --git a/swift/common/middleware/memcache.py b/swift/common/middleware/memcache.py index 562b0b9d88..1bb142657d 100644 --- a/swift/common/middleware/memcache.py +++ b/swift/common/middleware/memcache.py @@ -13,15 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os - -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 +from swift.common.memcached import load_memcache +from swift.common.utils import get_logger class MemcacheMiddleware(object): @@ -32,90 +25,7 @@ class MemcacheMiddleware(object): def __init__(self, app, conf): self.app = app self.logger = get_logger(conf, log_route='memcache') - self.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 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) + self.memcache = load_memcache(conf, self.logger) def __call__(self, env, start_response): env['swift.cache'] = self.memcache diff --git a/test/unit/common/middleware/test_memcache.py b/test/unit/common/middleware/test_memcache.py index 8ba0c7e150..c74ac32ce8 100644 --- a/test/unit/common/middleware/test_memcache.py +++ b/test/unit/common/middleware/test_memcache.py @@ -19,7 +19,6 @@ import unittest from eventlet.green import ssl import mock -from six.moves.configparser import NoSectionError, NoOptionError from swift.common.middleware import memcache from swift.common.memcached import MemcacheRing @@ -34,63 +33,6 @@ class FakeApp(object): 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): pass @@ -106,165 +48,13 @@ class TestCacheMiddleware(unittest.TestCase): self.assertTrue('swift.cache' in resp) 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): factory = memcache.filter_factory({'max_connections': '3'}, memcache_servers='10.10.10.10:10') thefilter = factory('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( thefilter.memcache._client_cache['10.10.10.10:10'].max_size, 3) diff --git a/test/unit/common/test_memcached.py b/test/unit/common/test_memcached.py index 42c3cab837..75786d8632 100644 --- a/test/unit/common/test_memcached.py +++ b/test/unit/common/test_memcached.py @@ -27,9 +27,11 @@ from uuid import uuid4 import os import mock +from six.moves.configparser import NoSectionError, NoOptionError from eventlet import GreenPool, sleep, Queue from eventlet.pools import Pool +from eventlet.green import ssl from swift.common import memcached from swift.common.memcached import MemcacheConnectionError @@ -1047,5 +1049,229 @@ class TestMemcached(unittest.TestCase): 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__': unittest.main()