Enable IPv6 by default for new installations

Enable IPv6 by default for new Supervisor installations. Let's also
make the `enable_ipv6` attribute nullable, so we can distinguish
between "not set" and "set to false".
This commit is contained in:
Stefan Agner 2025-07-16 13:01:39 +02:00
parent 780ae1e15c
commit d2cc165387
No known key found for this signature in database
GPG Key ID: AE01353D1E44747D
4 changed files with 22 additions and 9 deletions

View File

@ -32,7 +32,7 @@ SCHEMA_DOCKER_REGISTRY = vol.Schema(
) )
# pylint: disable=no-value-for-parameter # pylint: disable=no-value-for-parameter
SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_ENABLE_IPV6): vol.Boolean()}) SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_ENABLE_IPV6): vol.Maybe(vol.Boolean())})
class APIDocker(CoreSysAttributes): class APIDocker(CoreSysAttributes):

View File

@ -95,12 +95,12 @@ class DockerConfig(FileConfiguration):
super().__init__(FILE_HASSIO_DOCKER, SCHEMA_DOCKER_CONFIG) super().__init__(FILE_HASSIO_DOCKER, SCHEMA_DOCKER_CONFIG)
@property @property
def enable_ipv6(self) -> bool: def enable_ipv6(self) -> bool | None:
"""Return IPv6 configuration for docker network.""" """Return IPv6 configuration for docker network."""
return self._data.get(ATTR_ENABLE_IPV6, False) return self._data.get(ATTR_ENABLE_IPV6, None)
@enable_ipv6.setter @enable_ipv6.setter
def enable_ipv6(self, value: bool) -> None: def enable_ipv6(self, value: bool | None) -> None:
"""Set IPv6 configuration for docker network.""" """Set IPv6 configuration for docker network."""
self._data[ATTR_ENABLE_IPV6] = value self._data[ATTR_ENABLE_IPV6] = value

View File

@ -47,6 +47,8 @@ DOCKER_NETWORK_PARAMS = {
"options": {"com.docker.network.bridge.name": DOCKER_NETWORK}, "options": {"com.docker.network.bridge.name": DOCKER_NETWORK},
} }
DOCKER_ENABLE_IPV6_DEFAULT = True
class DockerNetwork: class DockerNetwork:
"""Internal Supervisor Network. """Internal Supervisor Network.
@ -59,7 +61,7 @@ class DockerNetwork:
self.docker: docker.DockerClient = docker_client self.docker: docker.DockerClient = docker_client
self._network: docker.models.networks.Network self._network: docker.models.networks.Network
async def post_init(self, enable_ipv6: bool = False) -> Self: async def post_init(self, enable_ipv6: bool | None = None) -> Self:
"""Post init actions that must be done in event loop.""" """Post init actions that must be done in event loop."""
self._network = await asyncio.get_running_loop().run_in_executor( self._network = await asyncio.get_running_loop().run_in_executor(
None, self._get_network, enable_ipv6 None, self._get_network, enable_ipv6
@ -111,16 +113,24 @@ class DockerNetwork:
"""Return observer of the network.""" """Return observer of the network."""
return DOCKER_IPV4_NETWORK_MASK[6] return DOCKER_IPV4_NETWORK_MASK[6]
def _get_network(self, enable_ipv6: bool = False) -> docker.models.networks.Network: def _get_network(
self, enable_ipv6: bool | None = None
) -> docker.models.networks.Network:
"""Get supervisor network.""" """Get supervisor network."""
try: try:
if network := self.docker.networks.get(DOCKER_NETWORK): if network := self.docker.networks.get(DOCKER_NETWORK):
if network.attrs.get(DOCKER_ENABLEIPV6) == enable_ipv6: current_ipv6 = network.attrs.get(DOCKER_ENABLEIPV6, False)
# If the network exists and we don't have an explicit setting,
# simply stick with what we have.
if enable_ipv6 is None or current_ipv6 == enable_ipv6:
return network return network
# We have an explicit setting which differs from the current state.
_LOGGER.info( _LOGGER.info(
"Migrating Supervisor network to %s", "Migrating Supervisor network to %s",
"IPv4/IPv6 Dual-Stack" if enable_ipv6 else "IPv4-Only", "IPv4/IPv6 Dual-Stack" if enable_ipv6 else "IPv4-Only",
) )
if (containers := network.containers) and ( if (containers := network.containers) and (
containers_all := all( containers_all := all(
container.name in (OBSERVER_DOCKER_NAME, SUPERVISOR_DOCKER_NAME) container.name in (OBSERVER_DOCKER_NAME, SUPERVISOR_DOCKER_NAME)
@ -134,6 +144,7 @@ class DockerNetwork:
requests.RequestException, requests.RequestException,
): ):
network.disconnect(container, force=True) network.disconnect(container, force=True)
if not containers or containers_all: if not containers or containers_all:
try: try:
network.remove() network.remove()
@ -151,7 +162,9 @@ class DockerNetwork:
_LOGGER.info("Can't find Supervisor network, creating a new network") _LOGGER.info("Can't find Supervisor network, creating a new network")
network_params = DOCKER_NETWORK_PARAMS.copy() network_params = DOCKER_NETWORK_PARAMS.copy()
network_params[ATTR_ENABLE_IPV6] = enable_ipv6 network_params[ATTR_ENABLE_IPV6] = (
DOCKER_ENABLE_IPV6_DEFAULT if enable_ipv6 is None else enable_ipv6
)
try: try:
self._network = self.docker.networks.create(**network_params) # type: ignore self._network = self.docker.networks.create(**network_params) # type: ignore

View File

@ -182,7 +182,7 @@ SCHEMA_DOCKER_CONFIG = vol.Schema(
} }
} }
), ),
vol.Optional(ATTR_ENABLE_IPV6): vol.Boolean(), vol.Optional(ATTR_ENABLE_IPV6, default=None): vol.Maybe(vol.Boolean()),
} }
) )