Skip to content

Commit 52cc17f

Browse files
authored
Delay initial version fetch until there is connectivity (#5603)
* Delay inital version fetch until there is connectivity * Add test * Only mock get not whole websession object * drive delayed fetch off of supervisor connectivity not host * Fix test to not rely on sleep guessing to track tasks * Use fixture to remove job throttle temporarily
1 parent fa6949f commit 52cc17f

File tree

13 files changed

+121
-36
lines changed

13 files changed

+121
-36
lines changed

supervisor/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ class BusEvent(StrEnum):
484484
DOCKER_CONTAINER_STATE_CHANGE = "docker_container_state_change"
485485
HARDWARE_NEW_DEVICE = "hardware_new_device"
486486
HARDWARE_REMOVE_DEVICE = "hardware_remove_device"
487+
SUPERVISOR_CONNECTIVITY_CHANGE = "supervisor_connectivity_change"
487488
SUPERVISOR_JOB_END = "supervisor_job_end"
488489
SUPERVISOR_JOB_START = "supervisor_job_start"
489490
SUPERVISOR_STATE_CHANGE = "supervisor_state_change"

supervisor/host/network.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ def connectivity(self, state: bool | None) -> None:
6767
self.sys_homeassistant.websocket.supervisor_update_event(
6868
"network", {ATTR_HOST_INTERNET: state}
6969
)
70+
if state and not self.sys_supervisor.connectivity:
71+
self.sys_create_task(self.sys_supervisor.check_connectivity())
7072

7173
@property
7274
def interfaces(self) -> list[Interface]:
@@ -148,7 +150,7 @@ async def _check_connectivity_changed(
148150
return
149151

150152
connectivity_check: bool | None = changed.get(DBUS_ATTR_CONNECTION_ENABLED)
151-
connectivity: bool | None = changed.get(DBUS_ATTR_CONNECTIVITY)
153+
connectivity: int | None = changed.get(DBUS_ATTR_CONNECTIVITY)
152154

153155
# This potentially updated the DNS configuration. Make sure the DNS plug-in
154156
# picks up the latest settings.

supervisor/jobs/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,9 @@ def _notify_on_job_change(
213213

214214
if attribute.name == "done":
215215
if value is False:
216-
self.sys_bus.fire_event(BusEvent.SUPERVISOR_JOB_START, job.uuid)
216+
self.sys_bus.fire_event(BusEvent.SUPERVISOR_JOB_START, job)
217217
if value is True:
218-
self.sys_bus.fire_event(BusEvent.SUPERVISOR_JOB_END, job.uuid)
218+
self.sys_bus.fire_event(BusEvent.SUPERVISOR_JOB_END, job)
219219

220220
def new_job(
221221
self,

supervisor/supervisor.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
from aiohttp.client_exceptions import ClientError
1414
from awesomeversion import AwesomeVersion, AwesomeVersionException
1515

16-
from .const import ATTR_SUPERVISOR_INTERNET, SUPERVISOR_VERSION, URL_HASSIO_APPARMOR
16+
from .const import (
17+
ATTR_SUPERVISOR_INTERNET,
18+
SUPERVISOR_VERSION,
19+
URL_HASSIO_APPARMOR,
20+
BusEvent,
21+
)
1722
from .coresys import CoreSys, CoreSysAttributes
1823
from .docker.stats import DockerStats
1924
from .docker.supervisor import DockerSupervisor
@@ -74,6 +79,7 @@ def connectivity(self, state: bool) -> None:
7479
if self._connectivity == state:
7580
return
7681
self._connectivity = state
82+
self.sys_bus.fire_event(BusEvent.SUPERVISOR_CONNECTIVITY_CHANGE, state)
7783
self.sys_homeassistant.websocket.supervisor_update_event(
7884
"network", {ATTR_SUPERVISOR_INTERNET: state}
7985
)

supervisor/updater.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import aiohttp
99
from awesomeversion import AwesomeVersion
1010

11+
from .bus import EventListener
1112
from .const import (
1213
ATTR_AUDIO,
1314
ATTR_AUTO_UPDATE,
@@ -23,6 +24,7 @@
2324
ATTR_SUPERVISOR,
2425
FILE_HASSIO_UPDATER,
2526
URL_HASSIO_VERSION,
27+
BusEvent,
2628
UpdateChannel,
2729
)
2830
from .coresys import CoreSysAttributes
@@ -47,11 +49,18 @@ def __init__(self, coresys):
4749
"""Initialize updater."""
4850
super().__init__(FILE_HASSIO_UPDATER, SCHEMA_UPDATER_CONFIG)
4951
self.coresys = coresys
52+
self._connectivity_listener: EventListener | None = None
5053

5154
async def load(self) -> None:
5255
"""Update internal data."""
53-
with suppress(UpdaterError):
54-
await self.fetch_data()
56+
# If there's no connectivity, delay initial version fetch
57+
if not self.sys_supervisor.connectivity:
58+
self._connectivity_listener = self.sys_bus.register_event(
59+
BusEvent.SUPERVISOR_CONNECTIVITY_CHANGE, self._check_connectivity
60+
)
61+
return
62+
63+
await self.reload()
5564

5665
async def reload(self) -> None:
5766
"""Update internal data."""
@@ -180,6 +189,11 @@ def auto_update(self, value: bool) -> None:
180189
"""Set Supervisor auto updates enabled."""
181190
self._data[ATTR_AUTO_UPDATE] = value
182191

192+
async def _check_connectivity(self, connectivity: bool):
193+
"""Fetch data once connectivity is true."""
194+
if connectivity:
195+
await self.reload()
196+
183197
@Job(
184198
name="updater_fetch_data",
185199
conditions=[JobCondition.INTERNET_SYSTEM],
@@ -214,6 +228,11 @@ async def fetch_data(self):
214228
_LOGGER.warning,
215229
) from err
216230

231+
# Fetch was successful. If there's a connectivity listener, time to remove it
232+
if self._connectivity_listener:
233+
self.sys_bus.remove_listener(self._connectivity_listener)
234+
self._connectivity_listener = None
235+
217236
# Validate
218237
try:
219238
await self.sys_security.verify_own_content(calc_checksum(data))

tests/conftest.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
import asyncio
44
from collections.abc import AsyncGenerator, Generator
5-
from functools import partial
6-
from inspect import unwrap
5+
from datetime import datetime
76
import os
87
from pathlib import Path
98
import subprocess
@@ -381,11 +380,6 @@ async def coresys(
381380
ha_version=AwesomeVersion("2021.2.4")
382381
)
383382

384-
# Remove rate limiting decorator from fetch_data
385-
coresys_obj.updater.fetch_data = partial(
386-
unwrap(coresys_obj.updater.fetch_data), coresys_obj.updater
387-
)
388-
389383
# Don't remove files/folders related to addons and stores
390384
with patch("supervisor.store.git.GitRepo._remove"):
391385
yield coresys_obj
@@ -765,3 +759,10 @@ def mock_is_mount() -> MagicMock:
765759
"""Mock is_mount in mounts."""
766760
with patch("supervisor.mounts.mount.Path.is_mount", return_value=True) as is_mount:
767761
yield is_mount
762+
763+
764+
@pytest.fixture
765+
def no_job_throttle():
766+
"""Remove job throttle for tests."""
767+
with patch("supervisor.jobs.decorator.Job.last_call", return_value=datetime.min):
768+
yield

tests/dbus_service_mocks/network_manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class NetworkManager(DBusServiceMock):
2222
interface = "org.freedesktop.NetworkManager"
2323
object_path = "/org/freedesktop/NetworkManager"
2424
version = "1.22.10"
25+
connectivity_check_enabled = True
2526
connectivity = 4
2627
devices = [
2728
"/org/freedesktop/NetworkManager/Devices/1",
@@ -155,7 +156,7 @@ def ConnectivityCheckAvailable(self) -> "b":
155156
@dbus_property()
156157
def ConnectivityCheckEnabled(self) -> "b":
157158
"""Get ConnectivityCheckEnabled."""
158-
return True
159+
return self.connectivity_check_enabled
159160

160161
@ConnectivityCheckEnabled.setter
161162
def ConnectivityCheckEnabled(self, value: "b"):

tests/jobs/test_job_decorator.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,13 +1144,13 @@ async def job_task(self) -> None:
11441144
started = False
11451145
ended = False
11461146

1147-
async def start_listener(job_id: str):
1147+
async def start_listener(evt_job: SupervisorJob):
11481148
nonlocal started
1149-
started = started or job_id == job.uuid
1149+
started = started or evt_job.uuid == job.uuid
11501150

1151-
async def end_listener(job_id: str):
1151+
async def end_listener(evt_job: SupervisorJob):
11521152
nonlocal ended
1153-
ended = ended or job_id == job.uuid
1153+
ended = ended or evt_job.uuid == job.uuid
11541154

11551155
coresys.bus.register_event(BusEvent.SUPERVISOR_JOB_START, start_listener)
11561156
coresys.bus.register_event(BusEvent.SUPERVISOR_JOB_END, end_listener)
@@ -1196,13 +1196,13 @@ async def job_task(self) -> None:
11961196
started = False
11971197
ended = False
11981198

1199-
async def start_listener(job_id: str):
1199+
async def start_listener(evt_job: SupervisorJob):
12001200
nonlocal started
1201-
started = started or job_id == job.uuid
1201+
started = started or evt_job.uuid == job.uuid
12021202

1203-
async def end_listener(job_id: str):
1203+
async def end_listener(evt_job: SupervisorJob):
12041204
nonlocal ended
1205-
ended = ended or job_id == job.uuid
1205+
ended = ended or evt_job.uuid == job.uuid
12061206

12071207
coresys.bus.register_event(BusEvent.SUPERVISOR_JOB_START, start_listener)
12081208
coresys.bus.register_event(BusEvent.SUPERVISOR_JOB_END, end_listener)

tests/misc/test_tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ async def test_watchdog_homeassistant_api_reanimation_limit(
171171
rebuild.assert_not_called()
172172

173173

174+
@pytest.mark.usefixtures("no_job_throttle")
174175
async def test_reload_updater_triggers_supervisor_update(
175176
tasks: Tasks, coresys: CoreSys
176177
):

tests/os/test_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# pylint: disable=protected-access
1717

1818

19-
@pytest.mark.asyncio
19+
@pytest.mark.usefixtures("no_job_throttle")
2020
async def test_ota_url_generic_x86_64_rename(coresys: CoreSys) -> None:
2121
"""Test download URL generated."""
2222
coresys.os._board = "intel-nuc"

0 commit comments

Comments
 (0)