Merge pull request #4 from echohead/key-types
Allow printing of metadata values with '--key KEY --type TYPE'
This commit is contained in:
commit
84a54e7558
|
@ -0,0 +1,2 @@
|
|||
class ConfigException(Exception):
|
||||
pass
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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 )
|
||||
|
||||
|
|
|
@ -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")
|
Loading…
Reference in New Issue