Implement unit_test config to disable syslog.

bug 701248
bug 819303

This change makes syslog optional, by providing a unit test
config with the ability to cause test/unit/__init__.py to
replace SysLogHandler with a fake logging class. The
default behavior is unchanged.

FakeLogger now inherits directly from Handler and mocks out
its API - this was a backward-compatibility issue.

Change-Id: I653dec148c4f6cf81759de03964c6a3290c1a290
This commit is contained in:
Dan Dillinger 2012-04-30 16:38:15 -04:00
parent e22ee457f0
commit f7fdb9cf12
6 changed files with 101 additions and 16 deletions

View File

@ -268,7 +268,7 @@ Do these commands as you on guest.
`cd ~/swift; sudo python setup.py develop` `cd ~/swift; sudo python setup.py develop`
#. Edit `~/.bashrc` and add to the end:: #. Edit `~/.bashrc` and add to the end::
export SWIFT_TEST_CONFIG_FILE=/etc/swift/func_test.conf export SWIFT_TEST_CONFIG_FILE=/etc/swift/test.conf
export PATH=${PATH}:~/bin export PATH=${PATH}:~/bin
#. `. ~/.bashrc` #. `. ~/.bashrc`
@ -658,7 +658,7 @@ Setting up scripts for running Swift
#. Get an `X-Storage-Url` and `X-Auth-Token`: ``curl -v -H 'X-Storage-User: test:tester' -H 'X-Storage-Pass: testing' http://127.0.0.1:8080/auth/v1.0`` #. Get an `X-Storage-Url` and `X-Auth-Token`: ``curl -v -H 'X-Storage-User: test:tester' -H 'X-Storage-Pass: testing' http://127.0.0.1:8080/auth/v1.0``
#. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>`` #. Check that you can GET account: ``curl -v -H 'X-Auth-Token: <token-from-x-auth-token-above>' <url-from-x-storage-url-above>``
#. Check that `swift` works: `swift -A http://127.0.0.1:8080/auth/v1.0 -U test:tester -K testing stat` #. Check that `swift` works: `swift -A http://127.0.0.1:8080/auth/v1.0 -U test:tester -K testing stat`
#. `cp ~/swift/test/functional/sample.conf /etc/swift/func_test.conf` #. `cp ~/swift/test/sample.conf /etc/swift/test.conf`
#. `cd ~/swift; ./.functests` (Note: functional tests will first delete #. `cd ~/swift; ./.functests` (Note: functional tests will first delete
everything in the configured accounts.) everything in the configured accounts.)
#. `cd ~/swift; ./.probetests` (Note: probe tests will reset your #. `cd ~/swift; ./.probetests` (Note: probe tests will reset your
@ -691,3 +691,8 @@ If all doesn't go as planned, and tests fail, or you can't auth, or something do
`swift-object-server /etc/swift/object-server/1.conf` will start the `swift-object-server /etc/swift/object-server/1.conf` will start the
object server. If there are problems not showing up in syslog, object server. If there are problems not showing up in syslog,
then you will likely see the traceback on startup. then you will likely see the traceback on startup.
#. If you need to, you can turn off syslog for unit tests. This can be
useful for environments where /dev/log is unavailable, or which
cannot rate limit (unit tests generate a lot of logs very quickly).
Open the file SWIFT_TEST_CONFIG_FILE points to, and change the
value of fake_syslog to True.

View File

@ -18,19 +18,32 @@ import logging
logging.raiseExceptions = False logging.raiseExceptions = False
def get_config(): def get_config(section_name=None, defaults=None):
""" """
Attempt to get a functional config dictionary. Attempt to get a test config dictionary.
:param section_name: the section to read (all sections if not defined)
:param defaults: an optional dictionary namespace of defaults
""" """
config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE', config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE',
'/etc/swift/func_test.conf') '/etc/swift/test.conf')
config = {} config = {}
if defaults is not None:
config.update(defaults)
try: try:
try: config = readconf(config_file, section_name)
config = readconf(config_file, 'func_test')
except MissingSectionHeaderError:
config_fp = StringIO('[func_test]\n' + open(config_file).read())
config = readconf(config_fp, 'func_test')
except SystemExit: except SystemExit:
print >>sys.stderr, 'UNABLE TO READ FUNCTIONAL TESTS CONFIG FILE' if not os.path.exists(config_file):
print >>sys.stderr, \
'Unable to read test config %s - file not found' \
% config_file
elif not os.access(config_file, os.R_OK):
print >>sys.stderr, \
'Unable to read test config %s - permission denied' \
% config_file
else:
print >>sys.stderr, \
'Unable to read test config %s - section %s not found' \
% (config_file, section_name)
return config return config

