mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-28 19:46:29 +00:00
Make check_port an async function (#4677)
* Make check_port asyncio This requires to change the ingress_port property to a async method. * Avoid using wait_for * Add missing async * Really await * Set dynamic ingress port on add-on installation/update * Fix pytest issue * Rename async_check_port back to check_port * Raise RuntimeError in case port is not set * Make sure port gets set on add-on restore * Drop unnecessary async * Simplify check_port by using asyncio.get_running_loop()
This commit is contained in:
parent
c2d4be3304
commit
883e54f989
@ -395,7 +395,7 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
port = self.data[ATTR_INGRESS_PORT]
|
port = self.data[ATTR_INGRESS_PORT]
|
||||||
if port == 0:
|
if port == 0:
|
||||||
return self.sys_ingress.get_dynamic_port(self.slug)
|
raise RuntimeError(f"No port set for add-on {self.slug}")
|
||||||
return port
|
return port
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -539,7 +539,7 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
# TCP monitoring
|
# TCP monitoring
|
||||||
if s_prefix == "tcp":
|
if s_prefix == "tcp":
|
||||||
return await self.sys_run_in_executor(check_port, self.ip_address, port)
|
return await check_port(self.ip_address, port)
|
||||||
|
|
||||||
# lookup the correct protocol from config
|
# lookup the correct protocol from config
|
||||||
if t_proto:
|
if t_proto:
|
||||||
@ -602,6 +602,16 @@ class Addon(AddonModel):
|
|||||||
_LOGGER.info("Removing add-on data folder %s", self.path_data)
|
_LOGGER.info("Removing add-on data folder %s", self.path_data)
|
||||||
await remove_data(self.path_data)
|
await remove_data(self.path_data)
|
||||||
|
|
||||||
|
async def _check_ingress_port(self):
|
||||||
|
"""Assign a ingress port if dynamic port selection is used."""
|
||||||
|
if not self.with_ingress:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.data[ATTR_INGRESS_PORT] == 0:
|
||||||
|
self.data[ATTR_INGRESS_PORT] = await self.sys_ingress.get_dynamic_port(
|
||||||
|
self.slug
|
||||||
|
)
|
||||||
|
|
||||||
@Job(
|
@Job(
|
||||||
name="addon_install",
|
name="addon_install",
|
||||||
limit=JobExecutionLimit.GROUP_ONCE,
|
limit=JobExecutionLimit.GROUP_ONCE,
|
||||||
@ -630,6 +640,8 @@ class Addon(AddonModel):
|
|||||||
self.sys_addons.data.uninstall(self)
|
self.sys_addons.data.uninstall(self)
|
||||||
raise AddonsError() from err
|
raise AddonsError() from err
|
||||||
|
|
||||||
|
await self._check_ingress_port()
|
||||||
|
|
||||||
# Add to addon manager
|
# Add to addon manager
|
||||||
self.sys_addons.local[self.slug] = self
|
self.sys_addons.local[self.slug] = self
|
||||||
|
|
||||||
@ -716,6 +728,7 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
_LOGGER.info("Add-on '%s' successfully updated", self.slug)
|
_LOGGER.info("Add-on '%s' successfully updated", self.slug)
|
||||||
self.sys_addons.data.update(store)
|
self.sys_addons.data.update(store)
|
||||||
|
await self._check_ingress_port()
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
with suppress(DockerError):
|
with suppress(DockerError):
|
||||||
@ -756,6 +769,7 @@ class Addon(AddonModel):
|
|||||||
raise AddonsError() from err
|
raise AddonsError() from err
|
||||||
|
|
||||||
self.sys_addons.data.update(self.addon_store)
|
self.sys_addons.data.update(self.addon_store)
|
||||||
|
await self._check_ingress_port()
|
||||||
_LOGGER.info("Add-on '%s' successfully rebuilt", self.slug)
|
_LOGGER.info("Add-on '%s' successfully rebuilt", self.slug)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
@ -1199,6 +1213,7 @@ class Addon(AddonModel):
|
|||||||
_LOGGER.info("Restore/Update of image for addon %s", self.slug)
|
_LOGGER.info("Restore/Update of image for addon %s", self.slug)
|
||||||
with suppress(DockerError):
|
with suppress(DockerError):
|
||||||
await self.instance.update(version, restore_image)
|
await self.instance.update(version, restore_image)
|
||||||
|
self._check_ingress_port()
|
||||||
|
|
||||||
# Restore data and config
|
# Restore data and config
|
||||||
def _restore_data():
|
def _restore_data():
|
||||||
|
@ -140,8 +140,7 @@ class HomeAssistantAPI(CoreSysAttributes):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Check if port is up
|
# Check if port is up
|
||||||
if not await self.sys_run_in_executor(
|
if not await check_port(
|
||||||
check_port,
|
|
||||||
self.sys_homeassistant.ip_address,
|
self.sys_homeassistant.ip_address,
|
||||||
self.sys_homeassistant.api_port,
|
self.sys_homeassistant.api_port,
|
||||||
):
|
):
|
||||||
|
@ -154,7 +154,7 @@ class Ingress(FileConfiguration, CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_dynamic_port(self, addon_slug: str) -> int:
|
async def get_dynamic_port(self, addon_slug: str) -> int:
|
||||||
"""Get/Create a dynamic port from range."""
|
"""Get/Create a dynamic port from range."""
|
||||||
if addon_slug in self.ports:
|
if addon_slug in self.ports:
|
||||||
return self.ports[addon_slug]
|
return self.ports[addon_slug]
|
||||||
@ -163,7 +163,7 @@ class Ingress(FileConfiguration, CoreSysAttributes):
|
|||||||
while (
|
while (
|
||||||
port is None
|
port is None
|
||||||
or port in self.ports.values()
|
or port in self.ports.values()
|
||||||
or check_port(self.sys_docker.network.gateway, port)
|
or await check_port(self.sys_docker.network.gateway, port)
|
||||||
):
|
):
|
||||||
port = random.randint(62000, 65500)
|
port = random.randint(62000, 65500)
|
||||||
|
|
||||||
|
@ -39,20 +39,19 @@ def process_lock(method):
|
|||||||
return wrap_api
|
return wrap_api
|
||||||
|
|
||||||
|
|
||||||
def check_port(address: IPv4Address, port: int) -> bool:
|
async def check_port(address: IPv4Address, port: int) -> bool:
|
||||||
"""Check if port is mapped."""
|
"""Check if port is mapped."""
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(0.5)
|
sock.setblocking(False)
|
||||||
try:
|
try:
|
||||||
result = sock.connect_ex((str(address), port))
|
async with asyncio.timeout(0.5):
|
||||||
sock.close()
|
await asyncio.get_running_loop().sock_connect(sock, (str(address), port))
|
||||||
|
except (OSError, TimeoutError):
|
||||||
# Check if the port is available
|
return False
|
||||||
if result == 0:
|
finally:
|
||||||
return True
|
if sock is not None:
|
||||||
except OSError:
|
sock.close()
|
||||||
pass
|
return True
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def check_exception_chain(err: Exception, object_type: Any) -> bool:
|
def check_exception_chain(err: Exception, object_type: Any) -> bool:
|
||||||
|
@ -50,15 +50,15 @@ async def test_save_on_unload(coresys: CoreSys):
|
|||||||
assert coresys.ingress.save_data.called
|
assert coresys.ingress.save_data.called
|
||||||
|
|
||||||
|
|
||||||
def test_dynamic_ports(coresys: CoreSys):
|
async def test_dynamic_ports(coresys: CoreSys):
|
||||||
"""Test dyanmic port handling."""
|
"""Test dyanmic port handling."""
|
||||||
port_test1 = coresys.ingress.get_dynamic_port("test1")
|
port_test1 = await coresys.ingress.get_dynamic_port("test1")
|
||||||
|
|
||||||
assert port_test1
|
assert port_test1
|
||||||
assert coresys.ingress.save_data.called
|
assert coresys.ingress.save_data.called
|
||||||
assert port_test1 == coresys.ingress.get_dynamic_port("test1")
|
assert port_test1 == await coresys.ingress.get_dynamic_port("test1")
|
||||||
|
|
||||||
port_test2 = coresys.ingress.get_dynamic_port("test2")
|
port_test2 = await coresys.ingress.get_dynamic_port("test2")
|
||||||
|
|
||||||
assert port_test2
|
assert port_test2
|
||||||
assert port_test2 != port_test1
|
assert port_test2 != port_test1
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
"""Check ports."""
|
"""Check ports."""
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
|
||||||
|
from supervisor.coresys import CoreSys
|
||||||
from supervisor.utils import check_port
|
from supervisor.utils import check_port
|
||||||
|
|
||||||
|
|
||||||
def test_exists_open_port():
|
async def test_exists_open_port(coresys: CoreSys):
|
||||||
"""Test a exists network port."""
|
"""Test a exists network port."""
|
||||||
assert check_port(ip_address("8.8.8.8"), 53)
|
assert await check_port(ip_address("8.8.8.8"), 53)
|
||||||
|
|
||||||
|
|
||||||
def test_not_exists_port():
|
async def test_not_exists_port(coresys: CoreSys):
|
||||||
"""Test a not exists network service."""
|
"""Test a not exists network service."""
|
||||||
assert not check_port(ip_address("192.0.2.1"), 53)
|
assert not await check_port(ip_address("192.0.2.1"), 53)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user