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 fb9ecc7ce90 Fix DockerOperator on_kill to respect auto_remove='force' 
and remove orphaned containers (#56149) (#67225)
fb9ecc7ce90 is described below

commit fb9ecc7ce90385ae9b626dc5fd46f6026d42b977
Author: 장병희 <[email protected]>
AuthorDate: Fri May 29 18:35:29 2026 +0900

    Fix DockerOperator on_kill to respect auto_remove='force' and remove 
orphaned containers (#56149) (#67225)
---
 .../airflow/providers/docker/operators/docker.py   |  9 +++++
 .../tests/unit/docker/operators/test_docker.py     | 46 ++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/providers/docker/src/airflow/providers/docker/operators/docker.py 
b/providers/docker/src/airflow/providers/docker/operators/docker.py
index e81579a4f1c..e87f8d08e16 100644
--- a/providers/docker/src/airflow/providers/docker/operators/docker.py
+++ b/providers/docker/src/airflow/providers/docker/operators/docker.py
@@ -534,6 +534,15 @@ class DockerOperator(BaseOperator):
                 self.log.info("Not attempting to kill container as it was not 
created")
                 return
             self.cli.stop(self.container["Id"])
+            if self.auto_remove == "force":
+                try:
+                    self.cli.remove_container(self.container["Id"], force=True)
+                except APIError as e:
+                    self.log.info(
+                        "Failed to remove docker container %s during on_kill; 
it may already be gone: %s",
+                        self.container["Id"],
+                        e,
+                    )
 
     @staticmethod
     def unpack_environment_variables(env_str: str) -> dict:
diff --git a/providers/docker/tests/unit/docker/operators/test_docker.py 
b/providers/docker/tests/unit/docker/operators/test_docker.py
index a753894d48f..81fd757ddf7 100644
--- a/providers/docker/tests/unit/docker/operators/test_docker.py
+++ b/providers/docker/tests/unit/docker/operators/test_docker.py
@@ -121,6 +121,52 @@ def test_on_kill_client_created(docker_api_client_patcher, 
container_exists):
         docker_api_client_patcher.return_value.stop.assert_not_called()
 
 
+def test_on_kill_auto_remove_force(docker_api_client_patcher):
+    """Test that on_kill removes the container when auto_remove='force'."""
+    op = DockerOperator(
+        image=TEST_IMAGE, hostname=TEST_DOCKER_URL, task_id="test_on_kill", 
auto_remove="force"
+    )
+    op.container = {"Id": "some_id"}
+
+    op.hook.get_conn()
+    op.on_kill()
+
+    
docker_api_client_patcher.return_value.stop.assert_called_once_with("some_id")
+    
docker_api_client_patcher.return_value.remove_container.assert_called_once_with("some_id",
 force=True)
+
+
[email protected]("auto_remove", ["success", "never"])
+def test_on_kill_auto_remove_not_force(docker_api_client_patcher, auto_remove):
+    """Test that on_kill does NOT remove the container when auto_remove is not 
'force'."""
+    op = DockerOperator(
+        image=TEST_IMAGE, hostname=TEST_DOCKER_URL, task_id="test_on_kill", 
auto_remove=auto_remove
+    )
+    op.container = {"Id": "some_id"}
+
+    op.hook.get_conn()
+    op.on_kill()
+
+    
docker_api_client_patcher.return_value.stop.assert_called_once_with("some_id")
+    docker_api_client_patcher.return_value.remove_container.assert_not_called()
+
+
+def test_on_kill_auto_remove_force_api_error(docker_api_client_patcher):
+    """Test that on_kill logs and swallows APIError when remove_container 
fails."""
+    docker_api_client_patcher.return_value.remove_container.side_effect = 
APIError("container already gone")
+
+    op = DockerOperator(
+        image=TEST_IMAGE, hostname=TEST_DOCKER_URL, task_id="test_on_kill", 
auto_remove="force"
+    )
+    op.container = {"Id": "some_id"}
+
+    op.hook.get_conn()
+    # Must not raise — on_kill is best-effort
+    op.on_kill()
+
+    
docker_api_client_patcher.return_value.stop.assert_called_once_with("some_id")
+    
docker_api_client_patcher.return_value.remove_container.assert_called_once_with("some_id",
 force=True)
+
+
 def test_on_kill_client_not_created(docker_api_client_patcher):
     """Test operator on_kill method if APIClient not created in case of 
error."""
     docker_api_client_patcher.side_effect = APIError("Fake Client Error")

Reply via email to