Repository: rucio/rucio

Issue:
add default value to config table
Motivation
----------
It would be nice to define a default value when reading a config from the config table.
[https://github.com/rucio/rucio/blob/master/lib/rucio/core/config.py#L101](https://github.com/rucio/rucio/blob/master/lib/rucio/core/config.py#L101
)

Modification
------------
```python
def get(section, option, session=None)
```
to
```python
def get(section, option, default=None, session=None)
```

Also REST and API have to be updated.



Discussion:

Resolving patch:
diff --git a/lib/rucio/api/config.py b/lib/rucio/api/config.py
--- a/lib/rucio/api/config.py
+++ b/lib/rucio/api/config.py
@@ -7,6 +7,7 @@
 #
 # Authors:
 # - Mario Lassnig, <mario.lassnig@cern.ch>, 2014
+# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019
 #
 # PY3K COMPATIBLE
 
diff --git a/lib/rucio/core/config.py b/lib/rucio/core/config.py
--- a/lib/rucio/core/config.py
+++ b/lib/rucio/core/config.py
@@ -9,6 +9,7 @@
   Authors:
   - Mario Lassnig, <mario.lassnig@cern.ch>, 2014
   - Cedric Serfon, <cedric.serfon@cern.ch>, 2017
+  - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019
 
   PY3K COMPATIBLE
 '''
@@ -98,7 +99,7 @@ def has_option(section, option, session=None):
 
 
 @read_session
-def get(section, option, session=None):
+def get(section, option, default=None, session=None):
     """
     Get an option value for the named section. Value can be auto-coerced to string, int, float, bool, None.
 
@@ -107,6 +108,7 @@ def get(section, option, session=None):
 
     :param section: The name of the section.
     :param option: The name of the option.
+    :param default: The default value if no value is found.
     :param session: The database session in use.
     :returns: The auto-coerced value.
     """
@@ -115,8 +117,10 @@ def get(section, option, session=None):
 
     if tmp is not None:
         return __convert_type(tmp[0])
+    elif default is None:
+        raise ConfigNotFound
     else:
-        raise ConfigNotFound()
+        return default
 
 
 @read_session
diff --git a/lib/rucio/core/lifetime_exception.py b/lib/rucio/core/lifetime_exception.py
--- a/lib/rucio/core/lifetime_exception.py
+++ b/lib/rucio/core/lifetime_exception.py
@@ -16,7 +16,7 @@
 from sqlalchemy.exc import IntegrityError
 from sqlalchemy.orm.exc import NoResultFound
 
-from rucio.common.exception import ConfigNotFound, RucioException, LifetimeExceptionDuplicate, LifetimeExceptionNotFound, UnsupportedOperation
+from rucio.common.exception import RucioException, LifetimeExceptionDuplicate, LifetimeExceptionNotFound, UnsupportedOperation
 from rucio.common.utils import generate_uuid, str_to_date
 import rucio.common.policy
 from rucio.core.config import get
@@ -120,12 +120,9 @@ def add_exception(dids, account, pattern, comments, expires_at, session=None):
     text += '\n'
     text += 'Approve:   https://rucio-ui.cern.ch/lifetime_exception?id=%s&action=approve\n' % str(exception_id)
     text += 'Deny:      https://rucio-ui.cern.ch/lifetime_exception?id=%s&action=deny\n' % str(exception_id)
-    approvers_email = []
-    try:
-        approvers_email = get('lifetime_model', 'approvers_email', session=session)
+    approvers_email = get('lifetime_model', 'approvers_email', default=[], session=session)
+    if approvers_email:
         approvers_email = approvers_email.split(',')  # pylint: disable=no-member
-    except ConfigNotFound:
-        approvers_email = []
 
     add_message(event_type='email',
                 payload={'body': text, 'to': approvers_email,


Test patch:
diff --git a/lib/rucio/tests/test_config.py b/lib/rucio/tests/test_config.py
--- a/lib/rucio/tests/test_config.py
+++ b/lib/rucio/tests/test_config.py
@@ -6,13 +6,45 @@
 #
 # Authors:
 # - Mario Lassnig, <mario.lassnig@cern.ch>, 2014
+# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2019
 
-from nose.tools import assert_equal, assert_in, assert_is_instance, assert_true
+from nose.tools import assert_equal, assert_in, assert_is_instance, assert_true, assert_raises
 
 from rucio.client.configclient import ConfigClient
+import rucio.core.config as config_core
+from rucio.common import exception
 from rucio.common.utils import generate_uuid
 
 
+class TestConfigCore:
+
+    def test_get_config_sections(self):
+        """ CONFIG (CORE): Retreive configuration section only """
+        expected_sections = [str(generate_uuid()), str(generate_uuid())]
+        for section in expected_sections:
+            config_core.set(section, str(generate_uuid()), str(generate_uuid()))
+        sections = config_core.sections()
+        for section in expected_sections:
+            assert_in(section, sections)
+
+    def test_get_and_set_section_option(self):
+        """ CONFIG (CORE): Retreive configuration option only """
+        # get and set
+        section = str(generate_uuid())
+        option = str(generate_uuid())
+        expected_value = str(generate_uuid())
+        config_core.set(section=section, option=option, value=expected_value)
+        value = config_core.get(section, option)
+        assert_equal(value, expected_value)
+
+        # default value
+        section = str(generate_uuid())
+        config_core.set(section=section, option=str(generate_uuid()), value=str(generate_uuid()))
+        default_value = 'default'
+        value = config_core.get(section, 'new_option', default=default_value)
+        assert_equal(value, default_value)
+
+
 class TestConfigClients:
 
     @classmethod
@@ -65,3 +97,6 @@ def test_get_config_section_option(self):
         """ CONFIG (CLIENT): Retrieve configuration option only """
         tmp = self.c.get_config(self.test_section_1, self.test_option_s)
         assert_equal(tmp, self.test_option_sv)
+
+        with assert_raises(exception.ConfigNotFound):
+            self.c.get_config(self.test_section_1, 'no_option')