Permalink
Browse files

Merge pull request #222 from olasd/feature/query-config

Add some features to fedmsg-config
  • Loading branch information...
2 parents 0663b22 + 7637986 commit e5a3a1804207ee21945343d14ab9045f8ffc68d8 @ralphbean ralphbean committed Feb 17, 2014
Showing with 177 additions and 14 deletions.
  1. +88 −3 fedmsg/commands/config.py
  2. +17 −11 fedmsg/config.py
  3. +72 −0 fedmsg/tests/test_commands.py
View
@@ -1,5 +1,6 @@
# This file is part of fedmsg.
# Copyright (C) 2012 Red Hat, Inc.
+# Copyright (C) 2014 Nicolas Dandrimont <olasd@debian.org>
#
# fedmsg is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -16,12 +17,96 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors: Ralph Bean <rbean@redhat.com>
+# Nicolas Dandrimont <olasd@debian.org>
#
+
+import argparse
+import sys
+import textwrap
+
import fedmsg.config
import fedmsg.encoding
def config():
- __doc__ = "Simply print out the fedmsg-config as a JSON."
- config = fedmsg.config.load_config([], __doc__)
- print fedmsg.encoding.pretty_dumps(config)
+ __doc__ = """
+ Query or print the parsed fedmsg-config.
+
+ fedmsg-config is a simple utility that prints out the contents of
+ the fully parsed fedmsg config as a JSON dictionary.
+
+ The tool allows you to query a specific configuration key with the
+ --query option. It returns an error code of 1 if the key isn't
+ found.
+
+ In query mode, the configuration key has the following syntax:
+ foo.bar.baz retrieves the value of config["foo"]["bar"]["baz"]
+
+ If the configuration value is an atomic value, it is printed
+ directly. If the value is a list, each item gets printed line by
+ line. Else, the value is printed as a JSON dictionary.
+ """
+
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ prog=sys.argv[0],
+ )
+
+ parser.add_argument(
+ '--config-filename',
+ dest='config_filename',
+ help="Config file to use.",
+ default=None,
+ )
+
+ parser.add_argument(
+ '--disable-defaults',
+ dest='disable_defaults',
+ help="Disable the configuration defaults.",
+ action="store_true",
+ )
+
+ parser.add_argument(
+ '--query',
+ dest='query',
+ help="The key to dump from the given config.",
+ type=str,
+ default=None
+ )
+
+ args = parser.parse_args()
+
+ filenames = None
+ if args.config_filename:
+ filenames = [args.config_filename]
+
+ config = fedmsg.config.load_config(
+ extra_args=[],
+ doc=__doc__,
+ filenames=filenames,
+ disable_defaults=args.disable_defaults,
+ )
+
+ cur = config
+
+ if args.query:
+ keys = args.query.split('.')
+ curpath = []
+ for key in keys:
+ curpath.append(key)
+ if key in cur:
+ cur = cur[key]
+ else:
+ print >>sys.stderr, (
+ "Key `%s` does not exist in config" % ".".join(curpath)
+ )
+ sys.exit(1)
+
+ if isinstance(cur, list):
+ for i in cur:
+ print i
+ elif isinstance(cur, basestring):
+ print cur
+ else:
+ print fedmsg.encoding.pretty_dumps(cur)
View
@@ -101,11 +101,12 @@ def load_config(extra_args=None,
doc=None,
filenames=None,
invalidate_cache=False,
- fedmsg_command=False):
+ fedmsg_command=False,
+ disable_defaults=False):
""" Setup a runtime config dict by integrating the following sources
(ordered by precedence):
- - defaults
+ - defaults (unless disable_defaults = True)
- config file
- command line arguments
@@ -125,7 +126,11 @@ def load_config(extra_args=None,
extra_args = extra_args or []
doc = doc or ""
- config = copy.deepcopy(defaults)
+ if not disable_defaults:
+ config = copy.deepcopy(defaults)
+ else:
+ config = {}
+
config.update(_process_config_file(filenames=filenames))
# This is optional (and defaults to false) so that only 'fedmsg-*' commands
@@ -143,23 +148,24 @@ def load_config(extra_args=None,
filenames=[config['config_filename']])
# Just a little debug option. :)
- if config['print_config']:
+ if config.get('print_config'):
print pretty_dumps(config)
sys.exit(0)
- if config['environment'] not in VALID_ENVIRONMENTS:
+ if config.get('environment', 'prod') not in VALID_ENVIRONMENTS:
raise ValueError("%r not one of %r" % (
config['environment'], VALID_ENVIRONMENTS))
- if 'endpoints' not in config:
+ if not disable_defaults and 'endpoints' not in config:
raise ValueError("No config value 'endpoints' found.")
- config['endpoints'] = dict(map(lambda (k, v): (k, list(iterate(v))),
- config['endpoints'].iteritems()))
-
- if not isinstance(config['endpoints'], dict):
+ if not isinstance(config.get('endpoints', {}), dict):
raise ValueError("The 'endpoint' config value must be a dict.")
+ if 'endpoints' in config:
+ config['endpoints'] = dict(map(lambda (k, v): (k, list(iterate(v))),
+ config['endpoints'].iteritems()))
+
if 'srv_endpoints' in config and len(config['srv_endpoints']) > 0:
from dns.resolver import query, NXDOMAIN, Timeout, NoNameservers
for e in config['srv_endpoints']:
@@ -186,7 +192,7 @@ def load_config(extra_args=None,
))
config['endpoints'][e] = list(iterate(urls))
- if 'topic_prefix_re' not in config:
+ if 'topic_prefix_re' not in config and 'topic_prefix' in config:
# Turn "org.fedoraproject" into "org\.fedoraproject\.(dev|stg|prod)"
config['topic_prefix_re'] = config['topic_prefix'].replace('.', '\.')\
+ '\.(%s)' % '|'.join(VALID_ENVIRONMENTS)
@@ -14,6 +14,7 @@
from fedmsg.commands.logger import LoggerCommand
from fedmsg.commands.tail import TailCommand
from fedmsg.commands.relay import RelayCommand
+from fedmsg.commands.config import config as config_command
import fedmsg.consumers.relay
from nose.tools import eq_
@@ -22,6 +23,8 @@
import six
+CONF_FILE = os.path.join(os.path.dirname(__file__), "fedmsg.d", "ircbot.py")
+
class TestCommands(unittest.TestCase):
def setUp(self):
@@ -184,3 +187,72 @@ def mock_main(options, consumers, framework):
assert(
actual_options[fedmsg.consumers.relay.RelayConsumer.config_key]
)
+
+ @patch("sys.argv", new_callable=lambda: ["fedmsg-config"])
+ @patch("sys.stdout", new_callable=six.StringIO)
+ def test_config_basic(self, stdout, argv):
+ with patch('fedmsg.config.__cache', {}):
+ config_command()
+
+ output = stdout.getvalue()
+ output_conf = json.loads(output)
+
+ with patch('fedmsg.config.__cache', {}):
+ fedmsg_conf = fedmsg.config.load_config()
+
+ eq_(output_conf, fedmsg_conf)
+
+ @patch("sys.argv", new_callable=lambda: [
+ "fedmsg-config", "--query", "endpoints",
+ ])
+ @patch("sys.stdout", new_callable=six.StringIO)
+ def test_config_query(self, stdout, argv):
+ with patch('fedmsg.config.__cache', {}):
+ config_command()
+
+ output = stdout.getvalue()
+ output_conf = json.loads(output)
+
+ with patch('fedmsg.config.__cache', {}):
+ fedmsg_conf = fedmsg.config.load_config()
+
+ eq_(output_conf, fedmsg_conf["endpoints"])
+
+ @patch("sys.argv", new_callable=lambda: [
+ "fedmsg-config", "--query", "endpoints.broken",
+ ])
+ @patch("sys.stdout", new_callable=six.StringIO)
+ @patch("sys.stderr", new_callable=six.StringIO)
+ def test_config_query_broken(self, stderr, stdout, argv):
+ try:
+ with patch('fedmsg.config.__cache', {}):
+ config_command()
+ except SystemExit as exc:
+ eq_(exc.code, 1)
+ else:
+ assert False
+
+ output = stdout.getvalue()
+ error = stderr.getvalue()
+
+ eq_(output.strip(), "")
+ eq_(error.strip(), "Key `endpoints.broken` does not exist in config")
+
+ @patch("sys.argv", new_callable=lambda: [
+ "fedmsg-config", "--disable-defaults", "--config-filename", CONF_FILE,
+ ])
+ @patch("sys.stdout", new_callable=six.StringIO)
+ def test_config_single_file(self, stdout, argv):
+ with patch('fedmsg.config.__cache', {}):
+ config_command()
+
+ output = stdout.getvalue()
+ output_conf = json.loads(output)
+
+ with patch('fedmsg.config.__cache', {}):
+ fedmsg_conf = fedmsg.config.load_config(
+ filenames=[CONF_FILE],
+ disable_defaults=True,
+ )
+
+ eq_(output_conf, fedmsg_conf)

0 comments on commit e5a3a18

Please sign in to comment.