This is an automated email from the ASF dual-hosted git repository.
jscheffl 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 94ece75ecbf Add workers.celery.podDisruptionBudget (#61414)
94ece75ecbf is described below
commit 94ece75ecbfb99d1ebde44637960c37ee5f1b026
Author: Przemysław Mirowski <[email protected]>
AuthorDate: Sun Mar 1 16:46:01 2026 +0100
Add workers.celery.podDisruptionBudget (#61414)
* Add workers.celery.podDisruptionBudget
* Add newsfragment
---
chart/newsfragments/61414.significant.rst | 1 +
chart/templates/NOTES.txt | 24 +++++
chart/values.schema.json | 50 +++++++++-
chart/values.yaml | 17 +++-
.../helm_tests/airflow_core/test_pdb_worker.py | 110 ++++++++++++++++++---
.../helm_tests/airflow_core/test_worker_sets.py | 95 +++++++++++++-----
6 files changed, 253 insertions(+), 44 deletions(-)
diff --git a/chart/newsfragments/61414.significant.rst
b/chart/newsfragments/61414.significant.rst
new file mode 100644
index 00000000000..137fb8e0daa
--- /dev/null
+++ b/chart/newsfragments/61414.significant.rst
@@ -0,0 +1 @@
+``workers.podDisruptionBudget`` section is now deprecated in favor of
``workers.celery.podDisruptionBudget``. Please update your configuration
accordingly.
diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt
index 6701ab1cd9f..8e0f30a5f24 100644
--- a/chart/templates/NOTES.txt
+++ b/chart/templates/NOTES.txt
@@ -375,6 +375,30 @@ DEPRECATION WARNING:
{{- end }}
+{{- if .Values.workers.podDisruptionBudget.enabled }}
+
+ DEPRECATION WARNING:
+ `workers.podDisruptionBudget.enabled` has been renamed to
`workers.celery.podDisruptionBudget.enabled`.
+ Please change your values as support for the old name will be dropped in a
future release.
+
+{{- end }}
+
+{{- if ne (int .Values.workers.podDisruptionBudget.config.maxUnavailable) 1 }}
+
+ DEPRECATION WARNING:
+ `workers.podDisruptionBudget.config.maxUnavailable` has been renamed to
`workers.celery.podDisruptionBudget.config.maxUnavailable`.
+ Please change your values as support for the old name will be dropped in a
future release.
+
+{{- end }}
+
+{{- if hasKey .Values.workers.podDisruptionBudget.config "minUnavailable" }}
+
+ DEPRECATION WARNING:
+ `workers.podDisruptionBudget.config.minUnavailable` has been renamed to
`workers.celery.podDisruptionBudget.config.minUnavailable`.
+ Please change your values as support for the old name will be dropped in a
future release.
+
+{{- end }}
+
{{- if not (empty .Values.webserver.defaultUser) }}
DEPRECATION WARNING:
diff --git a/chart/values.schema.json b/chart/values.schema.json
index 2dffad8087d..1e4de6ac25a 100644
--- a/chart/values.schema.json
+++ b/chart/values.schema.json
@@ -2229,22 +2229,22 @@
}
},
"podDisruptionBudget": {
- "description": "Worker pod disruption budget.",
+ "description": "Worker pod disruption budget (deprecated,
use `workers.celery.podDisruptionBudget` instead).",
"type": "object",
"additionalProperties": true,
"properties": {
"enabled": {
- "description": "Enable pod disruption budget.",
+ "description": "Enable pod disruption budget
(deprecated, use `workers.celery.podDisruptionBudget.enabled` instead).",
"type": "boolean",
"default": false
},
"config": {
- "description": "Disruption budget configuration.",
+ "description": "Disruption budget configuration
(deprecated, use `workers.celery.podDisruptionBudget.config` instead).",
"type": "object",
"additionalProperties": true,
"properties": {
"maxUnavailable": {
- "description": "Max unavailable pods for
worker.",
+ "description": "Max unavailable pods for
worker (deprecated, use
`workers.celery.podDisruptionBudget.config.maxUnavailable` instead).",
"type": [
"integer",
"string"
@@ -2252,7 +2252,7 @@
"default": 1
},
"minAvailable": {
- "description": "Min available pods for
worker.",
+ "description": "Min available pods for
worker (deprecated, use
`workers.celery.podDisruptionBudget.config.minAvailable` instead).",
"type": [
"integer",
"string"
@@ -2879,6 +2879,46 @@
}
]
},
+ "podDisruptionBudget": {
+ "description": "Airflow Celery worker pod
disruption budget.",
+ "type": "object",
+ "additionalProperties": true,
+ "properties": {
+ "enabled": {
+ "description": "Enable pod disruption
budget.",
+ "type": [
+ "boolean",
+ "null"
+ ],
+ "default": null
+ },
+ "config": {
+ "description": "Disruption budget
configuration.",
+ "type": "object",
+ "additionalProperties": true,
+ "properties": {
+ "maxUnavailable": {
+ "description": "Max unavailable
pods for worker.",
+ "type": [
+ "integer",
+ "string",
+ "null"
+ ],
+ "default": null
+ },
+ "minAvailable": {
+ "description": "Min available pods
for worker.",
+ "type": [
+ "integer",
+ "string",
+ "null"
+ ],
+ "default": null
+ }
+ }
+ }
+ }
+ },
"persistence": {
"description": "Persistence configuration for
Airflow Celery workers.",
"type": "object",
diff --git a/chart/values.yaml b/chart/values.yaml
index d7d5a009324..11c9d96c5af 100644
--- a/chart/values.yaml
+++ b/chart/values.yaml
@@ -703,14 +703,19 @@ workers:
# to separate value between Celery workers and pod-template-file.
containerLifecycleHooks: {}
- # Worker pod disruption budget
+ # Airflow Celery workers pod disruption budget
+ # (deprecated, use `workers.celery.podDisruptionBudget` instead)
podDisruptionBudget:
+ # (deprecated, use `workers.celery.podDisruptionBudget.enabled` instead)
enabled: false
# PDB configuration
+ # (deprecated, use `workers.celery.podDisruptionBudget.config` instead)
config:
# minAvailable and maxUnavailable are mutually exclusive
+ # (deprecated, use
`workers.celery.podDisruptionBudget.config.maxUnavailable` instead)
maxUnavailable: 1
+ # (deprecated, use
`workers.celery.podDisruptionBudget.config.minAvailable` instead)
# minAvailable: 1
# Create ServiceAccount for Airflow Celery workers and pods created with
pod-template-file
@@ -1110,6 +1115,16 @@ workers:
# Container level Lifecycle Hooks definition for Airflow Celery workers
containerLifecycleHooks: {}
+ # Airflow Celery workers pod disruption budget
+ podDisruptionBudget:
+ enabled: ~
+
+ # PDB configuration
+ config:
+ # minAvailable and maxUnavailable are mutually exclusive
+ maxUnavailable: ~
+ # minAvailable: ~
+
# Persistence volume configuration for Airflow Celery workers
persistence:
# Enable persistent volumes
diff --git a/helm-tests/tests/helm_tests/airflow_core/test_pdb_worker.py
b/helm-tests/tests/helm_tests/airflow_core/test_pdb_worker.py
index d0f9de671c1..b3ba1467910 100644
--- a/helm-tests/tests/helm_tests/airflow_core/test_pdb_worker.py
+++ b/helm-tests/tests/helm_tests/airflow_core/test_pdb_worker.py
@@ -17,41 +17,127 @@
from __future__ import annotations
import jmespath
+import pytest
from chart_utils.helm_template_generator import render_chart
class TestWorkerPdb:
"""Tests Worker PDB."""
- def test_should_pass_validation_with_just_pdb_enabled(self):
- render_chart(
- values={"workers": {"podDisruptionBudget": {"enabled": True}}},
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {"podDisruptionBudget": {"enabled": True}},
+ {"celery": {"podDisruptionBudget": {"enabled": True}}},
+ ],
+ )
+ def test_pod_disruption_budget_enabled(self, workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
)
- def test_should_add_component_specific_labels(self):
+ assert len(docs) == 1
+
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {"podDisruptionBudget": {"enabled": True}},
+ {"celery": {"podDisruptionBudget": {"enabled": True}}},
+ ],
+ )
+ def test_pod_disruption_budget_name(self, workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
+ show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
+ )
+
+ assert jmespath.search("metadata.name", docs[0]) ==
"release-name-worker-pdb"
+
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {"podDisruptionBudget": {"enabled": True}},
+ {"celery": {"podDisruptionBudget": {"enabled": True}}},
+ ],
+ )
+ def test_should_add_component_specific_labels(self, workers_values):
docs = render_chart(
values={
"workers": {
- "podDisruptionBudget": {"enabled": True},
+ **workers_values,
"labels": {"test_label": "test_label_value"},
},
},
show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
)
- assert "test_label" in jmespath.search("metadata.labels", docs[0])
assert jmespath.search("metadata.labels", docs[0])["test_label"] ==
"test_label_value"
- def
test_should_pass_validation_with_pdb_enabled_and_min_available_param(self):
- render_chart(
- values={
- "workers": {
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {"podDisruptionBudget": {"enabled": True}},
+ {"celery": {"podDisruptionBudget": {"enabled": True}}},
+ ],
+ )
+ def test_pod_disruption_budget_config_max_unavailable_default(self,
workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
+ show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
+ )
+
+ assert jmespath.search("spec.maxUnavailable", docs[0]) == 1
+ assert jmespath.search("spec.minAvailable", docs[0]) is None
+
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {"podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 2}}},
+ {"celery": {"podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 2}}}},
+ {
+ "podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 3}},
+ "celery": {"podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 2}}},
+ },
+ ],
+ )
+ def test_pod_disruption_budget_config_max_unavailable_overwrite(self,
workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
+ show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
+ )
+
+ assert jmespath.search("spec.maxUnavailable", docs[0]) == 2
+ assert jmespath.search("spec.minAvailable", docs[0]) is None
+
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {"podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": None, "minAvailable": 1}}},
+ {
+ "celery": {
"podDisruptionBudget": {
"enabled": True,
"config": {"maxUnavailable": None, "minAvailable": 1},
- },
+ }
}
},
+ {
+ "podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 2, "minAvailable": 3}},
+ "celery": {
+ "podDisruptionBudget": {
+ "enabled": True,
+ "config": {"maxUnavailable": None, "minAvailable": 1},
+ }
+ },
+ },
+ ],
+ )
+ def test_pod_disruption_budget_config_min_available_set(self,
workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
- ) # checks that no validation exception is raised
+ )
+
+ assert jmespath.search("spec.maxUnavailable", docs[0]) is None
+ assert jmespath.search("spec.minAvailable", docs[0]) == 1
diff --git a/helm-tests/tests/helm_tests/airflow_core/test_worker_sets.py
b/helm-tests/tests/helm_tests/airflow_core/test_worker_sets.py
index 72bd7c61e77..e242139c611 100644
--- a/helm-tests/tests/helm_tests/airflow_core/test_worker_sets.py
+++ b/helm-tests/tests/helm_tests/airflow_core/test_worker_sets.py
@@ -1038,8 +1038,7 @@ class TestWorkerSets:
docs = render_chart(
values={
"workers": {
- "celery": {"enableDefault": enable_default},
- "podDisruptionBudget": {"enabled": True},
+ "celery": {"enableDefault": enable_default,
"podDisruptionBudget": {"enabled": True}},
}
},
show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
@@ -1059,9 +1058,9 @@ class TestWorkerSets:
name="test",
values={
"workers": {
- "podDisruptionBudget": {"enabled": True},
"celery": {
"enableDefault": enable_default,
+ "podDisruptionBudget": {"enabled": True},
"sets": [
{"name": "set1"},
{"name": "set2"},
@@ -1089,38 +1088,82 @@ class TestWorkerSets:
assert docs[0] is not None
- def test_overwrite_pod_disruption_budget_disable(self):
- docs = render_chart(
- values={
- "workers": {
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {
+ "celery": {
+ "enableDefault": False,
+ "sets": [{"name": "test", "podDisruptionBudget":
{"enabled": False}}],
+ },
+ },
+ {
+ "podDisruptionBudget": {"enabled": True},
+ "celery": {
+ "enableDefault": False,
+ "sets": [{"name": "test", "podDisruptionBudget":
{"enabled": False}}],
+ },
+ },
+ {
+ "celery": {
+ "enableDefault": False,
"podDisruptionBudget": {"enabled": True},
- "celery": {
- "enableDefault": False,
- "sets": [{"name": "test", "podDisruptionBudget":
{"enabled": False}}],
- },
- }
+ "sets": [{"name": "test", "podDisruptionBudget":
{"enabled": False}}],
+ },
},
+ ],
+ )
+ def test_overwrite_pod_disruption_budget_disable(self, workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
)
assert len(docs) == 0
- def test_overwrite_pod_disruption_budget_config(self):
- docs = render_chart(
- values={
- "workers": {
+ @pytest.mark.parametrize(
+ "workers_values",
+ [
+ {
+ "celery": {
+ "enableDefault": False,
+ "sets": [
+ {
+ "name": "test",
+ "podDisruptionBudget": {"enabled": True, "config":
{"minAvailable": 1}},
+ }
+ ],
+ },
+ },
+ {
+ "podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 1}},
+ "celery": {
+ "enableDefault": False,
+ "sets": [
+ {
+ "name": "test",
+ "podDisruptionBudget": {"enabled": True, "config":
{"minAvailable": 1}},
+ }
+ ],
+ },
+ },
+ {
+ "celery": {
+ "enableDefault": False,
"podDisruptionBudget": {"enabled": True, "config":
{"maxUnavailable": 1}},
- "celery": {
- "enableDefault": False,
- "sets": [
- {
- "name": "test",
- "podDisruptionBudget": {"enabled": True,
"config": {"minAvailable": 1}},
- }
- ],
- },
- }
+ "sets": [
+ {
+ "name": "test",
+ "podDisruptionBudget": {"enabled": True, "config":
{"minAvailable": 1}},
+ }
+ ],
+ },
},
+ ],
+ )
+ def test_overwrite_pod_disruption_budget_config(self, workers_values):
+ docs = render_chart(
+ values={"workers": workers_values},
show_only=["templates/workers/worker-poddisruptionbudget.yaml"],
)