diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 82acba4b7..4a64a4d3a 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -32,7 +32,7 @@ from gns3server.config import Config from gns3server.utils.asyncio import locking from gns3server.compute.base_manager import BaseManager from gns3server.compute.docker.docker_vm import DockerVM -from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error +from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error, DockerHttp409Error log = logging.getLogger(__name__) @@ -240,6 +240,8 @@ class Docker(BaseManager): raise DockerHttp304Error(f"Docker has returned an error: {response.status} {body}") elif response.status == 404: raise DockerHttp404Error(f"Docker has returned an error: {response.status} {body}") + elif response.status == 409: + raise DockerHttp409Error(f"Docker has returned an error: {response.status} {body}") else: raise DockerError(f"Docker has returned an error: {response.status} {body}") return response diff --git a/gns3server/compute/docker/docker_error.py b/gns3server/compute/docker/docker_error.py index 298b9c3b8..0b77f778e 100644 --- a/gns3server/compute/docker/docker_error.py +++ b/gns3server/compute/docker/docker_error.py @@ -31,3 +31,7 @@ class DockerHttp304Error(DockerError): class DockerHttp404Error(DockerError): pass + + +class DockerHttp409Error(DockerError): + pass diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index a72c5173c..b78773203 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -41,7 +41,7 @@ from ..base_node import BaseNode from ..adapters.ethernet_adapter import EthernetAdapter from ..nios.nio_udp import NIOUDP -from .docker_error import DockerError, DockerHttp304Error, DockerHttp404Error +from .docker_error import DockerError, DockerHttp304Error, DockerHttp404Error, DockerHttp409Error import logging @@ -548,7 +548,28 @@ class DockerVM(BaseNode): params["Env"].append(f"GNS3_EXTRA_HOSTS={extra_hosts}") # Support name in Doker: [a-zA-Z0-9][a-zA-Z0-9_.-] - result = await self.manager.query("POST", f"containers/create?name={self.docker_name}", data=params) + try: + result = await self.manager.query("POST", f"containers/create?name={self.docker_name}", data=params) + except DockerHttp409Error: + # Container name already exists. This can happen when the server crashes + # and leaves containers behind. Try to remove the conflicting container. + log.warning(f"Container name '{self.docker_name}' is already in use, attempting to clean up the stale container...") + try: + # Try to get and remove the conflicting container + try: + container_info = await self.manager.query("GET", f"containers/{self.docker_name}/json") + container_id = container_info["Id"] + # Force remove the container + await self.manager.query("DELETE", f"containers/{container_id}", params={"force": 1, "v": 1}) + log.info(f"Removed stale container '{self.docker_name}' ({container_id})") + except DockerHttp404Error: + # Container doesn't exist anymore, race condition - just continue + pass + # Retry creating the container + result = await self.manager.query("POST", f"containers/create?name={self.docker_name}", data=params) + except DockerError as e: + log.error(f"Failed to clean up conflicting container '{self.docker_name}': {e}") + raise self._cid = result["Id"] log.info(f"Docker container '{self._name}' [{self._id}] created") if self._cpus > 0: