1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2026-05-15 12:20:08 +01:00
Files
supervisor/tests/docker/test_network.py
T
Stefan Agner a30f2509a3 Enable IPv6 on Supervisor network by default for all installations (#6720)
* Improve Docker network test coverage and infrastructure

Add test cases for enable_ipv6=None (no user setting) to
test_network_recreation, verifying existing behavior where None
leaves the network unchanged. Use pytest.param with descriptive IDs
for better test readability.

Add create_network_mock side_effect to the docker fixture so network
creation returns realistic metadata built from the provided params.
Remove redundant manual create mock setups from individual tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Enable IPv6 on Supervisor network by default for all installations

Previously, IPv6 was only enabled by default for new installations
(when enable_ipv6 config was None). Existing installations with
IPv4-only networks were left unchanged unless the user explicitly
set enable_ipv6 to true.

Now, when no explicit IPv6 setting exists, the network is migrated
to dual-stack on next boot. The same safety checks apply: migration
is blocked if user containers are running and requires a reboot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 18:08:30 +02:00

254 lines
7.5 KiB
Python

"""Test Internal network manager for Supervisor."""
from http import HTTPStatus
from unittest.mock import MagicMock
import aiodocker
from aiodocker.networks import DockerNetwork as AiodockerNetwork
import pytest
from supervisor.const import (
DOCKER_NETWORK,
OBSERVER_DOCKER_NAME,
SUPERVISOR_DOCKER_NAME,
)
from supervisor.docker.manager import DockerAPI
from supervisor.docker.network import (
DOCKER_ENABLEIPV6,
DOCKER_NETWORK_PARAMS,
DockerNetwork,
)
@pytest.mark.parametrize(
(
"delete_error",
"disconnect_error",
"containers",
"old_enable_ipv6",
"new_enable_ipv6",
"create_expected",
),
[
pytest.param(
None,
None,
[OBSERVER_DOCKER_NAME, SUPERVISOR_DOCKER_NAME],
False,
True,
True,
id="ipv6_off-enable-system_only-recreated",
),
pytest.param(
None,
None,
[OBSERVER_DOCKER_NAME, SUPERVISOR_DOCKER_NAME],
True,
False,
True,
id="ipv6_on-disable-system_only-recreated",
),
pytest.param(
None,
None,
["test_container"],
False,
True,
False,
id="ipv6_off-enable-user_running-blocked",
),
pytest.param(
None,
None,
[],
False,
True,
True,
id="ipv6_off-enable-no_containers-recreated",
),
pytest.param(
None,
aiodocker.DockerError(
HTTPStatus.INTERNAL_SERVER_ERROR,
{"message": "Simulated disconnect error"},
),
[OBSERVER_DOCKER_NAME, SUPERVISOR_DOCKER_NAME],
False,
True,
False,
id="ipv6_off-enable-disconnect_error-blocked",
),
pytest.param(
aiodocker.DockerError(
HTTPStatus.INTERNAL_SERVER_ERROR,
{"message": "Simulated removal error"},
),
None,
[],
False,
True,
False,
id="ipv6_off-enable-delete_error-blocked",
),
pytest.param(
None,
None,
[],
True,
True,
False,
id="ipv6_on-enable-no_change",
),
pytest.param(
None,
None,
[],
False,
None,
True,
id="ipv6_off-no_setting-migrated",
),
pytest.param(
None,
None,
[],
True,
None,
False,
id="ipv6_on-no_setting-no_change",
),
pytest.param(
None,
None,
[OBSERVER_DOCKER_NAME, SUPERVISOR_DOCKER_NAME],
False,
None,
True,
id="ipv6_off-no_setting-system_only-migrated",
),
pytest.param(
None,
None,
["test_container"],
False,
None,
False,
id="ipv6_off-no_setting-user_running-blocked",
),
],
)
async def test_network_recreation(
docker: DockerAPI,
delete_error: aiodocker.DockerError | None,
disconnect_error: aiodocker.DockerError | None,
containers: list[str],
old_enable_ipv6: bool,
new_enable_ipv6: bool,
create_expected: bool,
):
"""Test network recreation with IPv6 enabled/disabled."""
docker.docker.networks.reset_mock()
docker.docker.networks.get.return_value = mock_network = MagicMock(
spec=AiodockerNetwork, id="test123"
)
mock_network.show.return_value = {
"Containers": {name: {"Id": name, "Name": name} for name in containers},
DOCKER_ENABLEIPV6: old_enable_ipv6,
}
mock_network.delete.side_effect = delete_error
mock_network.disconnect.side_effect = disconnect_error
docker_network = await DockerNetwork(docker.docker).post_init(new_enable_ipv6)
docker.docker.networks.get.assert_called_with(DOCKER_NETWORK)
assert docker_network.network
assert docker_network.network_meta
if not create_expected:
assert docker_network.network_meta[DOCKER_ENABLEIPV6] is old_enable_ipv6
docker.docker.networks.create.assert_not_called()
else:
expected_params = DOCKER_NETWORK_PARAMS.copy()
if new_enable_ipv6 is not None:
expected_params[DOCKER_ENABLEIPV6] = new_enable_ipv6
docker.docker.networks.create.assert_called_once_with(expected_params)
async def test_network_default_ipv6_for_new_installations(docker: DockerAPI):
"""Test that IPv6 is enabled by default when no user setting is provided (None)."""
docker.docker.networks.reset_mock()
docker.docker.networks.get.side_effect = aiodocker.DockerError(
HTTPStatus.NOT_FOUND, {"message": "Network not found"}
)
# Pass None as enable_ipv6 to simulate no user setting
docker_network = await DockerNetwork(docker.docker).post_init(None)
assert docker_network.network
assert docker_network.network_meta
assert docker_network.network_meta[DOCKER_ENABLEIPV6] is True
# Verify that create was called with default params (IPv6 enabled)
docker.docker.networks.create.assert_called_with(DOCKER_NETWORK_PARAMS)
async def test_network_mtu_recreation(docker: DockerAPI):
"""Test network recreation with different MTU settings."""
docker.docker.networks.reset_mock()
docker.docker.networks.get.return_value = mock_network = MagicMock(
spec=AiodockerNetwork, id="test123"
)
mock_network.show.return_value = {
DOCKER_ENABLEIPV6: False,
"Containers": {},
"Options": {"com.docker.network.driver.mtu": "1500"},
}
# Set new MTU to 1450
docker_network = await DockerNetwork(docker.docker).post_init(True, 1450)
docker.docker.networks.get.assert_called_with(DOCKER_NETWORK)
assert docker_network.network
assert docker_network.network_meta
assert docker_network.network_meta[DOCKER_ENABLEIPV6] is True
assert (
docker_network.network_meta["Options"]["com.docker.network.driver.mtu"]
== "1450"
)
# Verify network was recreated with new MTU
expected_options = DOCKER_NETWORK_PARAMS["Options"] | {
"com.docker.network.driver.mtu": "1450"
}
docker.docker.networks.create.assert_called_with(
DOCKER_NETWORK_PARAMS | {DOCKER_ENABLEIPV6: True, "Options": expected_options}
)
async def test_network_mtu_no_change(docker: DockerAPI):
"""Test that network is not recreated when MTU hasn't changed."""
docker.docker.networks.reset_mock()
docker.docker.networks.get.return_value = mock_network = MagicMock(
spec=AiodockerNetwork, id="test123"
)
mock_network.show.return_value = {
DOCKER_ENABLEIPV6: True,
"Containers": {},
"Options": {"com.docker.network.driver.mtu": "1450"},
}
# Set same MTU (1450)
docker_network = await DockerNetwork(docker.docker).post_init(True, 1450)
docker.docker.networks.get.assert_called_with(DOCKER_NETWORK)
assert docker_network.network
assert docker_network.network_meta
assert docker_network.network_meta[DOCKER_ENABLEIPV6] is True
assert (
docker_network.network_meta["Options"]["com.docker.network.driver.mtu"]
== "1450"
)
# Verify network was NOT recreated since MTU is the same
docker.docker.networks.create.assert_not_called()