mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-28 11:36:32 +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]
|
||||
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
|
||||
|
||||
@property
|
||||
@ -539,7 +539,7 @@ class Addon(AddonModel):
|
||||
|
||||
# TCP monitoring
|
||||
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
|
||||
if t_proto:
|
||||
@ -602,6 +602,16 @@ class Addon(AddonModel):
|
||||
_LOGGER.info("Removing add-on data folder %s", 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(
|
||||
name="addon_install",
|
||||
limit=JobExecutionLimit.GROUP_ONCE,
|
||||
@ -630,6 +640,8 @@ class Addon(AddonModel):
|
||||
self.sys_addons.data.uninstall(self)
|
||||
raise AddonsError() from err
|
||||
|
||||
await self._check_ingress_port()
|
||||
|
||||
# Add to addon manager
|
||||
self.sys_addons.local[self.slug] = self
|
||||
|
||||
@ -716,6 +728,7 @@ class Addon(AddonModel):
|
||||
try:
|
||||
_LOGGER.info("Add-on '%s' successfully updated", self.slug)
|
||||
self.sys_addons.data.update(store)
|
||||
await self._check_ingress_port()
|
||||
|
||||
# Cleanup
|
||||
with suppress(DockerError):
|
||||
@ -756,6 +769,7 @@ class Addon(AddonModel):
|
||||
raise AddonsError() from err
|
||||
|
||||
self.sys_addons.data.update(self.addon_store)
|
||||
await self._check_ingress_port()
|
||||
_LOGGER.info("Add-on '%s' successfully rebuilt", self.slug)
|
||||
|
||||
finally:
|
||||
@ -1199,6 +1213,7 @@ class Addon(AddonModel):
|
||||
_LOGGER.info("Restore/Update of image for addon %s", self.slug)
|
||||
with suppress(DockerError):
|
||||
await self.instance.update(version, restore_image)
|
||||
self._check_ingress_port()
|
||||
|
||||
# Restore data and config
|
||||
def _restore_data():
|
||||
|
@ -140,8 +140,7 @@ class HomeAssistantAPI(CoreSysAttributes):
|
||||
return None
|
||||
|
||||
# Check if port is up
|
||||
if not await self.sys_run_in_executor(
|
||||
check_port,
|
||||
if not await check_port(
|
||||
self.sys_homeassistant.ip_address,
|
||||
self.sys_homeassistant.api_port,
|
||||
):
|
||||
|
@ -154,7 +154,7 @@ class Ingress(FileConfiguration, CoreSysAttributes):
|
||||
|
||||
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."""
|
||||
if addon_slug in self.ports:
|
||||
return self.ports[addon_slug]
|
||||
@ -163,7 +163,7 @@ class Ingress(FileConfiguration, CoreSysAttributes):
|
||||
while (
|
||||
port is None
|
||||
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)
|
||||
|
||||
|
@ -39,20 +39,19 @@ def process_lock(method):
|
||||
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."""
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(0.5)
|
||||
sock.setblocking(False)
|
||||
try:
|
||||
result = sock.connect_ex((str(address), port))
|
||||
sock.close()
|
||||
|
||||
# Check if the port is available
|
||||
if result == 0:
|
||||
return True
|
||||
except OSError:
|
||||
pass
|
||||
return False
|
||||
async with asyncio.timeout(0.5):
|
||||
await asyncio.get_running_loop().sock_connect(sock, (str(address), port))
|
||||
except (OSError, TimeoutError):
|
||||
return False
|
||||
finally:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
return True
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def test_dynamic_ports(coresys: CoreSys):
|
||||
async def test_dynamic_ports(coresys: CoreSys):
|
||||
"""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 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 != port_test1
|
||||
|
@ -1,14 +1,15 @@
|
||||
"""Check ports."""
|
||||
from ipaddress import ip_address
|
||||
|
||||
from supervisor.utils import check_port
|
||||
|
||||
|
||||
def test_exists_open_port():
|
||||
"""Test a exists network port."""
|
||||
assert check_port(ip_address("8.8.8.8"), 53)
|
||||
|
||||
|
||||
def test_not_exists_port():
|
||||
"""Test a not exists network service."""
|
||||
assert not check_port(ip_address("192.0.2.1"), 53)
|
||||
"""Check ports."""
|
||||
from ipaddress import ip_address
|
||||
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.utils import check_port
|
||||
|
||||
|
||||
async def test_exists_open_port(coresys: CoreSys):
|
||||
"""Test a exists network port."""
|
||||
assert await check_port(ip_address("8.8.8.8"), 53)
|
||||
|
||||
|
||||
async def test_not_exists_port(coresys: CoreSys):
|
||||
"""Test a not exists network service."""
|
||||
assert not await check_port(ip_address("192.0.2.1"), 53)
|
||||
|
Loading…
x
Reference in New Issue
Block a user