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:
@@ -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()))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[marathon]
|
||||
port = 8080
|
||||
host = "localhost"
|
||||
port = 8080
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user