View File

@ -26,7 +26,7 @@ import unittest
from test import get_config from test import get_config
from test.functional.swift import Account, Connection, File, ResponseError from test.functional.swift import Account, Connection, File, ResponseError
config = get_config() config = get_config('func_test')
locale.setlocale(locale.LC_COLLATE, config.get('collate', 'C')) locale.setlocale(locale.LC_COLLATE, config.get('collate', 'C'))

View File

@ -11,7 +11,7 @@ from test import get_config
from swift.common.client import get_auth, http_connection from swift.common.client import get_auth, http_connection
conf = get_config() conf = get_config('func_test')
# If no conf was read, we will fall back to old school env vars # If no conf was read, we will fall back to old school env vars
swift_test_auth = os.environ.get('SWIFT_TEST_AUTH') swift_test_auth = os.environ.get('SWIFT_TEST_AUTH')

View File

@ -20,3 +20,6 @@ username3 = tester3
password3 = testing3 password3 = testing3
collate = C collate = C
[unit_test]
fake_syslog = False

View File

@ -1,11 +1,19 @@
""" Swift tests """ """ Swift tests """
import sys
import os import os
import copy
from contextlib import contextmanager from contextlib import contextmanager
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from eventlet.green import socket from eventlet.green import socket
from tempfile import mkdtemp from tempfile import mkdtemp
from shutil import rmtree from shutil import rmtree
from test import get_config
from ConfigParser import MissingSectionHeaderError
from StringIO import StringIO
from swift.common.utils import readconf, TRUE_VALUES
from logging import Handler
import logging.handlers
def readuntil2crlfs(fd): def readuntil2crlfs(fd):
@ -91,11 +99,14 @@ def temptree(files, contents=''):
rmtree(tempdir) rmtree(tempdir)
class FakeLogger(object): class FakeLogger(Handler):
# a thread safe logger # a thread safe logger
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.log_dict = dict(error=[], info=[], warning=[], debug=[]) self.log_dict = dict(error=[], info=[], warning=[], debug=[])
self.level = logging.NOTSET
if 'facility' in kwargs:
self.facility = kwargs['facility']
def error(self, *args, **kwargs): def error(self, *args, **kwargs):
self.log_dict['error'].append((args, kwargs)) self.log_dict['error'].append((args, kwargs))
@ -109,12 +120,61 @@ class FakeLogger(object):
def debug(self, *args, **kwargs): def debug(self, *args, **kwargs):
self.log_dict['debug'].append((args, kwargs)) self.log_dict['debug'].append((args, kwargs))
def setFormatter(self, obj):
self.formatter = obj
def close(self):
self.log_dict = dict(error=[], info=[], warning=[], debug=[])
def set_name(self, name):
# don't touch _handlers
self._name = name
def acquire(self):
pass
def release(self):
pass
def createLock(self):
pass
def emit(self, record):
pass
def handle(self, record):
pass
def flush(self):
pass
def handleError(self, record):
pass
original_syslog_handler = logging.handlers.SysLogHandler
def fake_syslog_handler():
for attr in dir(original_syslog_handler):
if attr.startswith('LOG'):
setattr(FakeLogger, attr,
copy.copy(getattr(logging.handlers.SysLogHandler, attr)))
FakeLogger.priority_map = \
copy.deepcopy(logging.handlers.SysLogHandler.priority_map)
logging.handlers.SysLogHandler = FakeLogger
if get_config('unit_test').get('fake_syslog', 'False').lower() in TRUE_VALUES:
fake_syslog_handler()
class MockTrue(object): class MockTrue(object):
""" """
Instances of MockTrue evaluate like True Instances of MockTrue evaluate like True
Any attr accessed on an instance of MockTrue will return a MockTrue instance Any attr accessed on an instance of MockTrue will return a MockTrue
Any method called on an instance of MockTrue will return a MockTrue instance instance. Any method called on an instance of MockTrue will return
a MockTrue instance.
>>> thing = MockTrue() >>> thing = MockTrue()
>>> thing >>> thing
@ -140,11 +200,15 @@ class MockTrue(object):
def __getattribute__(self, *args, **kwargs): def __getattribute__(self, *args, **kwargs):
return self return self
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return self return self
def __repr__(*args, **kwargs): def __repr__(*args, **kwargs):
return repr(True) return repr(True)
def __eq__(self, other): def __eq__(self, other):
return other is True return other is True
def __ne__(self, other): def __ne__(self, other):
return other is not True return other is not True