Trigger auto-update through Core WebSocket call (#5896)

* Trigger auto-update through Core WebSocket call

Instead of auto-updating add-ons on Supervisor side trigger an update
through Core via a WebSocket command. This makes sure that the backup
is categorized correctly and all backup features like retention are
applied.

* Add pytest

* Fix pytest

* Fix pytest

* Fix pytest

* Fix pytest

* Fix pytest cleaner

* Set timestamp of add-on far into the past
This commit is contained in:
Stefan Agner 2025-05-20 15:18:37 +02:00 committed by GitHub
parent b5a7e521ae
commit 6e6fe5ba39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 15 deletions

View File

@ -32,6 +32,7 @@ class WSType(StrEnum):
SUPERVISOR_EVENT = "supervisor/event"
BACKUP_START = "backup/start"
BACKUP_END = "backup/end"
HASSIO_UPDATE_ADDON = "hassio/update/addon"
class WSEvent(StrEnum):

View File

@ -1,13 +1,11 @@
"""A collection of tasks."""
import asyncio
from collections.abc import Awaitable
from datetime import datetime, timedelta
import logging
from ..addons.const import ADDON_UPDATE_CONDITIONS
from ..backups.const import LOCATION_CLOUD_BACKUP
from ..const import AddonState
from ..const import ATTR_TYPE, AddonState
from ..coresys import CoreSysAttributes
from ..exceptions import (
AddonsError,
@ -15,7 +13,7 @@ from ..exceptions import (
HomeAssistantError,
ObserverError,
)
from ..homeassistant.const import LANDINGPAGE
from ..homeassistant.const import LANDINGPAGE, WSType
from ..jobs.decorator import Job, JobCondition, JobExecutionLimit
from ..plugins.const import PLUGIN_UPDATE_CONDITIONS
from ..utils.dt import utcnow
@ -106,7 +104,6 @@ class Tasks(CoreSysAttributes):
)
async def _update_addons(self):
"""Check if an update is available for an Add-on and update it."""
start_tasks: list[Awaitable[None]] = []
for addon in self.sys_addons.all:
if not addon.is_installed or not addon.auto_update:
continue
@ -131,16 +128,21 @@ class Tasks(CoreSysAttributes):
)
continue
# Run Add-on update sequential
# avoid issue on slow IO
_LOGGER.info("Add-on auto update process %s", addon.slug)
try:
if start_task := await self.sys_addons.update(addon.slug, backup=True):
start_tasks.append(start_task)
except AddonsError:
_LOGGER.error("Can't auto update Add-on %s", addon.slug)
await asyncio.gather(*start_tasks)
# Call Home Assistant Core to update add-on to make sure that backups
# get created through the Home Assistant Core API (categorized correctly).
# Ultimately auto updates should be handled by Home Assistant Core itself
# through a update entity feature.
message = {
ATTR_TYPE: WSType.HASSIO_UPDATE_ADDON,
"addon": addon.slug,
"backup": True,
}
_LOGGER.debug(
"Sending update add-on WebSocket command to Home Assistant Core: %s",
message,
)
await self.sys_homeassistant.websocket.async_send_command(message)
@Job(
name="tasks_update_supervisor",

View File

@ -8,7 +8,8 @@ from unittest.mock import AsyncMock, MagicMock, Mock, PropertyMock, patch
from awesomeversion import AwesomeVersion
import pytest
from supervisor.const import CoreState
from supervisor.addons.addon import Addon
from supervisor.const import ATTR_VERSION_TIMESTAMP, CoreState
from supervisor.coresys import CoreSys
from supervisor.exceptions import HomeAssistantError
from supervisor.homeassistant.api import HomeAssistantAPI
@ -230,3 +231,37 @@ async def test_core_backup_cleanup(
assert not coresys.backups.get("7fed74c8")
assert new_tar.exists()
assert not old_tar.exists()
async def test_update_addons_auto_update_success(
tasks: Tasks,
coresys: CoreSys,
tmp_supervisor_data: Path,
ha_ws_client: AsyncMock,
install_addon_example: Addon,
):
"""Test that an eligible add-on is auto-updated via websocket command."""
await coresys.core.set_state(CoreState.RUNNING)
# Set up the add-on as eligible for auto-update
install_addon_example.auto_update = True
install_addon_example.data_store[ATTR_VERSION_TIMESTAMP] = 0
with patch.object(
Addon, "version", new=PropertyMock(return_value=AwesomeVersion("1.0"))
):
assert install_addon_example.need_update is True
assert install_addon_example.auto_update_available is True
# Make sure all job events from installing the add-on are cleared
ha_ws_client.async_send_command.reset_mock()
# pylint: disable-next=protected-access
await tasks._update_addons()
ha_ws_client.async_send_command.assert_any_call(
{
"type": "hassio/update/addon",
"addon": install_addon_example.slug,
"backup": True,
}
)