From acc26364cc98e130db867c6606fcc29476d9ec90 Mon Sep 17 00:00:00 2001 From: Filipe Utzig Date: Thu, 12 Sep 2024 16:42:29 -0300 Subject: [PATCH] Remove and return container when start fails When the container fail to start and the user has set the `remove` flag we should remove the containter, also if the flag is not set, we lose track of the container, leaving it dangling on the system. By adding this execption return whenever the container fails to start we provide means to the user to remove the container if needed. Signed-off-by: Filipe Utzig --- docker/errors.py | 13 +++++++++++++ docker/models/containers.py | 16 +++++++++++++++- tests/integration/models_containers_test.py | 6 +++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/docker/errors.py b/docker/errors.py index d03e10f69..8b2762abe 100644 --- a/docker/errors.py +++ b/docker/errors.py @@ -150,6 +150,19 @@ def __init__(self, container, exit_status, command, image, stderr): ) +class ContainerStartError(DockerException): + """ + Represents a container that has failed to start. + """ + def __init__(self, container, reason): + self.container = container + self.msg = reason + + super().__init__( + f"Container '{container.short_id}' failed to start: {reason}" + ) + + class StreamParseError(RuntimeError): def __init__(self, reason): self.msg = reason diff --git a/docker/models/containers.py b/docker/models/containers.py index 4795523a1..99d614b7d 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -5,7 +5,9 @@ from ..api import APIClient from ..constants import DEFAULT_DATA_CHUNK_SIZE from ..errors import ( + APIError, ContainerError, + ContainerStartError, DockerException, ImageNotFound, NotFound, @@ -842,6 +844,8 @@ def run(self, image, command=None, stdout=True, stderr=False, :py:class:`docker.errors.ContainerError` If the container exits with a non-zero exit code and ``detach`` is ``False``. + :py:class:`docker.errors.ContainerStartError` + If the container fails to start. :py:class:`docker.errors.ImageNotFound` If the specified image does not exist. :py:class:`docker.errors.APIError` @@ -880,7 +884,17 @@ def run(self, image, command=None, stdout=True, stderr=False, container = self.create(image=image, command=command, detach=detach, **kwargs) - container.start() + try: + container.start() + except APIError as e: + if remove: + container.remove() + + if e.explanation: + error = e.explanation + else: + error = e + raise ContainerStartError(container, error) from e if detach: return container diff --git a/tests/integration/models_containers_test.py b/tests/integration/models_containers_test.py index 476263ae2..478c1bd34 100644 --- a/tests/integration/models_containers_test.py +++ b/tests/integration/models_containers_test.py @@ -158,13 +158,13 @@ def test_run_with_networking_config_with_undeclared_network(self): ), } - with pytest.raises(docker.errors.APIError): - container = client.containers.run( + with pytest.raises(docker.errors.ContainerStartError) as err: + client.containers.run( 'alpine', 'echo hello world', network=net_name, networking_config=networking_config, detach=True ) - self.tmp_containers.append(container.id) + self.tmp_containers.append(err.container.id) def test_run_with_networking_config_only_undeclared_network(self): net_name = random_name()