Repository: rucio/rucio

Issue:
Alembic context issue with  move_rse_artributes_to_rse_attributes migration

Motivation
----------
The migration introduced in https://github.com/rucio/rucio/commit/8e744b0b5a65b07a08ef2f29880ce33e13165aed prevents alembic from running commands related to creating new revisions or listing the migration history. 

```
Traceback (most recent call last):
  File "<string>", line 5, in get_context
NameError: name '_proxy' is not defined

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/alembic", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.6/site-packages/alembic/config.py", line 588, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/usr/local/lib/python3.6/site-packages/alembic/config.py", line 582, in main
    self.run_cmd(cfg, options)
  File "/usr/local/lib/python3.6/site-packages/alembic/config.py", line 562, in run_cmd
    **dict((k, getattr(options, k, None)) for k in kwarg)
  File "/usr/local/lib/python3.6/site-packages/alembic/command.py", line 461, in history
    _display_history(config, script, base, head)
  File "/usr/local/lib/python3.6/site-packages/alembic/command.py", line 430, in _display_history
    base=base or "base", head=head or "heads"
  File "/usr/local/lib/python3.6/site-packages/alembic/script/base.py", line 278, in walk_revisions
    head, base, inclusive=True, assert_relative_length=False
  File "/usr/local/lib/python3.6/site-packages/alembic/script/revision.py", line 794, in iterate_revisions
    assert_relative_length=assert_relative_length,
  File "/usr/local/lib/python3.6/site-packages/alembic/script/revision.py", line 1392, in _collect_upgrade_revisions
    assert_relative_length=assert_relative_length,
  File "/usr/local/lib/python3.6/site-packages/alembic/script/revision.py", line 1189, in _parse_upgrade_target
    return self.get_revisions(target)
  File "/usr/local/lib/python3.6/site-packages/alembic/script/revision.py", line 528, in get_revisions
    id_  # type:ignore [arg-type]
  File "/usr/local/lib/python3.6/site-packages/alembic/script/revision.py", line 743, in _resolve_revision_number
    self._revision_map
  File "/usr/local/lib64/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 1113, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "/usr/local/lib/python3.6/site-packages/alembic/script/revision.py", line 189, in _revision_map
    for revision in self._generator():
  File "/usr/local/lib/python3.6/site-packages/alembic/script/base.py", line 136, in _load_revisions
    script = Script._from_filename(self, vers, file_)
  File "/usr/local/lib/python3.6/site-packages/alembic/script/base.py", line 999, in _from_filename
    module = util.load_python_file(dir_, filename)
  File "/usr/local/lib/python3.6/site-packages/alembic/util/pyfiles.py", line 92, in load_python_file
    module = load_module_py(module_id, path)
  File "/usr/local/lib/python3.6/site-packages/alembic/util/pyfiles.py", line 108, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/opt/rucio/lib/rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py", line 41, in <module>
    if context.get_context().version_table_schema
  File "<string>", line 7, in get_context
  File "/usr/local/lib/python3.6/site-packages/alembic/util/langhelpers.py", line 101, in _name_error
    ) from from_
NameError: Can't invoke function 'get_context', as the proxy object has not yet been established for the Alembic 'EnvironmentContext' class.  Try placing this code inside a callable.
```


Modification
------------





Discussion:
The exception comes from the following lines:
https://github.com/rucio/rucio/blob/2bedb4d368eeb61577b2de877a5916ca0f310bc3/lib/rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py#L39-L43

Essentially the `get_context` should be referenced inside the `upgrade` or `downgrade` methods

Resolving patch:
diff --git a/lib/rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py b/lib/rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py
--- a/lib/rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py
+++ b/lib/rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py
@@ -26,48 +26,51 @@
 revision = "2190e703eb6e"
 down_revision = "f41ffe206f37"
 
