mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-05-19 14:18:53 +01:00
4938fb215d
Triaging SUPERVISOR-1JWK turned up a missed port conflict: RE_PORT_CONFLICT_ERROR only matched one of the Docker daemon's port-in-use message shapes. The two variants produced by current moby — "Bind for <ip>:<port> failed: port is already allocated" from portallocator and "failed to bind host port <ip>:<port>/<proto>: address already in use" from osallocator — fell through to DockerAPIError, got re-raised as AppUnknownError, and the watchdog shipped them to Sentry as unknown errors. Widen the regex to match all known shapes (including the older form embedding the container endpoint, still observed from older daemons and wrappers), anchored on the "failed to set up container networking" prefix and one of the "address already in use" or "port is already allocated" suffixes. Log the raw Docker message at debug level before converting, so curious users can still see the exact upstream text (host IP, container endpoint, protocol) when investigating which process is holding the port. The watchdog's _restart_after_problem now catches AppPortConflict explicitly ahead of the generic AppsError handler: log a warning, break the retry loop, do not call async_capture_exception. A port conflict is an environment condition — another process grabbed the port while the add-on was down — so retrying cannot make it succeed and reporting to Sentry is noise. With port conflicts now raised as typed APIError subclasses at the detection site, the DockerAPIError → format_message() rewrite fallback in api_return_error has no work left. Drop the fallback and delete supervisor/utils/log_format.py along with its tests; the module only ever handled port-conflict prose. Fixes SUPERVISOR-1JWK Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
38 lines
1.7 KiB
Python
38 lines
1.7 KiB
Python
"""Test observer plugin."""
|
|
|
|
from http import HTTPStatus
|
|
|
|
import aiodocker
|
|
import pytest
|
|
|
|
from supervisor.coresys import CoreSys
|
|
from supervisor.exceptions import ObserverPortConflict
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"docker_message",
|
|
[
|
|
"failed to set up container networking: driver failed programming external connectivity on endpoint hassio_observer (ea4d0fdaa72cf86f2c9199a04208e3eaf0c5a0d6fd34b3c7f4fab2daadb1f3a9): failed to bind host port for 0.0.0.0:4357:172.30.33.4:80/tcp: address already in use",
|
|
"failed to set up container networking: driver failed programming external connectivity on endpoint hassio_observer (ea4d0fdaa72cf86f2c9199a04208e3eaf0c5a0d6fd34b3c7f4fab2daadb1f3a9): Bind for 0.0.0.0:4357 failed: port is already allocated",
|
|
"failed to set up container networking: driver failed programming external connectivity on endpoint hassio_observer (ea4d0fdaa72cf86f2c9199a04208e3eaf0c5a0d6fd34b3c7f4fab2daadb1f3a9): failed to bind host port 0.0.0.0:4357/tcp: address already in use",
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("container", "tmp_supervisor_data", "path_extern")
|
|
async def test_observer_start_port_conflict(
|
|
coresys: CoreSys, caplog: pytest.LogCaptureFixture, docker_message: str
|
|
):
|
|
"""Test port conflict error when trying to start observer."""
|
|
coresys.docker.containers.create.return_value.start.side_effect = (
|
|
aiodocker.DockerError(HTTPStatus.INTERNAL_SERVER_ERROR, docker_message)
|
|
)
|
|
await coresys.plugins.observer.load()
|
|
|
|
caplog.clear()
|
|
with pytest.raises(ObserverPortConflict):
|
|
await coresys.plugins.observer.start()
|
|
|
|
assert (
|
|
"Cannot start container hassio_observer because port 4357 is already in use"
|
|
in caplog.text
|
|
)
|