DCOS-104 Basic config functionality

1. Add ability to create mutable and immutable TOML configuration. The
DCOS config cli should be the only one using the mutable version
2. Add ability to list of all the properties in the TOML configuration.

Test: tox
This commit is contained in:
José Armando García Sancio
2015-01-24 12:07:16 +00:00
parent bdcf172423
commit 59ace056a8
4 changed files with 236 additions and 63 deletions

View File

@@ -3,50 +3,162 @@ import collections
import toml
class Toml(collections.MutableMapping):
def mutable_load_from_path(path):
"""Loads a TOML file from the path
"""Class for managing CLI configuration through TOML."""
:param path: Path to the TOML file
:type path: str
:returns: Mutable map for the configuration file
:rtype: MutableToml
"""
with open(path) as config_file:
return MutableToml(toml.loads(config_file.read()))
def load_from_path(path):
"""Loads a TOML file from the path
:param path: Path to the TOML file
:type path: str
:returns: Map for the configuration file
:rtype: Toml
"""
with open(path) as config_file:
return Toml(toml.loads(config_file.read()))
def _get_path(config, path):
"""
:param path: Path to the value. E.g. 'path.to.value'
:type path: str
:returns: Value stored at the given path
:rtype: double, int, str, list or dict
"""
for section in path.split('.'):
config = config[section]
return config
def _iterator(parent, dictionary):
"""
:param parent: Path to the value parameter
:type parent: str
:param dictionary: Value of the key
:type dictionary: collection.Mapping
"""
for key, value in dictionary.items():
new_key = key
if parent is not None:
new_key = "{}.{}".format(parent, key)
if not isinstance(value, collections.Mapping):
yield (new_key, value)
else:
for x in _iterator(new_key, value):
yield x
class Toml(collections.Mapping):
"""Class for getting value from TOML."""
def __init__(self, dictionary):
"""Constructs interface for managing configurations
:param dictionary: Configuration dictionary
"""
:param dictionary: configuration dictionary
:type dictionary: dict
"""
self._dictionary = dictionary
def __getitem__(self, path):
"""Get the value for a path
"""
:param path: Path to the value. E.g. 'path.to.value'
:type path: str
:returns: Value stored at the given path
:rtype: double, int, str, list or dict
"""
config = self._dictionary
for section in path.split('.'):
config = config[section]
if isinstance(config, collections.MutableMapping):
config = _get_path(self._dictionary, path)
if isinstance(config, collections.Mapping):
return Toml(config)
else:
return config
def __iter__(self):
"""Returns iterator
"""
:returns: Dictionary iterator
:rtype: iterator
"""
return iter(self._dictionary)
def __len__(self):
"""Length of the toml configuration
def property_items(self):
"""Iterator for full-path keys and values
:returns: Iterator for pull-path keys and values
:rtype: iterator of tuples
"""
return _iterator(None, self._dictionary)
def __len__(self):
"""
:returns: The length of the dictionary
:rtype: int
"""
return len(self._dictionary)
class MutableToml(collections.MutableMapping):
"""Class for managing CLI configuration through TOML."""
def __init__(self, dictionary):
"""
:param dictionary: configuration dictionary
:type dictionary: dict
"""
self._dictionary = dictionary
def __getitem__(self, path):
"""
:param path: Path to the value. E.g. 'path.to.value'
:type path: str
:returns: Value stored at the given path
:rtype: double, int, str, list or dict
"""
config = _get_path(self._dictionary, path)
if isinstance(config, collections.MutableMapping):
return MutableToml(config)
else:
return config
def __iter__(self):
"""
:returns: Dictionary iterator
:rtype: iterator
"""
return iter(self._dictionary)
def property_items(self):
"""Iterator for full-path keys and values
:returns: Iterator for pull-path keys and values
:rtype: iterator of tuples
"""
return _iterator(None, self._dictionary)
def __len__(self):
"""
:returns: The length of the dictionary
:rtype: int
"""
@@ -54,8 +166,7 @@ class Toml(collections.MutableMapping):
return len(self._dictionary)
def __setitem__(self, path, value):
"""Set the path to a value
"""
:param path: Path to set
:type path: str
:param value: Value to store
@@ -66,13 +177,12 @@ class Toml(collections.MutableMapping):
sections = path.split('.')
for section in sections[:-1]:
config = config[section]
config = config.setdefault(section, {})
config[sections[-1]] = value
def __delitem__(self, path):
"""Delete value stored at path
"""
:param path: Path to delete
:type path: str
"""
@@ -83,16 +193,3 @@ class Toml(collections.MutableMapping):
config = config[section]
del config[sections[-1]]
@classmethod
def load_from_path(class_, path):
"""Loads a TOML file from the path
:param path: Path to the TOML file
:type path: str
:returns: Dictionary for the configuration file
:rtype: Toml
"""
with open(path) as config_file:
return Toml(toml.loads(config_file.read()))

View File

@@ -3,6 +3,7 @@ Usage:
dcos config info
dcos config <name> [<value>]
dcos config --unset <name>
dcos config --list
dcos config --help
Options:
@@ -10,11 +11,12 @@ Options:
--unset Remove property from the config file
"""
import collections
import os
import docopt
import toml
from dcos.api import config, constants
from dcos.api import config, constants, options
def main():
@@ -27,21 +29,33 @@ def main():
print('Get and set DCOS command line options')
elif args['config'] and args['--unset']:
toml_config = config.Toml.load_from_path(config_path)
del toml_config[args['<name>']]
_save_config_file(config_path, toml_config)
toml_config = config.mutable_load_from_path(config_path)
if toml_config.pop(args['<name>'], None):
_save_config_file(config_path, toml_config)
elif args['config'] and args['--list']:
toml_config = config.load_from_path(config_path)
for key, value in toml_config.property_items():
print('{}={}'.format(key, value))
elif args['config'] and args['<value>'] is None:
toml_config = config.Toml.load_from_path(config_path)
print(toml_config[args['<name>']])
toml_config = config.load_from_path(config_path)
value = toml_config.get(args['<name>'])
if value is not None and not isinstance(value, collections.Mapping):
print(value)
else:
return 1
elif args['config']:
toml_config = config.Toml.load_from_path(config_path)
toml_config = config.mutable_load_from_path(config_path)
toml_config[args['<name>']] = args['<value>']
_save_config_file(config_path, toml_config)
else:
print(args)
print(options.make_generic_usage_error(__doc__))
return 1
return 0
def _save_config_file(config_path, toml_config):
@@ -51,6 +65,6 @@ def _save_config_file(config_path, toml_config):
:type config_path: str or unicode
"""
serial = toml.dumps(toml_config)
serial = toml.dumps(toml_config._dictionary)
with open(config_path, 'w') as config_file:
config_file.write(serial)

View File

@@ -1,3 +1,3 @@
[marathon]
port = 8080
host = "localhost"
port = 8080

View File

@@ -4,19 +4,35 @@ from dcos.api import config
@pytest.fixture
def conf():
return config.Toml({
'dcos': {
'user': 'principal',
'mesos_uri': 'zk://localhost/mesos'
},
'package': {
'repo_uri': 'git://localhost/mesosphere/package-repo.git'
}
return config.Toml(_conf())
def test_get_property(conf):
conf['dcos.mesos_uri'] == 'zk://localhost/mesos'
def test_get_partial_property(conf):
conf['dcos'] == config.Toml({
'user': 'group',
'mesos_uri': 'zk://localhost/mesos'
})
def test_unset_property(conf):
expect = config.Toml({
def test_iterator(conf):
assert (sorted(list(conf.property_items())) == [
('dcos.mesos_uri', 'zk://localhost/mesos'),
('dcos.user', 'principal'),
('package.repo_uri', 'git://localhost/mesosphere/package-repo.git'),
])
@pytest.fixture
def mutable_conf():
return config.MutableToml(_conf())
def test_mutable_unset_property(mutable_conf):
expect = config.MutableToml({
'dcos': {
'user': 'principal',
'mesos_uri': 'zk://localhost/mesos'
@@ -24,13 +40,13 @@ def test_unset_property(conf):
'package': {}
})
del conf['package.repo_uri']
del mutable_conf['package.repo_uri']
assert conf == expect
assert mutable_conf == expect
def test_set_property(conf):
expect = config.Toml({
def test_mutable_set_property(mutable_conf):
expect = config.MutableToml({
'dcos': {
'user': 'group',
'mesos_uri': 'zk://localhost/mesos'
@@ -40,10 +56,56 @@ def test_set_property(conf):
}
})
conf['dcos.user'] = 'group'
mutable_conf['dcos.user'] = 'group'
assert conf == expect
assert mutable_conf == expect
def test_get_property(conf):
conf['dcos.mesos_uri'] == 'zk://localhost/mesos'
def test_mutable_test_deep_property(mutable_conf):
expect = config.MutableToml({
'dcos': {
'user': 'principal',
'mesos_uri': 'zk://localhost/mesos'
},
'package': {
'repo_uri': 'git://localhost/mesosphere/package-repo.git'
},
'new': {
'key': 42
},
})
mutable_conf['new.key'] = 42
assert mutable_conf == expect
def test_mutable_get_property(mutable_conf):
mutable_conf['dcos.mesos_uri'] == 'zk://localhost/mesos'
def test_mutable_get_partial_property(mutable_conf):
mutable_conf['dcos'] == config.MutableToml({
'user': 'group',
'mesos_uri': 'zk://localhost/mesos'
})
def test_mutable_iterator(mutable_conf):
assert (sorted(list(mutable_conf.property_items())) == [
('dcos.mesos_uri', 'zk://localhost/mesos'),
('dcos.user', 'principal'),
('package.repo_uri', 'git://localhost/mesosphere/package-repo.git'),
])
def _conf():
return {
'dcos': {
'user': 'principal',
'mesos_uri': 'zk://localhost/mesos'
},
'package': {
'repo_uri': 'git://localhost/mesosphere/package-repo.git'
}
}