-CHANGED_RSE_SETTINGS = [
-    ("city", sa.String(255)),
-    ("region_code", sa.String(2)),
-    ("country_name", sa.String(255)),
-    ("continent", sa.String(2)),
-    ("time_zone", sa.String(255)),
-    ("ISP", sa.String(255)),
-    ("ASN", sa.String(255)),
-]
-
-SCHEMA = (
-    context.get_context().version_table_schema
-    if context.get_context().version_table_schema
-    else ""
-)
-
-RSEAttrAssociation = sa.sql.table(
-    "rse_attr_map",
-    sa.Column("rse_id", GUID()),
-    sa.Column("key", sa.String(255)),
-    sa.Column("value", BooleanString(255)),
-    sa.Column("created_at", sa.DateTime),
-    sa.Column("updated_at", sa.DateTime),
-    schema=SCHEMA,
-)
+
+def get_changed_rse_settings():
+    return [
+        ("city", sa.String(255)),
+        ("region_code", sa.String(2)),
+        ("country_name", sa.String(255)),
+        ("continent", sa.String(2)),
+        ("time_zone", sa.String(255)),
+        ("ISP", sa.String(255)),
+        ("ASN", sa.String(255)),
+    ]
+
+
+def get_schema():
+    return context.get_context().version_table_schema \
+        if context.get_context().version_table_schema \
+        else ""
+
+
+def get_rse_attr_association():
+    return sa.sql.table(
+        "rse_attr_map",
+        sa.Column("rse_id", GUID()),
+        sa.Column("key", sa.String(255)),
+        sa.Column("value", BooleanString(255)),
+        sa.Column("created_at", sa.DateTime),
+        sa.Column("updated_at", sa.DateTime),
+        schema=get_schema(),
+    )
 
 
 def upgrade():
     """
     Upgrade the database to this revision
     """
-
     if context.get_context().dialect.name in ["oracle", "mysql", "postgresql"]:
         conn = get_bind()
-        for setting, setting_datatype in CHANGED_RSE_SETTINGS:
+        for setting, setting_datatype in get_changed_rse_settings():
             rse_table = sa.sql.table(
                 "rses",
                 sa.Column("id", GUID()),
                 sa.Column(setting, setting_datatype),
                 sa.Column("created_at", sa.DateTime),
                 sa.Column("updated_at", sa.DateTime),
-                schema=SCHEMA,
+                schema=get_schema(),
             )
 
             select_stmt = (
@@ -83,7 +86,7 @@ def upgrade():
             )
 
             conn.execute(
-                sa.insert(RSEAttrAssociation).from_select(
+                sa.insert((get_rse_attr_association())).from_select(
                     ["rse_id", "key", "value", "created_at", "updated_at"], select_stmt
                 )
             )
@@ -93,15 +96,14 @@ def downgrade():
     """
     Downgrade the database to the previous revision
     """
-
     if context.get_context().dialect.name in ["oracle", "mysql", "postgresql"]:
         conn = get_bind()
-        for setting, setting_datatype in CHANGED_RSE_SETTINGS:
+        for setting, setting_datatype in get_changed_rse_settings():
             rse_table = sa.sql.table(
                 "rses",
                 sa.Column("id", GUID()),
                 sa.Column(setting, setting_datatype),
-                schema=SCHEMA,
+                schema=get_schema(),
             )
 
             rse_attr_association = sa.sql.table(
@@ -109,7 +111,7 @@ def downgrade():
                 sa.Column("rse_id", GUID()),
                 sa.Column("key", sa.String(255)),
                 sa.Column("value", BooleanString(255)),
-                schema=SCHEMA,
+                schema=get_schema(),
             )
 
             # Oracle needs the sub-query, since multi-table updates are not supported.
@@ -130,5 +132,5 @@ def downgrade():
             )
 
             conn.execute(
-                RSEAttrAssociation.delete().where(RSEAttrAssociation.c.key == setting)
+                rse_attr_association.delete().where(rse_attr_association.c.key == setting)
             )


Test patch: