Repository: rucio/rucio
Issue: Bug in dirac.addfile when lifetime is set

Motivation
----------
`add_file` is crashing if `lifetime` option is defined in dirac section, because the comparison is done on the `InternalScope`
https://github.com/rucio/rucio/blob/master/lib/rucio/core/dirac.py#L106-L112






Changed files:
lib/rucio/core/dirac.py, lib/rucio/tests/test_belleii.py, tools/bootstrap_tests.py, lib/rucio/tests/common.py

diff --git a/lib/rucio/core/dirac.py b/lib/rucio/core/dirac.py
index 6d489d7f7..3d30bc57a 100644
--- a/lib/rucio/core/dirac.py
+++ b/lib/rucio/core/dirac.py
@@ -71,7 +71,7 @@ def add_files(lfns, account, ignore_availability, vo='def', session=None):
     scopes = [scope.external for scope in scopes]
     exist_lfn = []
     try:
-        lifetime_dict = config_get(section='dirac', option='lifetime', session=session)
+        lifetime_dict: str = config_get(section='dirac', option='lifetime', default='{}', session=session)
         lifetime_dict = loads(lifetime_dict)
     except ConfigNotFound:
         lifetime_dict = {}
@@ -104,10 +104,10 @@ def add_files(lfns, account, ignore_availability, vo='def', session=None):
         # Compute lifetime
         lifetime = None
         if dsn_scope in lifetime_dict:
-            lifetime = lifetime_dict[dsn_scope]
+            lifetime = lifetime_dict[dsn_scope.external]
         else:
             for pattern in lifetime_dict:
-                if re.match(pattern, dsn_scope):
+                if dsn_scope.external and re.match(pattern, str(dsn_scope.external)):
                     lifetime = lifetime_dict[pattern]
                     break
 
diff --git a/lib/rucio/tests/common.py b/lib/rucio/tests/common.py
index 8c881702f..43d9835ca 100644
--- a/lib/rucio/tests/common.py
+++ b/lib/rucio/tests/common.py
@@ -103,7 +103,7 @@ def did_name_generator(did_type='file', name_prefix='', name_suffix='', path=Non
         if path is not None:
             return path
 
-        container_path = os.path.join("/belle", "mock", name_prefix, 'cont_%s' % str(uuid()))
+        container_path = os.path.join("/belle", name_prefix if name_prefix else "mock", 'cont_%s' % str(uuid()))
         if did_type == 'container':
             return container_path
 
diff --git a/lib/rucio/tests/test_belleii.py b/lib/rucio/tests/test_belleii.py
index 489048d9b..16f885bde 100644
--- a/lib/rucio/tests/test_belleii.py
+++ b/lib/rucio/tests/test_belleii.py
@@ -13,13 +13,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from datetime import datetime, timedelta
+from datetime import datetime
 
 import pytest
 
 from rucio.common.exception import InvalidObject
 from rucio.common.schema.belleii import validate_schema
 from rucio.common.utils import generate_uuid, extract_scope
+from rucio.core.config import set as config_set
 from rucio.tests.common import did_name_generator, skip_non_belleii
 
 
@@ -29,8 +30,9 @@ def test_dirac_addfile(rse_factory, did_factory, root_account, did_client, dirac
     nbfiles = 5
     rse1, rse1_id = rse_factory.make_srm_rse(deterministic=True)
     rse_client.add_rse_attribute(rse=rse1, key='ANY', value='True')
+    config_set('dirac', 'lifetime', '{"user.*": 2592400}')
 
-    # Create replicas on rse1 using addfile
+    # Create replicas on rse1 using addfile in mock scope (not lifetime)
     lfns = [{'lfn': did_name_generator('file'), 'rse': rse1, 'bytes': 1, 'adler32': '0cc737eb', 'guid': generate_uuid()} for _ in range(nbfiles)]
     files = [{'scope': extract_scope(lfn['lfn'], [])[0], 'name': lfn['lfn']} for lfn in lfns]
     reps = [{'scope': extract_scope(lfn['lfn'], [])[0], 'name': lfn['lfn'], 'rse': rse1} for lfn in lfns]
@@ -65,7 +67,23 @@ def test_dirac_addfile(rse_factory, did_factory, root_account, did_client, dirac
         rules = [rule for rule in did_client.list_did_rules(scope, name)]
         assert len(rules) == 1
         assert rules[0]['rse_expression'] == rse1
-        assert datetime.now() - rules[0]['expires_at'] < timedelta(hours=24)
+        assert (rules[0]['expires_at'] - datetime.utcnow()).seconds < 86400
+
+    # Create replicas on rse1 using addfile in user scope (30 days lifetime)
+    lfns = [{'lfn': did_name_generator('file', name_prefix='user'), 'rse': rse1, 'bytes': 1, 'adler32': '0cc737eb', 'guid': generate_uuid()} for _ in range(nbfiles)]
+    files = [{'scope': extract_scope(lfn['lfn'], [])[0], 'name': lfn['lfn']} for lfn in lfns]
+    reps = [{'scope': extract_scope(lfn['lfn'], [])[0], 'name': lfn['lfn'], 'rse': rse1} for lfn in lfns]
+    dirac_client.add_files(lfns=lfns, ignore_availability=False)
+
+    # Check that the default rules are created
+    for lfn in lfns:
+        # Check dataset rule
+        directory = "/".join(lfn['lfn'].split('/')[:-1])
+        scope, name = extract_scope(directory, [])
+        rules = [rule for rule in did_client.list_did_rules(scope, name)]
+        assert len(rules) == 1
+        assert rules[0]['rse_expression'] == 'ANY=true'
+        assert (rules[0]['expires_at'] - datetime.utcnow()).days == 30
 
 
 @skip_non_belleii
diff --git a/tools/bootstrap_tests.py b/tools/bootstrap_tests.py
index 4889c128d..69aa131c9 100755
--- a/tools/bootstrap_tests.py
+++ b/tools/bootstrap_tests.py
@@ -38,7 +38,7 @@ from rucio.tests.common_server import reset_config_table  # noqa: E402
 
 
 def belleii_bootstrap(client):
-    scopes = ['raw', 'hraw', 'other', 'mc_tmp', 'mc', 'test', 'user', 'data', 'data_tmp', 'group']
+    scopes = ['raw', 'hraw', 'other', 'mc_tmp', 'mc', 'test', 'user', 'data', 'data_tmp', 'group', 'mock']
     for scope in scopes:
         try:
             client.add_scope(scope=scope, account='root')
@@ -47,7 +47,7 @@ def belleii_bootstrap(client):
         except Exception as err:
             print(err)
 
-    lpns = ['/belle', '/belle/mc', '/belle/Data', '/belle/user', '/belle/raw']
+    lpns = ['/belle', '/belle/mc', '/belle/Data', '/belle/user', '/belle/raw', '/belle/mock']
     for lpn in lpns:
         scope, name = extract_scope(lpn)
         try: