mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-05-15 12:20:08 +01:00
a30f2509a3
* 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>
254 lines
7.5 KiB
Python
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()
|