...
 
Commits (4)
...@@ -14,11 +14,10 @@ function(CopyFilesFromSource TARGET SOURCE_FILES) ...@@ -14,11 +14,10 @@ function(CopyFilesFromSource TARGET SOURCE_FILES)
if (NOT IN_SOURCE_BUILD) if (NOT IN_SOURCE_BUILD)
add_custom_target(${TARGET} ALL SOURCES ${SOURCE_FILES}) add_custom_target(${TARGET} ALL SOURCES ${SOURCE_FILES})
foreach (infile ${SOURCE_FILES}) foreach (infile ${SOURCE_FILES})
string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} outfile ${infile}) #message("-- staging for copy: ${CMAKE_CURRENT_SOURCE_DIR}/${infile} -> ${CMAKE_CURRENT_BINARY_DIR}/${infile}")
#message("-- staging for copy: ${CMAKE_CURRENT_SOURCE_DIR}/${infile} -> ${CMAKE_CURRENT_BINARY_DIR}/${outfile}")
add_custom_command( add_custom_command(
TARGET ${TARGET} TARGET ${TARGET}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${infile} ${outfile} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${infile} ${infile}
VERBATIM VERBATIM
) )
endforeach() endforeach()
......
...@@ -16,6 +16,8 @@ foreach (infile ${INIT_FILES}) ...@@ -16,6 +16,8 @@ foreach (infile ${INIT_FILES})
endforeach (infile) endforeach (infile)
CopyFilesFromSource(augeas_lens "agocontrol.aug")
add_subdirectory (conf.d) add_subdirectory (conf.d)
add_subdirectory (schema.d) add_subdirectory (schema.d)
add_subdirectory (sysvinit) add_subdirectory (sysvinit)
......
cmake_minimum_required (VERSION 3.0) cmake_minimum_required (VERSION 3.0)
file (GLOB CONFD_FILES *.in) file (GLOB CONFD_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.in)
foreach (infile ${CONFD_FILES}) foreach (infile ${CONFD_FILES})
string(REGEX REPLACE ".in$" "" outfile ${infile}) string(REGEX REPLACE ".in$" "" outfile ${infile})
string(REGEX REPLACE ".*/" "" outfile ${outfile}) string(REGEX REPLACE ".*/" "" outfile ${outfile})
configure_file( configure_file(
"${infile}" "${infile}"
"${CMAKE_CURRENT_BINARY_DIR}/${outfile}" "${CMAKE_CURRENT_BINARY_DIR}/${outfile}"
@ONLY @ONLY
) )
LIST(APPEND CONFD_FILES_DONE ${CMAKE_CURRENT_BINARY_DIR}/${outfile}) LIST(APPEND CONFD_FILES_DONE ${outfile})
endforeach (infile) endforeach (infile)
file (GLOB CONFD_FILES *.conf) file (GLOB CONFD_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.conf)
install (FILES ${CONFD_FILES_DONE} ${CONFD_FILES} DESTINATION ${CONFDIR}/conf.d)
CopyFilesFromSource(conf_files "${CONFD_FILES}")
InstallFiles(${CONFDIR}/conf.d "${CONFD_FILES_DONE};${CONFD_FILES}")
...@@ -3,6 +3,7 @@ from __future__ import print_function ...@@ -3,6 +3,7 @@ from __future__ import print_function
import agoclient._logging import agoclient._logging
import argparse import argparse
from . import config from . import config
import logging import logging
import os.path import os.path
...@@ -424,6 +425,29 @@ class AgoApp: ...@@ -424,6 +425,29 @@ class AgoApp:
return config.get_config_option(section, option, default_value, app) return config.get_config_option(section, option, default_value, app)
def get_config_section(self, section=None, app=None):
"""Read all options in the given section(s) from the configuration subsystem.
Same as get_config_option, but will return a dict with all defined values under the given
section(s). If more than one section/app is defined, all will be looked at, in the order
specified. If an option is set in multiple sections/app, the last one seen will be used.
Returns a dict with key/values.
"""
if section is None:
section = self.app_short_name
if app is None:
if type(section) == str:
app = [self.app_short_name, section]
else:
app = [self.app_short_name] + section
config._iterable_replace_none(section, self.app_short_name)
config._iterable_replace_none(app, self.app_short_name)
return config.get_config_section(section, app)
def set_config_option(self, option, value, section=None, app=None): def set_config_option(self, option, value, section=None, app=None):
"""Write a config option to the configuration subsystem. """Write a config option to the configuration subsystem.
......
...@@ -20,7 +20,6 @@ try: ...@@ -20,7 +20,6 @@ try:
HAS_MQTT = True HAS_MQTT = True
except ImportError: except ImportError:
HAS_MQTT = False HAS_MQTT = False
raise
class AgoConnection: class AgoConnection:
......
...@@ -77,7 +77,6 @@ def _conf_file_path(filename): ...@@ -77,7 +77,6 @@ def _conf_file_path(filename):
def _augeas_path(path, section, option): def _augeas_path(path, section, option):
return "/files%s/%s/%s" % (path, section, option) return "/files%s/%s/%s" % (path, section, option)
return "/files%s/%s/%s" % (path, section, option)
def get_config_option(section, option, default_value=None, app=None): def get_config_option(section, option, default_value=None, app=None):
...@@ -110,7 +109,28 @@ def get_config_option(section, option, default_value=None, app=None): ...@@ -110,7 +109,28 @@ def get_config_option(section, option, default_value=None, app=None):
A unicode object with the value found in the data store, if found. A unicode object with the value found in the data store, if found.
If not found, default_value is passed through unmodified. If not found, default_value is passed through unmodified.
""" """
if not augeas: _augeas_init() value = _get_config_option(section, option, app)
if not value:
return default_value
return value
def get_config_section(section, app=None):
"""Read all options in the given section(s) from the configuration subsystem.
Same as get_config_option, but will return a dict with all defined values under the given
section(s). If more than one section/app is defined, all will be looked at, in the order
specified. If an option is set in multiple sections/app, the last one seen will be used.
Returns a dict with key/values.
"""
return _get_config_option(section, None, app)
def _get_config_option(section, option=None, app=None):
if not augeas:
_augeas_init()
if type(section) == str: if type(section) == str:
section = (section,) section = (section,)
...@@ -120,25 +140,47 @@ def get_config_option(section, option, default_value=None, app=None): ...@@ -120,25 +140,47 @@ def get_config_option(section, option, default_value=None, app=None):
elif type(app) == str: elif type(app) == str:
app = (app,) app = (app,)
CONFIG_LOCK.acquire(True) results = {} # Only used for multi-match
try:
with CONFIG_LOCK:
for fn in app: for fn in app:
for s in section: path = _conf_file_path(fn)
path = _conf_file_path(fn)
aug_path = _augeas_path(path, s, option)
try:
value = augeas.get(aug_path)
if value:
# First match
return value
except ValueError as e:
logging.error("Failed to read configuration from %s: %s",
aug_path, e)
finally:
CONFIG_LOCK.release()
# Fall back on default value for s in section:
return default_value if option:
try:
aug_path = _augeas_path(path, s, option)
value = augeas.get(aug_path)
if value:
return value
except ValueError as e:
logging.error("Failed to read configuration from %s: %s", aug_path, e)
else:
# list find all options in section
base_path = _augeas_path(path, s, '')
try:
matches = augeas.match(base_path + '*')
# match returns a list of full paths; figure out each key and put in dict
for path in matches:
key = path[len(base_path):]
# First found takes precedence, just as get_config_option behaves.
if key[0] == '#' or key in results:
continue
try:
value = augeas.get(path)
results[key] = value
except ValueError as e:
logging.error("Failed to read configuration from %s: %s", path, e)
except ValueError as e:
logging.error("Failed to read configuration from %s: %s", aug_path, e)
if not option:
return results
return None
def set_config_option(section, option, value, app=None): def set_config_option(section, option, value, app=None):
......
import unittest
import agoclient.config as config
from agoclient.agoapp import AgoApp
import tempfile
import os, os.path, shutil
# Find checkedout code dir:
# We're in subdir shared/agoclient/
devdir = os.path.realpath(os.path.dirname(os.path.realpath(__file__)) + "/../..")
class AgoTest(AgoApp):
pass
class ConfigTestBase(unittest.TestCase):
def setUp(self):
# Create dummy conf
self.tempdir = tempfile.mkdtemp()
os.mkdir("%s/conf.d" % self.tempdir)
# Custom dir, we must place agocontrol.aug into this dir
# TODO: or add some dir in loadpath?
shutil.copyfile("%s/conf/agocontrol.aug" % devdir, "%s/agocontrol.aug" % self.tempdir)
shutil.copyfile("%s/conf/conf.d/system.conf" % devdir, "%s/conf.d/system.conf" % self.tempdir)
config.set_config_dir(self.tempdir)
with open(config.get_config_path('conf.d/test.conf'), 'w') as f:
f.write("[test]\n"+
"test_value_0=100\n"+
"test_value_blank=\n"+
"local=4\n"+
"units=inv\n"+
"[system]\n"+
"password=testpwd\n"+
"[sec2]\n"+
"some=value\n"+
"test_value_blank=not blank\n")
# Kill augeas
config.augeas = None
def tearDown(self):
shutil.rmtree(self.tempdir)
class ConfigTest(ConfigTestBase):
def setUp(self):
super(ConfigTest, self).setUp()
global gco, sco
gco = config.get_config_option
sco = config.set_config_option
def test_get_path(self):
self.assertEqual(config.get_config_path("conf.d"), "%s/conf.d" % self.tempdir)
self.assertEqual(config.get_config_path("conf.d/"), "%s/conf.d/" % self.tempdir)
self.assertEqual(config.get_config_path("/conf.d/"), "%s/conf.d/" % self.tempdir)
def test_get_basic(self):
self.assertEqual(gco('system', 'broker'), 'localhost')
self.assertEqual(gco('system', 'not_set'), None)
self.assertEqual(gco('test', 'test_value_blank'), None)
self.assertEqual(gco('test', 'local'), '4')
# Test with defaults
self.assertEqual(gco('system', 'broker', 'def'), 'localhost')
self.assertEqual(gco('system', 'not_set', 'def'), 'def')
def test_get_multiapp(self):
self.assertEqual(gco('system', 'broker', app="test"), None)
self.assertEqual(gco('system', 'broker', app=["test", "system"]), 'localhost')
self.assertEqual(gco('system', 'password', app=["test", "system"]), 'testpwd')
def test_get_multisection(self):
# Must explicitly tell app=test, with differnet section name
self.assertEqual(gco('sec2', 'some', app='test'), 'value')
self.assertEqual(gco(['sec2', 'test'], 'test_value_blank', app='test'), 'not blank')
self.assertEqual(gco(['system'], 'units'), 'SI')
self.assertEqual(gco(['test', 'system'], 'units'), 'inv')
def test_set_basic(self):
self.assertEqual(sco('system', 'new_value', '666'), True)
self.assertEqual(gco('system', 'new_value'), '666')
self.assertEqual(sco('system', 'test_string', 'goodbye'), True)
self.assertEqual(gco('system', 'test_string'), 'goodbye')
def test_set_new(self):
self.assertFalse(os.path.exists(config.get_config_path("conf.d/newfile.conf")))
self.assertEqual(sco('newfile', 'new_value', '666'), True)
self.assertTrue(os.path.exists(config.get_config_path("conf.d/newfile.conf")))
self.assertEqual(gco('newfile', 'new_value'), '666')
def test_unwritable(self):
if os.getuid() == 0:
# TODO: Fix builders so they dont run as root, then change to self.fail instead.
#self.fail("You cannot run this test as. Also, do not develop as root!")
print("You cannot run this test as. Also, do not develop as root!")
return
with open(config.get_config_path('conf.d/blocked.conf'), 'w') as f:
f.write("[blocked]\nnop=nop\n")
os.chmod(config.get_config_path('conf.d/blocked.conf'), 0o444)
self.assertEqual(gco('blocked', 'nop'), 'nop')
# This shall fail, and write log msg
self.assertEqual(sco('blocked', 'new_value', '666'), False)
os.chmod(config.get_config_path('conf.d/blocked.conf'), 0)
# reload
config.augeas = None
self.assertEqual(gco('blocked', 'nop'), None)
self.assertEqual(gco('blocked', 'nop', 'def'), 'def')
class AppConfigTest(ConfigTestBase):
def setUp(self):
super(AppConfigTest, self).setUp()
self.app = AgoTest()
global gco, sco
gco = self.app.get_config_option
sco = self.app.set_config_option
def test_get_basic(self):
self.assertEqual(gco('test_value_0'), '100')
self.assertEqual(gco('test_value_1'), None)
self.assertEqual(gco('units'), 'inv')
def test_get_section(self):
# from system.conf:
self.assertEqual(gco('broker', section='system'), 'localhost')
# from test.conf:
self.assertEqual(gco('password', section='system'), 'testpwd')
def test_get_app(self):
# Will give None, since we're looking in section test, which is not in system.conf
self.assertEqual(gco('test_value_0', app=['other', 'system']), None)
# Will give system.confs password
self.assertEqual(gco('password', app=['other', 'system'], section='system'), 'letmein')
def test_set_basic(self):
self.assertEqual(sco('new_value', '666'), True)
self.assertEqual(gco('new_value'), '666')
self.assertEqual(sco('test_string', 'goodbye'), True)
self.assertEqual(gco('test_string'), 'goodbye')
def test_set_section(self):
self.assertEqual(sco('some_value', '0', 'sec2'), True)
self.assertEqual(gco('some_value'), None)
self.assertEqual(gco('some_value', section='sec2'), '0')
def test_set_new_file(self):
self.assertFalse(os.path.exists(config.get_config_path("conf.d/newfile.conf")))
self.assertEqual(sco('new_value', '666', section='newfile'), True)
self.assertTrue(os.path.exists(config.get_config_path("conf.d/newfile.conf")))
self.assertEqual(gco('new_value', section='newfile'), '666')
...@@ -216,6 +216,31 @@ namespace agocontrol { ...@@ -216,6 +216,31 @@ namespace agocontrol {
return getConfigSectionOption(section_, option, defaultValue, app_); return getConfigSectionOption(section_, option, defaultValue, app_);
} }
/**
* Read all options in the given section from the configuration subsystem.
*
* Returns a map of all found values in the given section/app.
* If an explicit section/app is set, we only look in those.
* If extra section is used, we look in apps own section first, then on the given extra section.
* If extra app is used, we look in the apps own file first, then in the given extra file.
*
* Note that we do not automaticall set app = section in this method!
*/
std::map<std::string, std::string> getConfigSection(const ConfigNameList &section=BLANK_CONFIG_NAME_LIST, const ConfigNameList &app = BLANK_CONFIG_NAME_LIST) {
ConfigNameList section_(section, appConfigSection);
ConfigNameList app_;
if(!app.empty() && !app.isExtra()) {
app_.addAll(app);
}else if(!app.empty()) {
app_.add(appConfigSection);
app_.addAll(app);
}else
app_.add(appConfigSection);
return agocontrol::getConfigSection(section_, app_);
}
/** /**
* Write a config option to the configuration subsystem. * Write a config option to the configuration subsystem.
* *
......
...@@ -257,7 +257,8 @@ const ConfigNameList BLANK_CONFIG_NAME_LIST = ConfigNameList(); ...@@ -257,7 +257,8 @@ const ConfigNameList BLANK_CONFIG_NAME_LIST = ConfigNameList();
ConfigNameList::ConfigNameList(const char* name) { ConfigNameList::ConfigNameList(const char* name) {
extra = false; extra = false;
add(name); if(name != nullptr)
add(name);
} }
ConfigNameList::ConfigNameList(const std::string& name) { ConfigNameList::ConfigNameList(const std::string& name) {
...@@ -359,6 +360,49 @@ std::string getConfigSectionOption(const ConfigNameList& section, const std::str ...@@ -359,6 +360,49 @@ std::string getConfigSectionOption(const ConfigNameList& section, const std::str
return std::string(); return std::string();
} }
std::map<std::string, std::string> getConfigSection(const ConfigNameList& section, const ConfigNameList &app) {
std::map<std::string, std::string> result;
if (augeas==NULL){
if(!augeas_init()) {
return result;
}
}
// If app has no values, default to section
ConfigNameList app_(app, section);
AGO_TRACE() << "Augeas: get all " << app_ << " / " << section;
BOOST_FOREACH(const std::string & a, app_.names()) {
BOOST_FOREACH(const std::string & s, section.names()) {
char **paths = NULL;
std::string match_path = augeasPath(confPathFromApp(a), s, "*");
int ret = aug_match(augeas, match_path.c_str(), &paths);
for(int i=0; i < ret; i++) {
char *path = paths[i];
const char *value;
if(aug_get(augeas, path, &value) == 1 && value) {
std::string key(path + match_path.size()-1);
if(key[0] == '#')
continue;
// First found takes precedence, just as getConfigSectionOption behaves.
if(result.find(key) != result.end())
continue;
AGO_TRACE() << "Augeas: found matching option in " << path << ", " << key << "=" << value;
result[key] = value;
}
free(path);
}
free(paths);
}
}
return result;
}
bool setConfigSectionOption(const std::string& section, const std::string& option, float value, const std::string& app) { bool setConfigSectionOption(const std::string& section, const std::string& option, float value, const std::string& app) {
std::stringstream stringvalue; std::stringstream stringvalue;
stringvalue << value; stringvalue << value;
......
...@@ -57,8 +57,6 @@ namespace agocontrol { ...@@ -57,8 +57,6 @@ namespace agocontrol {
std::list<std::string> name_list; std::list<std::string> name_list;
protected: protected:
bool extra; bool extra;
bool isExtra() const { return extra; }
ConfigNameList& addAll(const ConfigNameList& names) ;
public: public:
ConfigNameList() : extra(false) {} ConfigNameList() : extra(false) {}
...@@ -67,8 +65,10 @@ namespace agocontrol { ...@@ -67,8 +65,10 @@ namespace agocontrol {
ConfigNameList(const ConfigNameList &names) ; ConfigNameList(const ConfigNameList &names) ;
ConfigNameList(const ConfigNameList &names1, const ConfigNameList &names2) ; ConfigNameList(const ConfigNameList &names1, const ConfigNameList &names2) ;
ConfigNameList& add(const std::string &name) ; ConfigNameList& add(const std::string &name) ;
ConfigNameList& addAll(const ConfigNameList& names) ;
int empty() const { return name_list.empty(); } int empty() const { return name_list.empty(); }
int size() const { return name_list.size(); } int size() const { return name_list.size(); }
bool isExtra() const { return extra; }
const std::list<std::string>& names() const { return name_list; } const std::list<std::string>& names() const { return name_list; }
friend std::ostream& operator<< (std::ostream& os, const ConfigNameList &list) ; friend std::ostream& operator<< (std::ostream& os, const ConfigNameList &list) ;
...@@ -81,7 +81,19 @@ namespace agocontrol { ...@@ -81,7 +81,19 @@ namespace agocontrol {
public: public:
ExtraConfigNameList(const std::string& name) ExtraConfigNameList(const std::string& name)
: ConfigNameList(name) : ConfigNameList(name)
{extra = true;} {
extra = true;
}
static ExtraConfigNameList toExtra(const ConfigNameList& existing) {
ExtraConfigNameList n;
n.addAll(existing);
return n;
}
private:
ExtraConfigNameList() {
extra = true;
}
}; };
/* Use this for default values */ /* Use this for default values */
...@@ -103,7 +115,7 @@ namespace agocontrol { ...@@ -103,7 +115,7 @@ namespace agocontrol {
* section -- A ConfigNameList section to look for the option in. * section -- A ConfigNameList section to look for the option in.
* Note that a regular string can be passed, it will create an implicit ConfigNameList. * Note that a regular string can be passed, it will create an implicit ConfigNameList.
* *
* option -- The name of the option to retreive * option -- The name of the option to retrieve
* *
* defaultValue -- If the option can not be found in any of the specified * defaultValue -- If the option can not be found in any of the specified
* sections, fall back to this value. * sections, fall back to this value.
...@@ -123,6 +135,19 @@ namespace agocontrol { ...@@ -123,6 +135,19 @@ namespace agocontrol {
std::string getConfigSectionOption(const ConfigNameList& section, const std::string& option, const char* defaultValue, const ConfigNameList& app = BLANK_CONFIG_NAME_LIST); std::string getConfigSectionOption(const ConfigNameList& section, const std::string& option, const char* defaultValue, const ConfigNameList& app = BLANK_CONFIG_NAME_LIST);
std::string getConfigSectionOption(const ConfigNameList& section, const std::string& option, const std::string& defaultValue, const ConfigNameList& app = BLANK_CONFIG_NAME_LIST); std::string getConfigSectionOption(const ConfigNameList& section, const std::string& option, const std::string& defaultValue, const ConfigNameList& app = BLANK_CONFIG_NAME_LIST);
boost::filesystem::path getConfigSectionOption(const ConfigNameList& section, const std::string& option, const boost::filesystem::path& defaultValue, const ConfigNameList& app = BLANK_CONFIG_NAME_LIST); boost::filesystem::path getConfigSectionOption(const ConfigNameList& section, const std::string& option, const boost::filesystem::path& defaultValue, const ConfigNameList& app = BLANK_CONFIG_NAME_LIST);
/**
* Read one or more configuration sections, and return all options as a map.
*
* Section and app arguments behaves same way as for getConfigSectionOption.
* If the key is found in multiple locations, the first match is used (same way as getConfigSectionOption would
* return that specific option).
*
* @param section
* @param app
* @return
*/
std::map<std::string, std::string> getConfigSection(const ConfigNameList& section, const ConfigNameList &app);
Json::Value getConfigTree(); Json::Value getConfigTree();
/** /**
......
...@@ -17,7 +17,9 @@ target_link_libraries (testrunner ${TEST_LIBRARIES}) ...@@ -17,7 +17,9 @@ target_link_libraries (testrunner ${TEST_LIBRARIES})
add_test( cpp-test testrunner ) add_test( cpp-test testrunner )
add_test( python-test python -m unittest discover -s ../shared/ ) add_test( python-test pytest -v ${CMAKE_CURRENT_SOURCE_DIR}/python/)
set_tests_properties(python-test PROPERTIES
ENVIRONMENT PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/../python/)
# Shortcut to build and run all tests # Shortcut to build and run all tests
add_custom_target(check add_custom_target(check
......
import pytest
import agoclient.config as config
from agoclient.agoapp import AgoApp
import os, os.path, shutil
# Find checkedout code dir:
# We're in subdir python/agoclient/
devdir = os.path.realpath(os.path.dirname(os.path.realpath(__file__)) + "/../..")
class AgoTest(AgoApp):
pass
@pytest.fixture
def setup_config(tmpdir):
# Create dummy conf
conf_d = tmpdir.mkdir('conf.d')
# Custom dir, we must place agocontrol.aug into this dir
# TODO: or add some dir in loadpath?
shutil.copyfile("%s/conf/agocontrol.aug" % devdir, str(tmpdir.join("agocontrol.aug")))
shutil.copyfile("%s/conf/conf.d/system.conf" % devdir, str(conf_d.join("system.conf")))
config.set_config_dir(str(tmpdir))
with open(config.get_config_path('conf.d/test.conf'), 'w') as f:
f.write("[test]\n"+
"test_value_0=100\n"+
"test_value_blank=\n"+
"local=4\n"+
"units=inv\n"+
"[system]\n"+
"password=testpwd\n"+
"[sec2]\n"+
"some=value\n"+
"test_value_blank=not blank\n")
# Kill augeas
config.augeas = None
yield str(tmpdir)
tmpdir.remove()
@pytest.fixture
def app_sut(setup_config):
app = AgoTest()
yield app
gco = config.get_config_option
sco = config.set_config_option
class TestConfig:
def test_get_path(self, setup_config):
assert config.get_config_path("conf.d") == "%s/conf.d" % setup_config
assert config.get_config_path("conf.d/") == "%s/conf.d/" % setup_config
assert config.get_config_path("/conf.d/") == "%s/conf.d/" % setup_config
def test_get_basic(self, setup_config):
assert gco('system', 'broker') == 'localhost'
assert gco('system', 'not_set') is None
assert gco('test', 'test_value_blank') is None
assert gco('test', 'local') == '4'
# Test with defaults
assert gco('system', 'broker', 'def') == 'localhost'
assert gco('system', 'not_set', 'def') == 'def'
def test_get_multiapp(self, setup_config):
assert gco('system', 'broker', app="test") is None
assert gco('system', 'broker', app=["test", "system"]) == 'localhost'
assert gco('system', 'password', app=["test", "system"]) == 'testpwd'
def test_get_multisection(self, setup_config):
# Must explicitly tell app=test, with differnet section name
assert gco('sec2', 'some', app='test') == 'value'
assert gco(['sec2', 'test'], 'test_value_blank', app='test') == 'not blank'
assert gco(['system'], 'units') == 'SI'
assert gco(['test', 'system'], 'units') == 'inv'
def test_get_section(self, setup_config):
d = config.get_config_section(['system'], ['system'])
assert d['broker'] == 'localhost'
assert d['password'] == 'letmein'
assert d['uuid'] == '00000000-0000-0000-000000000000'
d = config.get_config_section(['system'], ['test', 'system'])
assert d['password'] == 'testpwd'
assert d['broker'] == 'localhost'
assert d['uuid'] == '00000000-0000-0000-000000000000'
def test_set_basic(self, setup_config):
assert sco('system', 'new_value', '666')
assert gco('system', 'new_value') == '666'
assert sco('system', 'test_string', 'goodbye')
assert gco('system', 'test_string') == 'goodbye'
def test_set_new(self, setup_config):
assert not os.path.exists(config.get_config_path("conf.d/newfile.conf"))
assert sco('newfile', 'new_value', '666') == True
assert os.path.exists(config.get_config_path("conf.d/newfile.conf"))
assert gco('newfile', 'new_value') == '666'
def test_unwritable(self, setup_config):
if os.getuid() == 0:
# TODO: Fix builders so they dont run as root, then change to self.fail instead.
#self.fail("You cannot run this test as. Also, do not develop as root!")
print("You cannot run this test as. Also, do not develop as root!")
return
with open(config.get_config_path('conf.d/blocked.conf'), 'w') as f:
f.write("[blocked]\nnop=nop\n")
os.chmod(config.get_config_path('conf.d/blocked.conf'), 0o444)
assert gco('blocked', 'nop') == 'nop'
# This shall fail, and write log msg
assert not sco('blocked', 'new_value', '666')
os.chmod(config.get_config_path('conf.d/blocked.conf'), 0)
# reload
config.augeas = None
assert gco('blocked', 'nop') == None
assert gco('blocked', 'nop', 'def') == 'def'
class TestAppConfig:
def test_get_basic(self, app_sut):
assert app_sut.get_config_option('test_value_0') == '100'
assert app_sut.get_config_option('test_value_1') is None
assert app_sut.get_config_option('units') == 'inv'
def test_get_section(self, app_sut):
# from system.conf:
assert app_sut.get_config_option('broker', section='system') == 'localhost'
# from test.conf:
assert app_sut.get_config_option('password', section='system') == 'testpwd'
def test_get_app(self, app_sut):
# Will give None, since we're looking in section test, which is not in system.conf
assert app_sut.get_config_option('test_value_0', app=['other', 'system']) is None
# Will give system.confs password
assert app_sut.get_config_option('password', app=['other', 'system'], section='system') == 'letmein'
def test_get_section(self, app_sut):
d = app_sut.get_config_section()
assert d == dict(test_value_0='100',
test_value_blank=None,
local='4',
units='inv')
d = app_sut.get_config_section(['system'], ['system'])
assert d['broker'] == 'localhost'
assert d['password'] == 'letmein'
assert d['uuid'] == '00000000-0000-0000-000000000000'
d = app_sut.get_config_section(['system'], ['test', 'system'])
assert d['password'] == 'testpwd'
assert d['broker'] == 'localhost'
assert d['uuid'] == '00000000-0000-0000-000000000000'
assert 'test_value_blank' not in d # from test section
def test_set_basic(self, app_sut):
assert app_sut.set_config_option('new_value', '666') == True
assert app_sut.get_config_option('new_value') == '666'
assert app_sut.set_config_option('test_string', 'goodbye') == True
assert app_sut.get_config_option('test_string') == 'goodbye'
def test_set_section(self, app_sut):
assert app_sut.set_config_option('some_value', '0', 'sec2') == True
assert app_sut.get_config_option('some_value') is None
assert app_sut.get_config_option('some_value', section='sec2') == '0'
def test_set_new_file(self, app_sut):
assert not os.path.exists(config.get_config_path("conf.d/newfile.conf"))
assert app_sut.set_config_option('new_value', '666', section='newfile')
assert os.path.exists(config.get_config_path("conf.d/newfile.conf"))
assert app_sut.get_config_option('new_value', section='newfile') == '666'
...@@ -21,6 +21,7 @@ class TestAgoConfig : public CppUnit::TestFixture ...@@ -21,6 +21,7 @@ class TestAgoConfig : public CppUnit::TestFixture
CPPUNIT_TEST( testSetError ); CPPUNIT_TEST( testSetError );
CPPUNIT_TEST( testAppSet ); CPPUNIT_TEST( testAppSet );
CPPUNIT_TEST( testAppGet ); CPPUNIT_TEST( testAppGet );
CPPUNIT_TEST( testAppGetSection );
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
ConfigNameList empty; ConfigNameList empty;
...@@ -39,6 +40,7 @@ public: ...@@ -39,6 +40,7 @@ public:
void testSetError(); void testSetError();
void testAppGet(); void testAppGet();
void testAppGetSection();
void testAppSet(); void testAppSet();
}; };
...@@ -56,6 +58,9 @@ public: ...@@ -56,6 +58,9 @@ public:
CPPUNIT_ASSERT_EQUAL(std::string(expected), (actual)) CPPUNIT_ASSERT_EQUAL(std::string(expected), (actual))
#define ASSERT_NOSTR(actual) \ #define ASSERT_NOSTR(actual) \
CPPUNIT_ASSERT_EQUAL(std::string(), (actual)) CPPUNIT_ASSERT_EQUAL(std::string(), (actual))
#define ASSERT_MAP_STR(expected, key, map) \
CPPUNIT_ASSERT(map.find(key) != map.end()); \
ASSERT_STR(expected, map.find(key)->second)
void TestAgoConfig::setUp() { void TestAgoConfig::setUp() {
tempDir = fs::temp_directory_path() / fs::unique_path(); tempDir = fs::temp_directory_path() / fs::unique_path();
...@@ -102,6 +107,11 @@ void TestAgoConfig::testBasicGet() { ...@@ -102,6 +107,11 @@ void TestAgoConfig::testBasicGet() {
ASSERT_STR("localhost", getConfigSectionOption("system", "broker", nullptr)); ASSERT_STR("localhost", getConfigSectionOption("system", "broker", nullptr));
ASSERT_STR("value", getConfigSectionOption("test", "existing_key", nullptr)); ASSERT_STR("value", getConfigSectionOption("test", "existing_key", nullptr));
auto map = getConfigSection("test", nullptr);
CPPUNIT_ASSERT_EQUAL((size_t)1, map.size());
ASSERT_MAP_STR("value", "existing_key", map);
} }
void TestAgoConfig::testFallbackGet() { void TestAgoConfig::testFallbackGet() {
...@@ -136,6 +146,10 @@ void TestAgoConfig::testSimulatedAppGet() { ...@@ -136,6 +146,10 @@ void TestAgoConfig::testSimulatedAppGet() {
// Overriden in test.conf // Overriden in test.conf
ASSERT_STR("testpwd", getConfigSectionOption(section, "password", nullptr, app)); ASSERT_STR("testpwd", getConfigSectionOption(section, "password", nullptr, app));
auto map = getConfigSection(section, app);
CPPUNIT_ASSERT_EQUAL((size_t)2, map.size());
ASSERT_MAP_STR("testpwd", "password", map);
ASSERT_MAP_STR("value", "existing_key", map);
// from system.conf; will not be found since we only look in app test // from system.conf; will not be found since we only look in app test
ASSERT_NOSTR(getConfigSectionOption(section, "broker", nullptr, app)); ASSERT_NOSTR(getConfigSectionOption(section, "broker", nullptr, app));
...@@ -146,6 +160,13 @@ void TestAgoConfig::testSimulatedAppGet() { ...@@ -146,6 +160,13 @@ void TestAgoConfig::testSimulatedAppGet() {
// Add system to app, and make sure we now can read broker // Add system to app, and make sure we now can read broker
app.add("system"); app.add("system");
ASSERT_STR("localhost", getConfigSectionOption(section, "broker", nullptr)); ASSERT_STR("localhost", getConfigSectionOption(section, "broker", nullptr));
map = getConfigSection(section, app);
CPPUNIT_ASSERT_GREATEREQUAL((size_t)8, map.size());
ASSERT_MAP_STR("testpwd", "password", map);
ASSERT_MAP_STR("value", "existing_key", map);
ASSERT_MAP_STR("00000000-0000-0000-000000000000", "uuid", map);
ASSERT_MAP_STR("SI", "units", map);
} }
void TestAgoConfig::testBasicSet() { void TestAgoConfig::testBasicSet() {
...@@ -191,15 +212,38 @@ void TestAgoConfig::testAppGet() { ...@@ -191,15 +212,38 @@ void TestAgoConfig::testAppGet() {
// This should go directly to system.conf, only // This should go directly to system.conf, only
ASSERT_STR("localhost", app.getConfigOption("broker", nullptr, "system")); ASSERT_STR("localhost", app.getConfigOption("broker", nullptr, "system"));
ASSERT_STR("letmein", app.getConfigOption("password", nullptr, "system"));
ASSERT_NOSTR(app.getConfigOption("nonexisting_key", nullptr, "system")); ASSERT_NOSTR(app.getConfigOption("nonexisting_key", nullptr, "system"));
ASSERT_NOSTR(app.getConfigOption("existing_key", nullptr, "system")); ASSERT_NOSTR(app.getConfigOption("existing_key", nullptr, "system"));
// This shall fall back on system.conf // This shall fall back on system.conf
ASSERT_STR("localhost", app.getConfigOption("broker", nullptr, ExtraConfigNameList("system"))); ASSERT_STR("localhost", app.getConfigOption("broker", nullptr, ExtraConfigNameList("system")));
ASSERT_STR("testpwd", app.getConfigOption("password", nullptr, ExtraConfigNameList("system")));
ASSERT_NOSTR(app.getConfigOption("nonexisting_key", nullptr, ExtraConfigNameList("system"))); ASSERT_NOSTR(app.getConfigOption("nonexisting_key", nullptr, ExtraConfigNameList("system")));
ASSERT_STR("value", app.getConfigOption("existing_key", nullptr, ExtraConfigNameList("system"))); ASSERT_STR("value", app.getConfigOption("existing_key", nullptr, ExtraConfigNameList("system")));
} }
void TestAgoConfig::testAppGetSection() {
DummyUnitTestApp app;
// This should get the app only
auto map = app.getConfigSection(nullptr, nullptr);
CPPUNIT_ASSERT_EQUAL((size_t)1, map.size());
ASSERT_MAP_STR("value", "existing_key", map);
// This sould only get system
map = app.getConfigSection("system", "system");
ASSERT_MAP_STR("letmein", "password", map);
// This should fall back on system
map = app.getConfigSection("system", ExtraConfigNameList("system"));
ASSERT_MAP_STR("testpwd", "password", map);
ASSERT_MAP_STR("00000000-0000-0000-000000000000", "uuid", map);
ASSERT_MAP_STR("SI", "units", map);
CPPUNIT_ASSERT(map.find("existing_key") == map.end());
}
void TestAgoConfig::testAppSet() { void TestAgoConfig::testAppSet() {
DummyUnitTestApp app; DummyUnitTestApp app;
ASSERT_NOSTR(app.getConfigOption("nonexisting_key", nullptr)); ASSERT_NOSTR(app.getConfigOption("nonexisting_key", nullptr));
......