mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-14 04:36:31 +00:00
Support dynamic ingress port (#1015)
* Support dynamic ingress port * Allow to remeber ports * Add tests * Fix schema * Cleanup handling / speed * Fix port
This commit is contained in:
parent
ead5993f3e
commit
c2deabb672
3
API.md
3
API.md
@ -512,7 +512,8 @@ Get all available addons.
|
||||
"ip_address": "ip address",
|
||||
"ingress": "bool",
|
||||
"ingress_entry": "null|/api/hassio_ingress/slug",
|
||||
"ingress_url": "null|/api/hassio_ingress/slug/entry.html"
|
||||
"ingress_url": "null|/api/hassio_ingress/slug/entry.html",
|
||||
"ingress_port": "null|int"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -431,9 +431,15 @@ class Addon(CoreSysAttributes):
|
||||
return f"{proto}://[HOST]:{port}{s_suffix}"
|
||||
|
||||
@property
|
||||
def ingress_internal(self):
|
||||
"""Return Ingress host URL."""
|
||||
return f"http://{self.ip_address}:{self._mesh[ATTR_INGRESS_PORT]}"
|
||||
def ingress_port(self):
|
||||
"""Return Ingress port."""
|
||||
if not self.is_installed or not self.with_ingress:
|
||||
return None
|
||||
|
||||
port = self._mesh[ATTR_INGRESS_PORT]
|
||||
if port == 0:
|
||||
return self.sys_ingress.get_dynamic_port(self.slug)
|
||||
return port
|
||||
|
||||
@property
|
||||
def host_network(self):
|
||||
|
@ -148,7 +148,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
|
||||
vol.Optional(ATTR_WEBUI):
|
||||
vol.Match(r"^(?:https?|\[PROTO:\w+\]):\/\/\[HOST\]:\[PORT:\d+\].*$"),
|
||||
vol.Optional(ATTR_INGRESS, default=False): vol.Boolean(),
|
||||
vol.Optional(ATTR_INGRESS_PORT, default=8099): NETWORK_PORT,
|
||||
vol.Optional(ATTR_INGRESS_PORT, default=8099): vol.Any(NETWORK_PORT, vol.Equal(0)),
|
||||
vol.Optional(ATTR_INGRESS_ENTRY): vol.Coerce(str),
|
||||
vol.Optional(ATTR_HOMEASSISTANT): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
||||
|
@ -44,6 +44,7 @@ from ..const import (
|
||||
ATTR_ICON,
|
||||
ATTR_INGRESS,
|
||||
ATTR_INGRESS_ENTRY,
|
||||
ATTR_INGRESS_PORT,
|
||||
ATTR_INGRESS_URL,
|
||||
ATTR_INSTALLED,
|
||||
ATTR_IP_ADDRESS,
|
||||
@ -223,6 +224,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_INGRESS: addon.with_ingress,
|
||||
ATTR_INGRESS_ENTRY: addon.ingress_entry,
|
||||
ATTR_INGRESS_URL: addon.ingress_url,
|
||||
ATTR_INGRESS_PORT: addon.ingress_port,
|
||||
}
|
||||
|
||||
@api_process
|
||||
|
@ -43,7 +43,7 @@ class APIIngress(CoreSysAttributes):
|
||||
|
||||
def _create_url(self, addon: Addon, path: str) -> str:
|
||||
"""Create URL to container."""
|
||||
return f"{addon.ingress_internal}/{path}"
|
||||
return f"http://{addon.ip_address}:{addon.ingress_port}/{path}"
|
||||
|
||||
@api_process
|
||||
async def create_session(self, request: web.Request) -> Dict[str, Any]:
|
||||
|
@ -1,14 +1,15 @@
|
||||
"""Fetch last versions from webserver."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Dict, Optional
|
||||
import random
|
||||
import secrets
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .addons.addon import Addon
|
||||
from .const import ATTR_SESSION, FILE_HASSIO_INGRESS
|
||||
from .const import ATTR_PORTS, ATTR_SESSION, FILE_HASSIO_INGRESS
|
||||
from .coresys import CoreSys, CoreSysAttributes
|
||||
from .utils.dt import utc_from_timestamp, utcnow
|
||||
from .utils.json import JsonConfig
|
||||
from .utils.dt import utcnow, utc_from_timestamp
|
||||
from .validate import SCHEMA_INGRESS_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -34,6 +35,11 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
||||
"""Return sessions."""
|
||||
return self._data[ATTR_SESSION]
|
||||
|
||||
@property
|
||||
def ports(self) -> Dict[str, int]:
|
||||
"""Return list of dynamic ports."""
|
||||
return self._data[ATTR_PORTS]
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Update internal data."""
|
||||
self._update_token_list()
|
||||
@ -101,3 +107,14 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
||||
self.sessions[session] = valid_until.timestamp()
|
||||
|
||||
return True
|
||||
|
||||
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]
|
||||
port = random.randint(62000, 65500)
|
||||
|
||||
# Save port for next time
|
||||
self.ports[addon_slug] = port
|
||||
self.save_data()
|
||||
return port
|
||||
|
@ -18,6 +18,7 @@ from .const import (
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_PASSWORD,
|
||||
ATTR_PORT,
|
||||
ATTR_PORTS,
|
||||
ATTR_REFRESH_TOKEN,
|
||||
ATTR_SESSION,
|
||||
ATTR_SSL,
|
||||
@ -143,6 +144,13 @@ SCHEMA_AUTH_CONFIG = vol.Schema({SHA256: SHA256})
|
||||
|
||||
|
||||
SCHEMA_INGRESS_CONFIG = vol.Schema(
|
||||
{vol.Required(ATTR_SESSION, default=dict): vol.Schema({TOKEN: vol.Coerce(float)})},
|
||||
{
|
||||
vol.Required(ATTR_SESSION, default=dict): vol.Schema(
|
||||
{TOKEN: vol.Coerce(float)}
|
||||
),
|
||||
vol.Required(ATTR_PORTS, default=dict): vol.Schema(
|
||||
{vol.Coerce(str): NETWORK_PORT}
|
||||
),
|
||||
},
|
||||
extra=vol.REMOVE_EXTRA,
|
||||
)
|
||||
|
@ -20,3 +20,22 @@ def test_session_handling(coresys):
|
||||
coresys.ingress.sessions[session] = not_valid.timestamp()
|
||||
assert not coresys.ingress.validate_session(session)
|
||||
assert not coresys.ingress.validate_session("invalid session")
|
||||
|
||||
|
||||
def test_dynamic_ports(coresys):
|
||||
"""Test dyanmic port handling."""
|
||||
port_test1 = coresys.ingress.get_dynamic_port("test1")
|
||||
|
||||
assert port_test1
|
||||
assert coresys.ingress.save_data.called
|
||||
assert port_test1 == coresys.ingress.get_dynamic_port("test1")
|
||||
|
||||
port_test2 = coresys.ingress.get_dynamic_port("test2")
|
||||
|
||||
assert port_test2
|
||||
assert port_test2 != port_test1
|
||||
|
||||
assert port_test2 > 62000
|
||||
assert port_test2 < 65500
|
||||
assert port_test1 > 62000
|
||||
assert port_test1 < 65500
|
||||
|
Loading…
x
Reference in New Issue
Block a user