graphite-dev team mailing list archive
-
graphite-dev team
-
Mailing list archive
-
Message #01019
[Merge] lp:~sidnei/graphite/hardcoded-conf-dir into lp:graphite
Sidnei da Silva has proposed merging lp:~sidnei/graphite/hardcoded-conf-dir into lp:graphite.
Requested reviews:
graphite-dev (graphite-dev)
Related bugs:
Bug #797845 in Graphite: "Hardcoded path for storage-schemas.conf"
https://bugs.launchpad.net/graphite/+bug/797845
For more details, see:
https://code.launchpad.net/~sidnei/graphite/hardcoded-conf-dir/+merge/64769
Refactors some of the config parsing logic out into the conf module, and adds some tests for it. Also took the opportunity to cleanup the carbon scripts a bit, though there's a lot more that could be done there (like Bug #797848 for example).
--
https://code.launchpad.net/~sidnei/graphite/hardcoded-conf-dir/+merge/64769
Your team graphite-dev is requested to review the proposed merge of lp:~sidnei/graphite/hardcoded-conf-dir into lp:graphite.
=== modified file '.bzrignore'
--- .bzrignore 2009-09-14 14:35:44 +0000
+++ .bzrignore 2011-06-15 22:55:39 +0000
@@ -1,1 +1,2 @@
MANIFEST
+_trial_temp
=== modified file 'carbon/bin/carbon-aggregator.py'
--- carbon/bin/carbon-aggregator.py 2011-04-05 21:42:51 +0000
+++ carbon/bin/carbon-aggregator.py 2011-06-15 22:55:39 +0000
@@ -1,18 +1,17 @@
#!/usr/bin/env python
import sys
import os
-import optparse
import atexit
from os.path import basename, dirname, exists, join, isdir
-__builtins__.program = basename( sys.argv[0] ).split('.')[0]
+program = basename( sys.argv[0] ).split('.')[0]
# Initialize twisted
try:
from twisted.internet import epollreactor
epollreactor.install()
-except:
+except:
pass
from twisted.internet import reactor
@@ -20,45 +19,61 @@
# Figure out where we're installed
BIN_DIR = dirname( os.path.abspath(__file__) )
ROOT_DIR = dirname(BIN_DIR)
-STORAGE_DIR = join(ROOT_DIR, 'storage')
-LOG_DIR = join(STORAGE_DIR, 'log', 'carbon-aggregator')
LIB_DIR = join(ROOT_DIR, 'lib')
-CONF_DIR = join(ROOT_DIR, 'conf')
sys.path.insert(0, LIB_DIR)
os.environ['GRAPHITE_ROOT'] = ROOT_DIR
-# Parse command line options
-parser = optparse.OptionParser(usage='%prog [options] <start|stop|status>')
-parser.add_option('--debug', action='store_true', help='Run in the foreground, log to stdout')
-parser.add_option('--profile', help='Record performance profile data to the given file')
-parser.add_option('--pidfile', default=join(STORAGE_DIR, '%s.pid' % program), help='Write pid to the given file')
-parser.add_option('--config', default=join(CONF_DIR, 'carbon.conf'), help='Use the given config file')
-parser.add_option('--rules', default=join(CONF_DIR, 'aggregation-rules.conf'), help='Use the give aggregation rules file')
-parser.add_option('--logdir', default=LOG_DIR, help="Write logs in the given directory")
-
-(options, args) = parser.parse_args()
-
-if not args:
- parser.print_usage()
- raise SystemExit(1)
-
+# Capture useful debug info for this commonly reported problem
+try:
+ import carbon
+except ImportError:
+ print 'Failed to import carbon, debug information follows.'
+ print 'pwd=%s' % os.getcwd()
+ print 'sys.path=%s' % sys.path
+ print '__file__=%s' % __file__
+ sys.exit(1)
+
+
+# Read config (we want failures to occur before daemonizing)
+from carbon.conf import (get_default_parser, parse_options,
+ read_config, settings as global_settings)
+
+
+parser = get_default_parser()
+parser.add_option(
+ '--rules',
+ default=None,
+ help='Use the give aggregation rules file')
+
+(options, args) = parse_options(parser)
+settings = read_config(program, options, ROOT_DIR=ROOT_DIR)
+global_settings.update(settings)
+
+if options.rules is None:
+ options.rules = join(settings.CONF_DIR, "aggregation-rules.conf")
+
+pidfile = settings.pidfile
+logdir = settings.LOG_DIR
+
+__builtins__.program = program
action = args[0]
+
if action == 'stop':
- if not exists(options.pidfile):
- print 'Pidfile %s does not exist' % options.pidfile
+ if not exists(pidfile):
+ print 'Pidfile %s does not exist' % pidfile
raise SystemExit(0)
- pidfile = open(options.pidfile, 'r')
+ pf = open(pidfile, 'r')
try:
pid = int( pidfile.read().strip() )
except:
- print 'Could not read pidfile %s' % options.pidfile
+ print 'Could not read pidfile %s' % pidfile
raise SystemExit(1)
- print 'Deleting %s (contained pid %d)' % (options.pidfile, pid)
- os.unlink(options.pidfile)
+ print 'Deleting %s (contained pid %d)' % (pidfile, pid)
+ os.unlink(pidfile)
print 'Sending kill signal to pid %d' % pid
os.kill(pid, 15)
@@ -66,15 +81,15 @@
elif action == 'status':
- if not exists(options.pidfile):
+ if not exists(pidfile):
print '%s is not running' % program
raise SystemExit(0)
- pidfile = open(options.pidfile, 'r')
+ pf = open(pidfile, 'r')
try:
pid = int( pidfile.read().strip() )
except:
- print 'Failed to read pid from %s' % options.pidfile
+ print 'Failed to read pid from %s' % pidfile
raise SystemExit(1)
if exists('/proc/%d' % pid):
@@ -84,21 +99,6 @@
print "%s is not running" % program
raise SystemExit(0)
-elif action != 'start':
- parser.print_usage()
- raise SystemExit(1)
-
-
-if exists(options.pidfile):
- print "Pidfile %s already exists, is %s already running?" % (options.pidfile, program)
- raise SystemExit(1)
-
-
-# Read config (we want failures to occur before daemonizing)
-from carbon.conf import settings
-settings.readFrom(options.config, 'aggregator')
-
-
# Import application components
from carbon.log import logToStdout, logToDir
from carbon.instrumentation import startRecording
@@ -112,7 +112,7 @@
RuleManager.read_from(options.rules)
-rewrite_rules_conf = join(CONF_DIR, 'rewrite-rules.conf')
+rewrite_rules_conf = join(settings.CONF_DIR, 'rewrite-rules.conf')
if exists(rewrite_rules_conf):
RewriteRuleManager.read_from(rewrite_rules_conf)
@@ -121,22 +121,22 @@
logToStdout()
else:
- if not isdir(options.logdir):
- os.makedirs(options.logdir)
+ if not isdir(logdir):
+ os.makedirs(logdir)
daemonize()
- pidfile = open(options.pidfile, 'w')
- pidfile.write( str(os.getpid()) )
- pidfile.close()
+ pf = open(pidfile, 'w')
+ pf.write( str(os.getpid()) )
+ pf.close()
def shutdown():
- if os.path.exists(options.pidfile):
- os.unlink(options.pidfile)
+ if os.path.exists(pidfile):
+ os.unlink(pidfile)
atexit.register(shutdown)
- logToDir(options.logdir)
+ logToDir(logdir)
# Configure application components
=== modified file 'carbon/bin/carbon-cache.py'
--- carbon/bin/carbon-cache.py 2011-04-05 21:42:51 +0000
+++ carbon/bin/carbon-cache.py 2011-06-15 22:55:39 +0000
@@ -17,11 +17,9 @@
import os
import socket
import pwd
-import optparse
import atexit
from os.path import basename, dirname, exists, join, isdir
-
program = basename( sys.argv[0] ).split('.')[0]
hostname = socket.gethostname().split('.')[0]
os.umask(022)
@@ -38,14 +36,10 @@
# Figure out where we're installed
BIN_DIR = dirname( os.path.abspath(__file__) )
ROOT_DIR = dirname(BIN_DIR)
-STORAGE_DIR = join(ROOT_DIR, 'storage')
-LOG_DIR = join(STORAGE_DIR, 'log', 'carbon-cache')
LIB_DIR = join(ROOT_DIR, 'lib')
-CONF_DIR = join(ROOT_DIR, 'conf')
-__builtins__.CONF_DIR = CONF_DIR # evil I know, but effective.
-
sys.path.insert(0, LIB_DIR)
+
# Capture useful debug info for this commonly reported problem
try:
import carbon
@@ -57,37 +51,25 @@
sys.exit(1)
-# Parse command line options
-parser = optparse.OptionParser(usage='%prog [options] <start|stop|status>')
-parser.add_option('--debug', action='store_true', help='Run in the foreground, log to stdout')
-parser.add_option('--profile', help='Record performance profile data to the given file')
-parser.add_option('--pidfile', default=join(STORAGE_DIR, '%s.pid' % program), help='Write pid to the given file')
-parser.add_option('--config', default=join(CONF_DIR, 'carbon.conf'), help='Use the given config file')
-parser.add_option('--logdir', default=LOG_DIR, help="Write logs in the given directory")
-parser.add_option('--instance', default=None, help="Manage a specific carbon instnace")
-
-(options, args) = parser.parse_args()
-
-if not args:
- parser.print_usage()
- raise SystemExit(1)
-
-# Assume standard locations when using --instance
-if options.instance:
- instance = str(options.instance)
- pidfile = join(STORAGE_DIR, '%s-%s.pid' % (program, instance))
- logdir = "%s-%s/" % (LOG_DIR.rstrip('/'), instance)
- config = options.config
-else:
- instance = None
- pidfile = options.pidfile
- logdir = options.logdir
- config = options.config
+# Read config (we want failures to occur before daemonizing)
+from carbon.conf import (get_default_parser, parse_options,
+ read_config, settings as global_settings)
+
+
+(options, args) = parse_options(get_default_parser())
+settings = read_config(program, options, ROOT_DIR=ROOT_DIR)
+global_settings.update(settings)
+
+instance = options.instance
+pidfile = settings.pidfile
+logdir = settings.LOG_DIR
+
__builtins__.instance = instance # This isn't as evil as you might think
__builtins__.program = program
action = args[0]
+
if action == 'stop':
if not exists(pidfile):
print 'Pidfile %s does not exist' % pidfile
@@ -127,23 +109,10 @@
print "%s (instance %s) is not running" % (program, instance)
raise SystemExit(0)
-elif action != 'start':
- parser.print_usage()
- raise SystemExit(1)
-
-
if exists(pidfile):
print "Pidfile %s already exists, is %s already running?" % (pidfile, program)
raise SystemExit(1)
-
-# Read config (we want failures to occur before daemonizing)
-from carbon.conf import settings
-settings.readFrom(config, 'cache')
-
-if instance:
- settings.readFrom(config, 'cache:%s' % instance)
-
# Import application components
from carbon.log import logToStdout, logToDir
from carbon.listeners import MetricLineReceiver, MetricPickleReceiver, CacheQueryHandler, startListener
@@ -151,7 +120,7 @@
from carbon.instrumentation import startRecording
from carbon.events import metricReceived
-storage_schemas = join(CONF_DIR, 'storage-schemas.conf')
+storage_schemas = join(settings.CONF_DIR, 'storage-schemas.conf')
if not exists(storage_schemas):
print "Error: missing required config %s" % storage_schemas
sys.exit(1)
=== modified file 'carbon/bin/carbon-relay.py'
--- carbon/bin/carbon-relay.py 2011-04-05 21:42:51 +0000
+++ carbon/bin/carbon-relay.py 2011-06-15 22:55:39 +0000
@@ -15,18 +15,18 @@
import sys
import os
-import optparse
import atexit
from os.path import basename, dirname, exists, join, isdir
+program = basename( sys.argv[0] ).split('.')[0]
+os.umask(022)
-__builtins__.program = basename( sys.argv[0] ).split('.')[0]
# Initialize twisted
try:
from twisted.internet import epollreactor
epollreactor.install()
-except:
+except:
pass
from twisted.internet import reactor
@@ -34,46 +34,60 @@
# Figure out where we're installed
BIN_DIR = dirname(__file__)
ROOT_DIR = dirname(BIN_DIR)
-STORAGE_DIR = join(ROOT_DIR, 'storage')
-LOG_DIR = join(STORAGE_DIR, 'log', 'carbon-relay')
LIB_DIR = join(ROOT_DIR, 'lib')
-CONF_DIR = join(ROOT_DIR, 'conf')
-__builtins__.CONF_DIR = CONF_DIR
-
sys.path.insert(0, LIB_DIR)
-# Parse command line options
-parser = optparse.OptionParser(usage='%prog [options] <start|stop|status>')
-parser.add_option('--debug', action='store_true', help='Run in the foreground, log to stdout')
-parser.add_option('--profile', help='Record performance profile data to the given file')
-parser.add_option('--pidfile', default=join(STORAGE_DIR, '%s.pid' % program), help='Write pid to the given file')
-parser.add_option('--config', default=join(CONF_DIR, 'carbon.conf'), help='Use the given config file')
-parser.add_option('--rules', default=join(CONF_DIR, 'relay-rules.conf'), help='Use the give relay rules file')
-parser.add_option('--logdir', default=LOG_DIR, help="Write logs in the given directory")
-
-(options, args) = parser.parse_args()
-
-if not args:
- parser.print_usage()
- raise SystemExit(1)
-
+# Capture useful debug info for this commonly reported problem
+try:
+ import carbon
+except ImportError:
+ print 'Failed to import carbon, debug information follows.'
+ print 'pwd=%s' % os.getcwd()
+ print 'sys.path=%s' % sys.path
+ print '__file__=%s' % __file__
+ sys.exit(1)
+
+
+# Read config (we want failures to occur before daemonizing)
+from carbon.conf import (get_default_parser, parse_options,
+ read_config, settings as global_settings)
+
+
+parser = get_default_parser()
+parser.add_option(
+ '--rules',
+ default=None,
+ help='Use the given relay rules file')
+
+(options, args) = parse_options(parser)
+settings = read_config(program, options, ROOT_DIR=ROOT_DIR)
+global_settings.update(settings)
+
+if options.rules is None:
+ options.rules = join(settings.CONF_DIR, "relay-rules.conf")
+
+pidfile = settings.pidfile
+logdir = settings.LOG_DIR
+
+__builtins__.program = program
action = args[0]
+
if action == 'stop':
- if not exists(options.pidfile):
- print 'Pidfile %s does not exist' % options.pidfile
+ if not exists(pidfile):
+ print 'Pidfile %s does not exist' % pidfile
raise SystemExit(0)
- pidfile = open(options.pidfile, 'r')
+ pf = open(pidfile, 'r')
try:
- pid = int( pidfile.read().strip() )
+ pid = int( pf.read().strip() )
except:
- print 'Could not read pidfile %s' % options.pidfile
+ print 'Could not read pidfile %s' % pidfile
raise SystemExit(1)
- print 'Deleting %s (contained pid %d)' % (options.pidfile, pid)
- os.unlink(options.pidfile)
+ print 'Deleting %s (contained pid %d)' % (pidfile, pid)
+ os.unlink(pidfile)
print 'Sending kill signal to pid %d' % pid
os.kill(pid, 15)
@@ -81,15 +95,15 @@
elif action == 'status':
- if not exists(options.pidfile):
+ if not exists(pidfile):
print '%s is not running' % program
raise SystemExit(0)
- pidfile = open(options.pidfile, 'r')
+ pf = open(pidfile, 'r')
try:
- pid = int( pidfile.read().strip() )
+ pid = int( pf.read().strip() )
except:
- print 'Failed to read pid from %s' % options.pidfile
+ print 'Failed to read pid from %s' % pidfile
raise SystemExit(1)
if exists('/proc/%d' % pid):
@@ -99,19 +113,9 @@
print "%s is not running" % program
raise SystemExit(0)
-elif action != 'start':
- parser.print_usage()
- raise SystemExit(1)
-
-
-if exists(options.pidfile):
- print "Pidfile %s already exists, is %s already running?" % (options.pidfile, program)
- raise SystemExit(1)
-
-
-# Read config (we want failures to occur before daemonizing)
-from carbon.conf import settings
-settings.readFrom(options.config, 'relay')
+if exists(pidfile):
+ print "Pidfile %s already exists, is %s already running?" % (pidfile, program)
+ raise SystemExit(1)
# Quick validation
if settings.RELAY_METHOD not in ('rules', 'consistent-hashing'):
@@ -131,20 +135,20 @@
if options.debug:
logToStdout()
else:
- if not isdir(options.logdir):
- os.makedirs(options.logdir)
+ if not isdir(logdir):
+ os.makedirs(logdir)
from carbon.util import daemonize
daemonize()
- logToDir(options.logdir)
+ logToDir(logdir)
- pidfile = open(options.pidfile, 'w')
+ pidfile = open(pidfile, 'w')
pidfile.write( str(os.getpid()) )
pidfile.close()
def shutdown():
- if os.path.exists(options.pidfile):
- os.unlink(options.pidfile)
+ if os.path.exists(pidfile):
+ os.unlink(pidfile)
atexit.register(shutdown)
=== modified file 'carbon/lib/carbon/conf.py'
--- carbon/lib/carbon/conf.py 2011-04-05 20:30:50 +0000
+++ carbon/lib/carbon/conf.py 2011-06-15 22:55:39 +0000
@@ -12,6 +12,8 @@
See the License for the specific language governing permissions and
limitations under the License."""
+from os.path import join, dirname, normpath
+from optparse import OptionParser
from ConfigParser import ConfigParser
@@ -113,3 +115,100 @@
settings = Settings()
settings.update(defaults)
+
+
+def get_default_parser(usage="%prog [options] <start|stop|status>"):
+ """Create a parser for command line options."""
+ parser = OptionParser(usage=usage)
+ parser.add_option(
+ "--debug", action="store_true",
+ help="Run in the foreground, log to stdout")
+ parser.add_option(
+ "--profile",
+ help="Record performance profile data to the given file")
+ parser.add_option(
+ "--pidfile", default=None,
+ help="Write pid to the given file")
+ parser.add_option(
+ "--config",
+ default=None,
+ help="Use the given config file")
+ parser.add_option(
+ "--logdir",
+ default=None,
+ help="Write logs in the given directory")
+ parser.add_option(
+ "--instance",
+ default=None,
+ help="Manage a specific carbon instance")
+
+ return parser
+
+
+def parse_options(parser, args):
+ """
+ Parse command line options and print usage message if no arguments were
+ provided for the command.
+ """
+ (options, args) = parser.parse_args(args)
+
+ if not args:
+ parser.print_usage()
+ raise SystemExit(1)
+
+ if args[0] not in ("start", "stop", "status"):
+ parser.print_usage()
+ raise SystemExit(1)
+
+ return options, args
+
+
+def read_config(program, options, **kwargs):
+ """
+ Read settings for 'program' from configuration file specified by
+ 'options.config', with missing values provided by 'defaults'.
+ """
+ settings = Settings()
+ settings.update(defaults)
+
+ # Initialize default values if not set yet.
+ for name, value in kwargs.items():
+ settings.setdefault(name, value)
+
+ # At minimum, 'ROOT_DIR' needs to be set.
+ if settings.get("ROOT_DIR", None) is None:
+ raise ValueError("ROOT_DIR needs to be provided.")
+
+ if options.config is None:
+ raise ValueError("--config needs to be provided.")
+
+ # Default config directory to same directory as the specified carbon
+ # configuration file.
+ settings.setdefault("CONF_DIR", dirname(normpath(options.config)))
+
+ # Other settings default to relative paths from the root directory, for
+ # backwards-compatibility.
+ settings.setdefault("STORAGE_DIR", join(settings.ROOT_DIR, "storage"))
+ settings.setdefault("LOG_DIR", join(settings.STORAGE_DIR, "log", program))
+
+ # Read configuration options from program-specific section.
+ section = program[len("carbon-"):]
+ settings.readFrom(options.config, section)
+
+ # If a specific instance of the program is specified, augment the settings
+ # with the instance-specific settings and provide sane defaults for
+ # optional settings.
+ if options.instance:
+ settings.readFrom(options.config, "%s:%s" % (section, options.instance))
+ settings["pidfile"] = (options.pidfile or
+ join(settings.STORAGE_DIR,
+ "%s-%s.pid" % (program, options.instance)))
+ settings["LOG_DIR"] = (options.logdir or
+ "%s-%s" % (settings.LOG_DIR.rstrip('/'),
+ options.instance))
+ else:
+ settings["pidfile"] = (options.pidfile or
+ join(settings.STORAGE_DIR, '%s.pid' % program))
+ settings["LOG_DIR"] = (options.logdir or settings.LOG_DIR)
+
+ return settings
=== modified file 'carbon/lib/carbon/storage.py'
--- carbon/lib/carbon/storage.py 2011-02-04 06:09:28 +0000
+++ carbon/lib/carbon/storage.py 2011-06-15 22:55:39 +0000
@@ -14,7 +14,7 @@
import os, re
from os.path import join, exists
-from carbon.conf import OrderedConfigParser, Settings, settings
+from carbon.conf import OrderedConfigParser, settings
try:
import cPickle as pickle
@@ -22,8 +22,8 @@
import pickle
-STORAGE_SCHEMAS_CONFIG = join(CONF_DIR, 'storage-schemas.conf')
-STORAGE_LISTS_DIR = join(CONF_DIR, 'lists')
+STORAGE_SCHEMAS_CONFIG = join(settings.CONF_DIR, 'storage-schemas.conf')
+STORAGE_LISTS_DIR = join(settings.CONF_DIR, 'lists')
def getFilesystemPath(metric):
=== added directory 'carbon/lib/carbon/tests'
=== added file 'carbon/lib/carbon/tests/__init__.py'
=== added file 'carbon/lib/carbon/tests/test_conf.py'
--- carbon/lib/carbon/tests/test_conf.py 1970-01-01 00:00:00 +0000
+++ carbon/lib/carbon/tests/test_conf.py 2011-06-15 22:55:39 +0000
@@ -0,0 +1,313 @@
+from os.path import dirname, join
+from unittest import TestCase
+from mocker import MockerTestCase
+from carbon.conf import get_default_parser, parse_options, read_config
+
+
+class FakeParser(object):
+
+ def __init__(self):
+ self.called = []
+
+ def parse_args(self, args):
+ return object(), args
+
+ def print_usage(self):
+ self.called.append("print_usage")
+
+
+class FakeOptions(object):
+
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+
+class DefaultParserTest(TestCase):
+
+ def test_default_parser(self):
+ """Check default parser settings."""
+ parser = get_default_parser()
+ self.assertTrue(parser.has_option("--debug"))
+ self.assertEqual(None, parser.defaults["debug"])
+ self.assertTrue(parser.has_option("--profile"))
+ self.assertEqual(None, parser.defaults["profile"])
+ self.assertTrue(parser.has_option("--pidfile"))
+ self.assertEqual(None, parser.defaults["pidfile"])
+ self.assertTrue(parser.has_option("--config"))
+ self.assertEqual(None, parser.defaults["config"])
+ self.assertTrue(parser.has_option("--logdir"))
+ self.assertEqual(None, parser.defaults["logdir"])
+ self.assertTrue(parser.has_option("--instance"))
+ self.assertEqual(None, parser.defaults["instance"])
+
+
+class ParseOptionsTest(TestCase):
+
+ def test_no_args_prints_usage_and_exit(self):
+ """
+ If no arguments are provided, the usage help will be printed and a
+ SystemExit exception will be raised.
+ """
+ parser = FakeParser()
+ self.assertRaises(SystemExit, parse_options, parser, ())
+ self.assertEqual(["print_usage"], parser.called)
+
+ def test_no_valid_args_prints_usage_and_exit(self):
+ """
+ If an argument which isn't a valid command was provided, 'print_usage'
+ will be called and a SystemExit exception will be raised.
+ """
+ parser = FakeParser()
+ self.assertRaises(SystemExit, parse_options, parser, ("bazinga!",))
+ self.assertEqual(["print_usage"], parser.called)
+
+ def test_valid_args(self):
+ """
+ If a valid argument is provided, it will be returned along with
+ options.
+ """
+ parser = FakeParser()
+ options, args = parser.parse_args(("start",))
+ self.assertEqual(("start",), args)
+
+
+class ReadConfigTest(MockerTestCase):
+
+ def test_root_dir_is_required(self):
+ """
+ At minimum, the caller must provide a 'ROOT_DIR' setting.
+ """
+ try:
+ read_config("carbon-foo", FakeOptions())
+ except ValueError, e:
+ self.assertEqual("ROOT_DIR needs to be provided.", e.message)
+ else:
+ self.fail("Did not raise exception.")
+
+ def test_config_is_required(self):
+ """
+ The 'options' object must have a non-None 'config' attribute.
+ """
+ try:
+ read_config("carbon-foo", FakeOptions(config=None), ROOT_DIR="foo")
+ except ValueError, e:
+ self.assertEqual("--config needs to be provided.", e.message)
+ else:
+ self.fail("Did not raise exception.")
+
+ def test_conf_dir_defaults_to_config_dirname(self):
+ """
+ The 'CONF_DIR' setting defaults to the parent directory of the
+ provided configuration file.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual(dirname(config), settings.CONF_DIR)
+
+ def test_storage_dir_relative_to_root_dir(self):
+ """
+ The 'STORAGE_DIR' setting defaults to the 'storage' directory relative
+ to the 'ROOT_DIR' setting.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual(join("foo", "storage"), settings.STORAGE_DIR)
+
+ def test_log_dir_relative_to_storage_dir(self):
+ """
+ The 'LOG_DIR' setting defaults to a program-specific directory relative
+ to the 'STORAGE_DIR' setting.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual(join("foo", "storage", "log", "carbon-foo"),
+ settings.LOG_DIR)
+
+ def test_log_dir_relative_to_provided_storage_dir(self):
+ """
+ Providing a different 'STORAGE_DIR' in defaults overrides the default
+ of being relative to 'ROOT_DIR'.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo", STORAGE_DIR="bar")
+ self.assertEqual(join("bar", "log", "carbon-foo"),
+ settings.LOG_DIR)
+
+ def test_log_dir_for_instance_relative_to_storage_dir(self):
+ """
+ The 'LOG_DIR' setting defaults to a program-specific directory relative
+ to the 'STORAGE_DIR' setting. In the case of an instance, the instance
+ name is appended to the directory.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance="x",
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual(join("foo", "storage", "log", "carbon-foo-x"),
+ settings.LOG_DIR)
+
+ def test_log_dir_for_instance_relative_to_provided_storage_dir(self):
+ """
+ Providing a different 'STORAGE_DIR' in defaults overrides the default
+ of being relative to 'ROOT_DIR'. In the case of an instance, the
+ instance name is appended to the directory.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance="x",
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo", STORAGE_DIR="bar")
+ self.assertEqual(join("bar", "log", "carbon-foo-x"),
+ settings.LOG_DIR)
+
+ def test_pidfile_relative_to_storage_dir(self):
+ """
+ The 'pidfile' setting defaults to a program-specific filename relative
+ to the 'STORAGE_DIR' setting.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual(join("foo", "storage", "carbon-foo.pid"),
+ settings.pidfile)
+
+ def test_pidfile_in_options_has_precedence(self):
+ """
+ The 'pidfile' option from command line overrides the default setting.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile="foo.pid", logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual("foo.pid", settings.pidfile)
+
+ def test_pidfile_for_instance_in_options_has_precedence(self):
+ """
+ The 'pidfile' option from command line overrides the default setting
+ for the instance, if one is specified.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance="x",
+ pidfile="foo.pid", logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual("foo.pid", settings.pidfile)
+
+ def test_storage_dir_as_provided(self):
+ """
+ Providing a 'STORAGE_DIR' in defaults overrides the root-relative
+ default.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo", STORAGE_DIR="bar")
+ self.assertEqual("bar", settings.STORAGE_DIR)
+
+ def test_log_dir_as_provided(self):
+ """
+ Providing a 'LOG_DIR' in defaults overrides the storage-relative
+ default.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo", STORAGE_DIR="bar", LOG_DIR='baz')
+ self.assertEqual("baz", settings.LOG_DIR)
+
+ def test_log_dir_from_options(self):
+ """
+ Providing a 'LOG_DIR' in the command line overrides the
+ storage-relative default.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir="baz"),
+ ROOT_DIR="foo")
+ self.assertEqual("baz", settings.LOG_DIR)
+
+ def test_log_dir_for_instance_from_options(self):
+ """
+ Providing a 'LOG_DIR' in the command line overrides the
+ storage-relative default for the instance.
+ """
+ config = self.makeFile(content="[foo]")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance="x",
+ pidfile=None, logdir="baz"),
+ ROOT_DIR="foo")
+ self.assertEqual("baz", settings.LOG_DIR)
+
+ def test_storage_dir_from_config(self):
+ """
+ Providing a 'STORAGE_DIR' in the configuration file overrides the
+ root-relative default.
+ """
+ config = self.makeFile(content="[foo]\nSTORAGE_DIR = bar")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual("bar", settings.STORAGE_DIR)
+
+ def test_log_dir_from_config(self):
+ """
+ Providing a 'LOG_DIR' in the configuration file overrides the
+ storage-relative default.
+ """
+ config = self.makeFile(content="[foo]\nLOG_DIR = baz")
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance=None,
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual("baz", settings.LOG_DIR)
+
+ def test_log_dir_from_instance_config(self):
+ """
+ Providing a 'LOG_DIR' for the specific instance in the configuration
+ file overrides the storage-relative default. The actual value will have
+ the instance name appended to it and ends with a forward slash.
+ """
+ config = self.makeFile(
+ content=("[foo]\nLOG_DIR = baz\n"
+ "[foo:x]\nLOG_DIR = boo"))
+ settings = read_config(
+ "carbon-foo",
+ FakeOptions(config=config, instance="x",
+ pidfile=None, logdir=None),
+ ROOT_DIR="foo")
+ self.assertEqual("boo-x", settings.LOG_DIR)
Follow ups