Let configuration option be overridden by environment variables
Related to #13889793.
--- a/cubicweb/cwconfig.py Fri Sep 23 09:51:10 2016 +0200
+++ b/cubicweb/cwconfig.py Fri Sep 23 13:36:06 2016 +0200
@@ -206,7 +206,7 @@
from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP,
ConfigurationError, Binary, _)
-from cubicweb.toolsutils import create_dir
+from cubicweb.toolsutils import create_dir, option_value_from_env
CONFIGURATIONS = []
@@ -416,6 +416,12 @@
'group': 'email', 'level': 3,
}),
)
+
+ def __getitem__(self, key):
+ """Get configuration option, by first looking at environmnent."""
+ file_value = super(CubicWebNoAppConfiguration, self).__getitem__(key)
+ return option_value_from_env(key, file_value)
+
# static and class methods used to get instance independant resources ##
@staticmethod
def cubicweb_version():
--- a/cubicweb/test/unittest_cwconfig.py Fri Sep 23 09:51:10 2016 +0200
+++ b/cubicweb/test/unittest_cwconfig.py Fri Sep 23 13:36:06 2016 +0200
@@ -203,6 +203,15 @@
from cubes import file
self.assertEqual(file.__path__, [join(self.custom_cubes_dir, 'file')])
+ def test_config_value_from_environment(self):
+ self.assertIsNone(self.config['base-url'])
+ os.environ['CW_BASE_URL'] = 'https://www.cubicweb.org'
+ try:
+ self.assertEqual(self.config['base-url'],
+ 'https://www.cubicweb.org')
+ finally:
+ del os.environ['CW_BASE_URL']
+
class FindPrefixTC(unittest.TestCase):
def make_dirs(self, *args):
--- a/cubicweb/test/unittest_toolsutils.py Fri Sep 23 09:51:10 2016 +0200
+++ b/cubicweb/test/unittest_toolsutils.py Fri Sep 23 13:36:06 2016 +0200
@@ -17,10 +17,12 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+import os
import tempfile
import unittest
-from cubicweb.toolsutils import RQLExecuteMatcher, read_config
+from cubicweb.toolsutils import (RQLExecuteMatcher, option_value_from_env,
+ read_config)
class RQLExecuteMatcherTests(unittest.TestCase):
@@ -78,6 +80,15 @@
class ToolsUtilsTC(unittest.TestCase):
+ def test_option_value_from_env(self):
+ os.environ['CW_DB_HOST'] = 'here'
+ try:
+ self.assertEqual(option_value_from_env('db-host'), 'here')
+ self.assertEqual(option_value_from_env('db-host', 'nothere'), 'here')
+ self.assertEqual(option_value_from_env('db-hots', 'nothere'), 'nothere')
+ finally:
+ del os.environ['CW_DB_HOST']
+
def test_read_config(self):
with tempfile.NamedTemporaryFile() as f:
f.write(SOURCES_CONTENT)
@@ -96,6 +107,17 @@
}
self.assertEqual(config, expected)
+ def test_read_config_env(self):
+ os.environ['CW_DB_HOST'] = 'here'
+ try:
+ with tempfile.NamedTemporaryFile() as f:
+ f.write(SOURCES_CONTENT)
+ f.seek(0)
+ config = read_config(f.name)
+ finally:
+ del os.environ['CW_DB_HOST']
+ self.assertEqual(config['system']['db-host'], 'here')
+
if __name__ == '__main__':
unittest.main()
--- a/cubicweb/toolsutils.py Fri Sep 23 09:51:10 2016 +0200
+++ b/cubicweb/toolsutils.py Fri Sep 23 13:36:06 2016 +0200
@@ -170,6 +170,15 @@
print('-> set permissions to 0600 for %s' % filepath)
chmod(filepath, 0o600)
+
+def option_value_from_env(option, default=None):
+ """Return the value of configuration `option` from cannonical environment
+ variable.
+ """
+ envvar = ('CW_' + '_'.join(option.split('-'))).upper()
+ return os.environ.get(envvar, default)
+
+
def read_config(config_file, raise_if_unreadable=False):
"""read some simple configuration from `config_file` and return it as a
dictionary. If `raise_if_unreadable` is false (the default), an empty
@@ -194,7 +203,7 @@
sys.stderr.write('ignoring malformed line\n%r\n' % line)
continue
option = option.strip().replace(' ', '_')
- value = value.strip()
+ value = option_value_from_env(option, value.strip())
current[option] = value or None
except IOError as ex:
if raise_if_unreadable: