Skip to content

Commit 5981ae2

Browse files
authored
Revert "Allow downgrading to 2.11 from 3.x (apache#54231)" (apache#54367)
This reverts commit b9cdc3d.
1 parent 1830857 commit 5981ae2

8 files changed

Lines changed: 66 additions & 75 deletions

File tree

airflow-core/src/airflow/cli/commands/db_command.py

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,39 +50,29 @@ def resetdb(args):
5050
db.resetdb(skip_init=args.skip_init)
5151

5252

53-
def _get_version_revision(version: str, revision_heads_map: dict[str, str] | None = None) -> str | None:
53+
def _get_version_revision(
54+
version: str, recursion_limit: int = 10, revision_heads_map: dict[str, str] | None = None
55+
) -> str | None:
5456
"""
55-
Search for the revision of the given version in revision_heads_map.
57+
Recursively search for the revision of the given version in revision_heads_map.
5658
5759
This searches given revision_heads_map for the revision of the given version, recursively
5860
searching for the previous version if the given version is not found.
59-
60-
``revision_heads_map`` must already be sorted in the dict in ascending order for this function to work. No
61-
checks are made that this is true
6261
"""
6362
if revision_heads_map is None:
6463
revision_heads_map = _REVISION_HEADS_MAP
65-
# Exact match found, we can just return it
6664
if version in revision_heads_map:
6765
return revision_heads_map[version]
68-
6966
try:
70-
wanted = tuple(map(int, version.split(".")))
67+
major, minor, patch = map(int, version.split("."))
7168
except ValueError:
7269
return None
73-
74-
# Else, we walk backwards in the revision map until we find a version that is < the target
75-
for revision, head in reversed(revision_heads_map.items()):
76-
try:
77-
current = tuple(map(int, revision.split(".")))
78-
except ValueError:
79-
log.debug("Unable to parse HEAD revision", exc_info=True)
80-
return None
81-
82-
if current < wanted:
83-
return head
84-
else:
70+
new_version = f"{major}.{minor}.{patch - 1}"
71+
recursion_limit -= 1
72+
if recursion_limit <= 0:
73+
# Prevent infinite recursion as I can't imagine 10 successive versions without migration
8574
return None
75+
return _get_version_revision(new_version, recursion_limit)
8676

8777

8878
def run_db_migrate_command(args, command, revision_heads_map: dict[str, str]):

airflow-core/src/airflow/utils/db.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,21 +1189,21 @@ def downgrade(*, to_revision, from_revision=None, show_sql_only=False, session:
11891189
config = _get_alembic_config()
11901190
# Check if downgrade is less than 3.0.0 and requires that `ab_user` fab table is present
11911191
if _revision_greater(config, _REVISION_HEADS_MAP["2.10.3"], to_revision):
1192-
try:
1193-
from airflow.providers.fab.auth_manager.models.db import FABDBManager
1194-
except ImportError:
1195-
# Raise the error with a new message
1196-
raise RuntimeError(
1197-
"Import error occurred while importing FABDBManager. We need that to exist before we can "
1198-
"downgrade to <3.0.0"
1192+
unitest_mode = conf.getboolean("core", "unit_test_mode")
1193+
if unitest_mode:
1194+
try:
1195+
from airflow.providers.fab.auth_manager.models.db import FABDBManager
1196+
1197+
dbm = FABDBManager(session)
1198+
dbm.initdb()
1199+
except ImportError:
1200+
log.warning("Import error occurred while importing FABDBManager. Skipping the check.")
1201+
return
1202+
if not inspect(settings.engine).has_table("ab_user") and not unitest_mode:
1203+
raise AirflowException(
1204+
"Downgrade to revision less than 3.0.0 requires that `ab_user` table is present. "
1205+
"Please add FabDBManager to [core] external_db_managers and run fab migrations before proceeding"
11991206
)
1200-
dbm = FABDBManager(session)
1201-
if hasattr(dbm, "reset_to_2_x"):
1202-
dbm.reset_to_2_x()
1203-
else:
1204-
# Older version before we added that function, it only has a single migration so we can just
1205-
# created
1206-
dbm.create_db_from_orm()
12071207
with create_global_lock(session=session, lock=DBLocks.MIGRATIONS):
12081208
if show_sql_only:
12091209
log.warning("Generating sql scripts for manual migration.")

airflow-core/src/airflow/utils/db_manager.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,12 @@ def upgradedb(self, session):
216216
m = manager(session)
217217
m.upgradedb()
218218

219+
def downgrade(self, session):
220+
"""Downgrade the external database managers."""
221+
for manager in self._managers:
222+
m = manager(session)
223+
m.downgrade()
224+
219225
def drop_tables(self, session, connection):
220226
"""Drop the external database managers."""
221227
for manager in self._managers:

airflow-core/tests/unit/cli/commands/test_db_command.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -658,19 +658,3 @@ def test_confirm_in_drop_archived_records_command(self, mock_drop_archived_recor
658658
)
659659
db_command.drop_archived(args)
660660
mock_drop_archived_records.assert_called_once_with(table_names=None, needs_confirm=expected)
661-
662-
663-
def test_get_version_revision():
664-
heads: dict[str, str] = {
665-
"2.10.0": "22ed7efa9da2",
666-
"2.10.3": "5f2621c13b39",
667-
"3.0.0": "29ce7909c52b",
668-
"3.0.3": "fe199e1abd77",
669-
"3.1.0": "808787349f22",
670-
}
671-
672-
assert db_command._get_version_revision("3.1.0", heads) == "808787349f22"
673-
assert db_command._get_version_revision("3.1.1", heads) == "808787349f22"
674-
assert db_command._get_version_revision("2.11.1", heads) == "5f2621c13b39"
675-
assert db_command._get_version_revision("2.10.1", heads) == "22ed7efa9da2"
676-
assert db_command._get_version_revision("2.0.0", heads) is None

airflow-core/tests/unit/utils/test_db.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
from sqlalchemy import Column, Integer, MetaData, Table, select
3434

3535
from airflow import settings
36+
from airflow.exceptions import AirflowException
3637
from airflow.models import Base as airflow_base
3738
from airflow.utils.db import (
39+
_REVISION_HEADS_MAP,
3840
AutocommitEngineForMySQL,
3941
LazySelectSequence,
4042
_get_alembic_config,
@@ -378,6 +380,18 @@ def scalar(self, stmt):
378380

379381
assert bool(lss) is False
380382

383+
@conf_vars({("core", "unit_test_mode"): "False"})
384+
def test_downgrade_raises_if_lower_than_v3_0_0_and_no_ab_user(self, mocker):
385+
mock_inspect = mocker.patch("airflow.utils.db.inspect")
386+
mock_inspect.return_value.has_table.return_value = False
387+
msg = (
388+
"Downgrade to revision less than 3.0.0 requires that `ab_user` table is present. "
389+
"Please add FabDBManager to [core] external_db_managers and run fab migrations before "
390+
"proceeding"
391+
)
392+
with pytest.raises(AirflowException, match=re.escape(msg)):
393+
downgrade(to_revision=_REVISION_HEADS_MAP["2.7.0"])
394+
381395

382396
class TestAutocommitEngineForMySQL:
383397
"""Test the AutocommitEngineForMySQL context manager."""

devel-common/src/tests_common/test_utils/db.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from __future__ import annotations
1919

2020
import json
21-
import os
2221
from tempfile import gettempdir
2322
from typing import TYPE_CHECKING
2423

@@ -102,23 +101,9 @@ def initial_db_init():
102101
from tests_common.test_utils.version_compat import AIRFLOW_V_3_0_PLUS
103102

104103
db.resetdb()
105-
# If we are testing core we want to downgrand and upgrade to ensure things migraitons are set correctly,
106-
# but we can skip that for providers
107-
108104
if AIRFLOW_V_3_0_PLUS:
109-
if os.getenv("TEST_GROUP") != "providers":
110-
db.downgrade(to_revision="5f2621c13b39")
111-
db.upgradedb(to_revision="head")
112-
else:
113-
try:
114-
from airflow.providers.fab.auth_manager.models.db import FABDBManager
115-
except ModuleNotFoundError:
116-
pass
117-
else:
118-
# If we loaded it (i.e. the provider exists) create the DB
119-
with create_session() as session:
120-
FABDBManager(session).create_db_from_orm()
121-
session.commit()
105+
db.downgrade(to_revision="5f2621c13b39")
106+
db.upgradedb(to_revision="head")
122107
else:
123108
from flask import Flask
124109

providers/fab/src/airflow/providers/fab/auth_manager/models/db.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,6 @@ def create_db_from_orm(self):
5959
super().create_db_from_orm()
6060
_get_flask_db(settings.SQL_ALCHEMY_CONN).create_all()
6161

62-
def reset_to_2_x(self):
63-
self.create_db_from_orm()
64-
# And ensure it's at the oldest version
65-
self.downgrade(_REVISION_HEADS_MAP["1.4.0"])
66-
6762
def upgradedb(self, to_revision=None, from_revision=None, show_sql_only=False):
6863
"""Upgrade the database."""
6964
if from_revision and not show_sql_only:

providers/fab/tests/unit/fab/db_manager/test_fab_db_manager.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from sqlalchemy import Table
2323

2424
from airflow.exceptions import AirflowException
25-
from airflow.utils.db import initdb
25+
from airflow.utils.db import downgrade, initdb
2626
from airflow.utils.db_manager import RunDBManager
2727

2828
from tests_common.test_utils.config import conf_vars
@@ -57,12 +57,27 @@ def test_defining_table_same_name_as_airflow_table_name_raises(self):
5757
run_db_manager.validate()
5858
metadata._remove_table("dag_run", None)
5959

60+
@mock.patch.object(RunDBManager, "downgrade")
6061
@mock.patch.object(RunDBManager, "upgradedb")
6162
@mock.patch.object(RunDBManager, "initdb")
62-
def test_init_db_calls_rundbmanager(self, mock_initdb, mock_upgrade_db, session):
63+
def test_init_db_calls_rundbmanager(self, mock_initdb, mock_upgrade_db, mock_downgrade_db, session):
6364
initdb(session=session)
6465
mock_initdb.assert_called()
6566
mock_initdb.assert_called_once_with(session)
67+
mock_downgrade_db.assert_not_called()
68+
69+
@mock.patch.object(RunDBManager, "downgrade")
70+
@mock.patch.object(RunDBManager, "upgradedb")
71+
@mock.patch.object(RunDBManager, "initdb")
72+
@mock.patch("alembic.command")
73+
def test_downgrade_dont_call_rundbmanager(
74+
self, mock_alembic_command, mock_initdb, mock_upgrade_db, mock_downgrade_db, session
75+
):
76+
downgrade(to_revision="base")
77+
mock_alembic_command.downgrade.assert_called_once_with(mock.ANY, revision="base", sql=False)
78+
mock_upgrade_db.assert_not_called()
79+
mock_initdb.assert_not_called()
80+
mock_downgrade_db.assert_not_called()
6681

6782
@conf_vars(
6883
{("database", "external_db_managers"): "airflow.providers.fab.auth_manager.models.db.FABDBManager"}
@@ -78,7 +93,9 @@ def test_rundbmanager_calls_dbmanager_methods(self, mock_fabdb_manager, session)
7893
# upgradedb
7994
ext_db.upgradedb(session=session)
8095
fabdb_manager.upgradedb.assert_called_once()
81-
# drop_tables
96+
# downgrade
97+
ext_db.downgrade(session=session)
98+
mock_fabdb_manager.return_value.downgrade.assert_called_once()
8299
connection = mock.MagicMock()
83100
ext_db.drop_tables(session, connection)
84101
mock_fabdb_manager.return_value.drop_tables.assert_called_once_with(connection)

0 commit comments

Comments
 (0)