This is an automated email from the ASF dual-hosted git repository.

potiuk 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 72f6173c1b2 Fix KubernetesPodOperator XCom loss when container_logs is 
a string (#72502)
72f6173c1b2 is described below

commit 72f6173c1b2a99592d833144c36dc3d9e2bc7229
Author: Henry Chen <[email protected]>
AuthorDate: Wed Sep 9 06:38:48 2026 +0800

    Fix KubernetesPodOperator XCom loss when container_logs is a string (#72502)
    
    When container_logs is a single container name, the check that decides
    whether the base container still needs to be awaited used `in` on a
    string, which is a substring match. Any name containing the base
    container name (a typo like "base2", or a sidecar like "base-metrics")
    made the operator believe the base container's logs were being followed
    and skip the explicit wait. XCom extraction then ran while the base
    container was still running, read an empty result, tore down the
    sidecar, and the task succeeded with a None XCom.
---
 .../providers/cncf/kubernetes/operators/pod.py     |  5 ++++-
 .../unit/cncf/kubernetes/operators/test_pod.py     | 22 ++++++++++++++++++++++
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git 
a/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/operators/pod.py
 
b/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/operators/pod.py
index 5a9df5645aa..1656b8a825e 100644
--- 
a/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/operators/pod.py
+++ 
b/providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/operators/pod.py
@@ -922,8 +922,11 @@ class KubernetesPodOperator(BaseOperator):
                     
container_name_log_prefix_enabled=self.container_name_log_prefix_enabled,
                     log_formatter=self.log_formatter,
                 )
+            followed_containers = (
+                [self.container_logs] if isinstance(self.container_logs, str) 
else self.container_logs
+            )
             if not self.get_logs or (
-                self.container_logs is not True and self.base_container_name 
not in self.container_logs
+                followed_containers is not True and self.base_container_name 
not in followed_containers
             ):
                 self.pod_manager.await_container_completion(
                     pod=pod,
diff --git 
a/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/operators/test_pod.py 
b/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/operators/test_pod.py
index 15a37133c3e..154af0708e2 100644
--- a/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/operators/test_pod.py
+++ b/providers/cncf/kubernetes/tests/unit/cncf/kubernetes/operators/test_pod.py
@@ -2126,6 +2126,28 @@ class TestKubernetesPodOperator:
         # check that we wait for the xcom sidecar to start before extracting 
XCom
         mock_await_xcom_sidecar.assert_called_once_with(pod=pod)
 
+    @pytest.mark.parametrize(
+        ("container_logs", "should_await_base"),
+        [
+            pytest.param("base", False, id="base-as-string"),
+            pytest.param("base2", True, 
id="base-is-substring-of-other-container"),
+        ],
+    )
+    @patch(f"{POD_MANAGER_CLASS}.await_container_completion")
+    @patch(f"{POD_MANAGER_CLASS}.fetch_requested_container_logs")
+    def 
test_string_container_logs_matches_base_container_by_name_not_substring(
+        self, mock_fetch_log, mock_await_container_completion, container_logs, 
should_await_base
+    ):
+        k = KubernetesPodOperator(task_id="task", get_logs=True, 
container_logs=container_logs)
+        pod, _ = self.run_pod(k)
+
+        if should_await_base:
+            mock_await_container_completion.assert_called_once_with(
+                pod=pod, container_name="base", polling_time=1
+            )
+        else:
+            mock_await_container_completion.assert_not_called()
+
     @patch(HOOK_CLASS, new=MagicMock)
     @patch(KUB_OP_PATH.format("find_pod"))
     def test_execute_sync_callbacks(self, find_pod_mock):

Reply via email to