This is an automated email from the ASF dual-hosted git repository.
onikolas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new a0d5b712172 Enforce at least one global executor is configured (#55816)
a0d5b712172 is described below
commit a0d5b7121723cb4a2271d1d684f639aa5cc127c1
Author: Niko Oliveira <[email protected]>
AuthorDate: Fri Sep 19 10:06:48 2025 -0700
Enforce at least one global executor is configured (#55816)
There are various processes in Airflow that are not yet team-ified and
that rely on executors (serving logs, callback handling) so for now,
enforce that at least one global executor is present to be used for such
things. This can be revisited and relaxed at a future time.
---
.../src/airflow/executors/executor_loader.py | 9 ++++++
.../tests/unit/executors/test_executor_loader.py | 35 +++++++++++++---------
2 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/airflow-core/src/airflow/executors/executor_loader.py
b/airflow-core/src/airflow/executors/executor_loader.py
index e13a0b80f3f..be5305c06ba 100644
--- a/airflow-core/src/airflow/executors/executor_loader.py
+++ b/airflow-core/src/airflow/executors/executor_loader.py
@@ -199,6 +199,15 @@ class ExecutorLoader:
# Split by comma to get the individual executor names and strip
spaces off of them
configs.append((team_name, [name.strip() for name in
executor_names.split(",")]))
+ # Validate that at least one global executor exists
+ has_global_executor = any(team_name is None for team_name, _ in
configs)
+ if not has_global_executor:
+ raise AirflowConfigException(
+ "At least one global executor must be configured. Current
configuration only contains "
+ "team-based executors. Please add a global executor
configuration (e.g., "
+ "'CeleryExecutor;team1=LocalExecutor' instead of
'team1=CeleryExecutor;team2=LocalExecutor')."
+ )
+
return configs
@classmethod
diff --git a/airflow-core/tests/unit/executors/test_executor_loader.py
b/airflow-core/tests/unit/executors/test_executor_loader.py
index 30fb90d0138..1d19db86b4c 100644
--- a/airflow-core/tests/unit/executors/test_executor_loader.py
+++ b/airflow-core/tests/unit/executors/test_executor_loader.py
@@ -94,17 +94,6 @@ class TestExecutorLoader:
],
id="one_executor",
),
- pytest.param(
- "team_a=CeleryExecutor",
- [
- ExecutorName(
-
module_path="airflow.providers.celery.executors.celery_executor.CeleryExecutor",
- alias="CeleryExecutor",
- team_name="team_a",
- ),
- ],
- id="one_executor_one_team",
- ),
pytest.param(
"=CeleryExecutor;team_a=CeleryExecutor;team_b=LocalExecutor",
[
@@ -486,10 +475,8 @@ class TestExecutorLoader:
@pytest.mark.parametrize(
"executor_config",
[
- "team1=CeleryExecutor;team1=LocalExecutor",
-
"team1=CeleryExecutor;team2=LocalExecutor;team1=KubernetesExecutor",
"CeleryExecutor;team1=LocalExecutor;team1=KubernetesExecutor",
-
"team_a=CeleryExecutor;team_b=LocalExecutor;team_a=KubernetesExecutor",
+
"CeleryExecutor;team_a=LocalExecutor;team_b=KubernetesExecutor;team_a=CeleryExecutor",
],
)
def test_duplicate_team_names_should_fail(self, executor_config):
@@ -560,3 +547,23 @@ class TestExecutorLoader:
with conf_vars({("core", "executor"): executor_config}):
configs =
executor_loader.ExecutorLoader._get_team_executor_configs()
assert configs == expected_configs
+
+ @pytest.mark.parametrize(
+ "executor_config",
+ [
+ "team1=CeleryExecutor",
+ "team1=CeleryExecutor;team2=LocalExecutor",
+
"team1=CeleryExecutor;team2=LocalExecutor;team3=KubernetesExecutor",
+ "team_a=CeleryExecutor,LocalExecutor;team_b=KubernetesExecutor",
+ ],
+ )
+ def test_team_only_configurations_should_fail(self, executor_config):
+ """Test that configurations with only team-based executors fail
validation."""
+ with (
+ mock.patch.object(executor_loader.ExecutorLoader,
"block_use_of_multi_team"),
+ conf_vars({("core", "executor"): executor_config}),
+ ):
+ with pytest.raises(
+ AirflowConfigException, match=r"At least one global executor
must be configured"
+ ):
+ executor_loader.ExecutorLoader._get_team_executor_configs()