Merge pull request #4 from echohead/key-types

Allow printing of metadata values with '--key KEY --type TYPE'
This commit is contained in:
rbtcollins 2013-02-28 13:44:30 -08:00
commit 84a54e7558
5 changed files with 89 additions and 9 deletions

View File

@ -0,0 +1,2 @@
class ConfigException(Exception):
pass

View File

@ -8,6 +8,8 @@ import tempfile
from argparse import ArgumentParser
from pystache.context import KeyNotFoundError
from subprocess import Popen, PIPE
from value_types import *
from config_exception import *
TEMPLATES_DIR = os.environ.get('OS_CONFIG_APPLIER_TEMPLATES',
'/opt/stack/os-config-applier/templates')
@ -19,6 +21,18 @@ def install_config(config_path, template_root, output_path, validate, subhash=No
for path, contents in tree.items():
write_file( os.path.join(output_path, strip_prefix('/', path)), contents)
def print_key(config_path, key, type_name):
config = read_config(config_path)
keys = key.split('.')
for key in keys:
try:
config = config[key]
except KeyError:
raise KeyError('key %s does not exist in %s' % (key, config_path))
ensure_type(config, type_name)
print config
def write_file(path, contents):
logger.info("writing %s", path)
d = os.path.dirname(path)
@ -101,6 +115,10 @@ def parse_opts(argv):
help='Print templates root and exit.')
parser.add_argument('-s', '--subhash',
help='use the sub-hash named by this key, instead of the full metadata hash')
parser.add_argument('--key', metavar='KEY', default=None,
help='print the specified key and exit. (may be used with --type)')
parser.add_argument('--type', default='default',
help='exit with error if the specified --key does not match type. Valid types are <int|default|raw>')
opts = parser.parse_args(argv[1:])
return opts
@ -114,18 +132,19 @@ def main(argv=sys.argv):
try:
if opts.templates is None:
raise ConfigException('missing option --templates')
if not os.access(opts.output, os.W_OK):
raise ConfigException("you don't have permission to write to '%s'" % opts.output)
install_config(opts.metadata, opts.templates, opts.output,
opts.validate, opts.subhash)
logger.info("success")
if opts.key:
print_key(opts.metadata, opts.key, opts.type)
else:
if not os.access(opts.output, os.W_OK):
raise ConfigException("you don't have permission to write to '%s'" % opts.output)
install_config(opts.metadata, opts.templates, opts.output,
opts.validate)
logger.info("success")
except ConfigException as e:
logger.error(e)
sys.exit(1)
class ConfigException(Exception):
pass
sys.exit(0)
# logginig
LOG_FORMAT = '[%(asctime)s] [%(levelname)s] %(message)s'

View File

@ -0,0 +1,15 @@
import re
from config_exception import ConfigException
TYPES = {
"int": "^[0-9]+$",
"default": "^[A-Za-z0-9]+$",
"raw": "."
}
def ensure_type(string_value, type_name='default'):
if type_name not in TYPES:
raise ValueError("requested validation of unknown type: %s" % type_name)
if not re.match(TYPES[type_name], string_value):
raise ConfigException("cannot interpret value '%s' as type %s" % (string_value, type_name))
return string_value

View File

@ -1,8 +1,10 @@
import json
import os
import subprocess
import tempfile
from StringIO import StringIO
from nose.tools import *
from os_config_applier.config_exception import *
from os_config_applier.os_config_applier import *
# example template tree
@ -32,10 +34,12 @@ def setup():
def teardown():
pass
def main_path():
return os.path.dirname(os.path.realpath(__file__)) + '/../os_config_applier/os_config_applier.py'
def template(relpath):
return os.path.join(TEMPLATES, relpath[1:])
def test_install_config():
t = tempfile.NamedTemporaryFile()
t.write(json.dumps(CONFIG))
@ -47,6 +51,28 @@ def test_install_config():
assert os.path.exists(full_path)
assert_equal( open(full_path).read(), contents )
def test_print_key():
t = tempfile.NamedTemporaryFile()
t.write(json.dumps(CONFIG))
t.flush()
out = subprocess.check_output([main_path(), '--metadata', t.name, '--key', 'database.url', '--type', 'raw'])
assert_equals(CONFIG['database']['url'], out.rstrip())
@raises(subprocess.CalledProcessError)
def test_print_key_missing():
t = tempfile.NamedTemporaryFile()
t.write(json.dumps(CONFIG))
t.flush()
out = subprocess.check_output([main_path(), '--metadata', t.name, '--key', 'does.not.exist'])
@raises(subprocess.CalledProcessError)
def test_print_key_wrong_type():
t = tempfile.NamedTemporaryFile()
t.write(json.dumps(CONFIG))
t.flush()
out = subprocess.check_output([main_path(), '--metadata', t.name, '--key', 'x', '--type', 'int'])
def test_build_tree():
assert_equals( build_tree(template_paths(TEMPLATES), CONFIG), OUTPUT )

18
tests/value_type_tests.py Normal file
View File

@ -0,0 +1,18 @@
from nose.tools import *
from os_config_applier.os_config_applier import *
from os_config_applier.config_exception import *
from os_config_applier.value_types import *
@raises(ValueError)
def test_unknown_type():
ensure_type("foo", "badtype")
def test_int():
assert_equals("123", ensure_type("123", "int"))
def test_defualt():
assert_equals("foobar", ensure_type("foobar", "default"))
@raises(ConfigException)
def test_default_bad():
ensure_type("foo\nbar", "default")