mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Add Docker host networking issue detection (#142259)
* Add Docker host networking issue detection * Update homeassistant/components/network/strings.json Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me> * Process review comments --------- Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
This commit is contained in:
parent
64e1735647
commit
69e241d2e6
@ -4,12 +4,14 @@ from __future__ import annotations
|
||||
|
||||
from ipaddress import IPv4Address, IPv6Address, ip_interface
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
||||
from homeassistant.helpers.typing import UNDEFINED, ConfigType, UndefinedType
|
||||
from homeassistant.loader import bind_hass
|
||||
from homeassistant.util import package
|
||||
|
||||
from . import util
|
||||
from .const import (
|
||||
@ -27,6 +29,19 @@ _LOGGER = logging.getLogger(__name__)
|
||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||
|
||||
|
||||
def _check_docker_without_host_networking() -> bool:
|
||||
"""Check if we are not using host networking in Docker."""
|
||||
if not package.is_docker_env():
|
||||
# We are not in Docker, so we don't need to check for host networking
|
||||
return True
|
||||
|
||||
if Path("/proc/sys/net/ipv4/ip_forward").exists():
|
||||
# If we can read this file, we likely have host networking
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@bind_hass
|
||||
async def async_get_adapters(hass: HomeAssistant) -> list[Adapter]:
|
||||
"""Get the network adapter configuration."""
|
||||
@ -166,5 +181,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
await async_get_network(hass)
|
||||
|
||||
if not await hass.async_add_executor_job(_check_docker_without_host_networking):
|
||||
docs_url = "https://docs.docker.com/network/network-tutorial-host/"
|
||||
install_url = "https://www.home-assistant.io/installation/linux#install-home-assistant-container"
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"docker_host_network",
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="docker_host_network",
|
||||
learn_more_url=install_url,
|
||||
translation_placeholders={"docs_url": docs_url, "install_url": install_url},
|
||||
)
|
||||
|
||||
async_register_websocket_commands(hass)
|
||||
return True
|
||||
|
@ -6,5 +6,11 @@
|
||||
"ipv6_addresses": "IPv6 addresses",
|
||||
"announce_addresses": "Announce addresses"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"docker_host_network": {
|
||||
"title": "Home Assistant is not using host networking",
|
||||
"description": "Home Assistant is running in a container without host networking mode. This can cause networking issues with device discovery, multicast, broadcast, other network features, and incorrectly detecting its own URL and IP addresses, causing issues with media players and sending audio responses to voice assistants.\n\nIt is recommended to run Home Assistant with host networking by adding the `--network host` flag to your Docker run command or setting `network_mode: host` in your `docker-compose.yml` file.\n\nSee the [Docker documentation]({docs_url}) for more information about Docker host networking and refer to the [Home Assistant installation guide]({install_url}) for our recommended and supported setup."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
tests/components/network/snapshots/test_init.ambr
Normal file
22
tests/components/network/snapshots/test_init.ambr
Normal file
@ -0,0 +1,22 @@
|
||||
# serializer version: 1
|
||||
# name: test_repair_docker_host_network_without_host_networking[mock_socket0]
|
||||
IssueRegistryItemSnapshot({
|
||||
'active': True,
|
||||
'breaks_in_ha_version': None,
|
||||
'created': <ANY>,
|
||||
'data': None,
|
||||
'dismissed_version': None,
|
||||
'domain': 'network',
|
||||
'is_fixable': False,
|
||||
'is_persistent': False,
|
||||
'issue_domain': None,
|
||||
'issue_id': 'docker_host_network',
|
||||
'learn_more_url': 'https://www.home-assistant.io/installation/linux#install-home-assistant-container',
|
||||
'severity': <IssueSeverity.WARNING: 'warning'>,
|
||||
'translation_key': 'docker_host_network',
|
||||
'translation_placeholders': dict({
|
||||
'docs_url': 'https://docs.docker.com/network/network-tutorial-host/',
|
||||
'install_url': 'https://www.home-assistant.io/installation/linux#install-home-assistant-container',
|
||||
}),
|
||||
})
|
||||
# ---
|
@ -1,10 +1,13 @@
|
||||
"""Test the Network Configuration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ipaddress import IPv4Address
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components import network
|
||||
from homeassistant.components.network.const import (
|
||||
@ -17,6 +20,7 @@ from homeassistant.components.network.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import LOOPBACK_IPADDR, NO_LOOPBACK_IPADDR
|
||||
@ -801,3 +805,48 @@ async def test_websocket_network_url(
|
||||
"external": None,
|
||||
"cloud": None,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mock_socket", [[]], indirect=True)
|
||||
@pytest.mark.usefixtures("mock_socket")
|
||||
async def test_repair_docker_host_network_not_docker(
|
||||
hass: HomeAssistant, issue_registry: ir.IssueRegistry
|
||||
) -> None:
|
||||
"""Test repair is not created when not in Docker."""
|
||||
with patch("homeassistant.util.package.is_docker_env", return_value=False):
|
||||
assert await async_setup_component(hass, "network", {})
|
||||
|
||||
assert not issue_registry.async_get_issue(DOMAIN, "docker_host_network")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mock_socket", [[]], indirect=True)
|
||||
@pytest.mark.usefixtures("mock_socket")
|
||||
async def test_repair_docker_host_network_with_host_networking(
|
||||
hass: HomeAssistant, issue_registry: ir.IssueRegistry
|
||||
) -> None:
|
||||
"""Test repair is not created when in Docker with host networking."""
|
||||
with (
|
||||
patch("homeassistant.util.package.is_docker_env", return_value=True),
|
||||
patch("homeassistant.components.network.Path.exists", return_value=True),
|
||||
):
|
||||
assert await async_setup_component(hass, "network", {})
|
||||
|
||||
assert not issue_registry.async_get_issue(DOMAIN, "docker_host_network")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mock_socket", [[]], indirect=True)
|
||||
@pytest.mark.usefixtures("mock_socket")
|
||||
async def test_repair_docker_host_network_without_host_networking(
|
||||
hass: HomeAssistant,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test repair is created when in Docker without host networking."""
|
||||
with (
|
||||
patch("homeassistant.util.package.is_docker_env", return_value=True),
|
||||
patch("homeassistant.components.network.Path.exists", return_value=False),
|
||||
):
|
||||
assert await async_setup_component(hass, "network", {})
|
||||
|
||||
assert (issue := issue_registry.async_get_issue(DOMAIN, "docker_host_network"))
|
||||
assert issue == snapshot
|
||||
|
Loading…
x
Reference in New Issue
Block a user