From 01064564b496db839bd2ab1b2cc6330cc3737379 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 22 Oct 2020 12:29:53 +0200 Subject: [PATCH] Fix new version handling with Plugins (#2159) --- supervisor/plugins/__init__.py | 66 ++++++++++----------- supervisor/plugins/audio.py | 2 + supervisor/plugins/cli.py | 2 + supervisor/plugins/dns.py | 2 + supervisor/plugins/multicast.py | 2 + supervisor/plugins/observer.py | 2 + supervisor/resolution/const.py | 5 +- tests/resolution/test_resolution_manager.py | 4 +- 8 files changed, 48 insertions(+), 37 deletions(-) diff --git a/supervisor/plugins/__init__.py b/supervisor/plugins/__init__.py index 010f0d15a..07d7b3593 100644 --- a/supervisor/plugins/__init__.py +++ b/supervisor/plugins/__init__.py @@ -2,10 +2,9 @@ import asyncio import logging -from packaging.version import LegacyVersion, parse as pkg_parse - from ..coresys import CoreSys, CoreSysAttributes from ..exceptions import HassioError +from ..resolution.const import ContextType, IssueType, SuggestionType from .audio import Audio from .cli import HaCli from .dns import CoreDNS @@ -18,12 +17,6 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) class PluginManager(CoreSysAttributes): """Manage supported function for plugins.""" - required_cli: LegacyVersion = pkg_parse("2020.10.0") - required_dns: LegacyVersion = pkg_parse("9") - required_audio: LegacyVersion = pkg_parse("17") - required_observer: LegacyVersion = pkg_parse("2020.10.1") - required_multicast: LegacyVersion = pkg_parse("3") - def __init__(self, coresys: CoreSys): """Initialize plugin manager.""" self.coresys: CoreSys = coresys @@ -72,44 +65,49 @@ class PluginManager(CoreSysAttributes): try: await plugin.load() except Exception as err: # pylint: disable=broad-except - _LOGGER.warning("Can't load plugin %s: %s", type(plugin).__name__, err) + _LOGGER.warning("Can't load plugin %s: %s", plugin.slug, err) + self.sys_resolution.create_issue( + IssueType.FATAL_ERROR, + ContextType.PLUGIN, + reference=plugin.slug, + suggestions=[SuggestionType.EXECUTE_REPAIR], + ) self.sys_capture_exception(err) # Check requirements - for plugin, required_version in ( - (self._audio, self.required_audio), - (self._dns, self.required_dns), - (self._cli, self.required_cli), - (self._observer, self.required_observer), - (self._multicast, self.required_multicast), + await self.sys_updater.reload() + for plugin in ( + self.dns, + self.audio, + self.cli, + self.observer, + self.multicast, ): # Check if need an update - try: - if pkg_parse(plugin.version) >= required_version: - continue - except TypeError: - _LOGGER.warning( - "Unexpected issue while checking requirements for %s", - type(plugin).__name__, - ) + if not plugin.need_update: + continue _LOGGER.info( - "%s does not have the required version %s, updating", - type(plugin).__name__, - required_version, + "%s does not have the latest version %s, updating", + plugin.slug, + plugin.latest_version, ) try: - await plugin.update(version=str(required_version)) + await plugin.update() except HassioError: _LOGGER.error( - "Can't update %s to %s but it's a reuirement, the Supervisor is now in an unhealthy state!", - type(plugin).__name__, - required_version, + "Can't update %s to %s, the Supervisor healthy could be compromised!", + plugin.slug, + plugin.latest_version, + ) + self.sys_resolution.create_issue( + IssueType.UPDATE_FAILED, + ContextType.PLUGIN, + reference=plugin.slug, + suggestions=[SuggestionType.EXECUTE_UPDATE], ) except Exception as err: # pylint: disable=broad-except - _LOGGER.warning( - "Can't update plugin %s: %s", type(plugin).__name__, err - ) + _LOGGER.warning("Can't update plugin %s: %s", plugin.slug, err) self.sys_capture_exception(err) async def repair(self) -> None: @@ -136,5 +134,5 @@ class PluginManager(CoreSysAttributes): try: await plugin.stop() except Exception as err: # pylint: disable=broad-except - _LOGGER.warning("Can't stop plugin %s: %s", type(plugin).__name__, err) + _LOGGER.warning("Can't stop plugin %s: %s", plugin.slug, err) self.sys_capture_exception(err) diff --git a/supervisor/plugins/audio.py b/supervisor/plugins/audio.py index 9ab48874e..a8163c5ff 100644 --- a/supervisor/plugins/audio.py +++ b/supervisor/plugins/audio.py @@ -30,6 +30,8 @@ ASOUND_TMPL: Path = Path(__file__).parents[1].joinpath("data/asound.tmpl") class Audio(JsonConfig, CoreSysAttributes): """Home Assistant core object for handle audio.""" + slug: str = "audio" + def __init__(self, coresys: CoreSys): """Initialize hass object.""" super().__init__(FILE_HASSIO_AUDIO, SCHEMA_AUDIO_CONFIG) diff --git a/supervisor/plugins/cli.py b/supervisor/plugins/cli.py index 78eafc2c3..499fb293c 100644 --- a/supervisor/plugins/cli.py +++ b/supervisor/plugins/cli.py @@ -25,6 +25,8 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) class HaCli(CoreSysAttributes, JsonConfig): """HA cli interface inside supervisor.""" + slug: str = "cli" + def __init__(self, coresys: CoreSys): """Initialize cli handler.""" super().__init__(FILE_HASSIO_CLI, SCHEMA_CLI_CONFIG) diff --git a/supervisor/plugins/dns.py b/supervisor/plugins/dns.py index 45f654853..0e479409c 100644 --- a/supervisor/plugins/dns.py +++ b/supervisor/plugins/dns.py @@ -42,6 +42,8 @@ class HostEntry: class CoreDNS(JsonConfig, CoreSysAttributes): """Home Assistant core object for handle it.""" + slug: str = "dns" + def __init__(self, coresys: CoreSys): """Initialize hass object.""" super().__init__(FILE_HASSIO_DNS, SCHEMA_DNS_CONFIG) diff --git a/supervisor/plugins/multicast.py b/supervisor/plugins/multicast.py index 5962a4229..213b9d440 100644 --- a/supervisor/plugins/multicast.py +++ b/supervisor/plugins/multicast.py @@ -24,6 +24,8 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) class Multicast(JsonConfig, CoreSysAttributes): """Home Assistant core object for handle it.""" + slug: str = "multicast" + def __init__(self, coresys: CoreSys): """Initialize hass object.""" super().__init__(FILE_HASSIO_MULTICAST, SCHEMA_MULTICAST_CONFIG) diff --git a/supervisor/plugins/observer.py b/supervisor/plugins/observer.py index eb10fe6c4..a78ae78d8 100644 --- a/supervisor/plugins/observer.py +++ b/supervisor/plugins/observer.py @@ -26,6 +26,8 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) class Observer(CoreSysAttributes, JsonConfig): """Supervisor observer instance.""" + slug: str = "observer" + def __init__(self, coresys: CoreSys): """Initialize observer handler.""" super().__init__(FILE_HASSIO_OBSERVER, SCHEMA_OBSERVER_CONFIG) diff --git a/supervisor/resolution/const.py b/supervisor/resolution/const.py index eb7893f70..33ab9b4cf 100644 --- a/supervisor/resolution/const.py +++ b/supervisor/resolution/const.py @@ -12,6 +12,7 @@ class ContextType(str, Enum): SYSTEM = "system" SUPERVISOR = "supervisor" + PLUGIN = "plugin" ADDON = "addon" CORE = "core" OS = "os" @@ -39,6 +40,7 @@ class IssueType(str, Enum): MISSING_IMAGE = "missing_image" UPDATE_FAILED = "update_failed" UPDATE_ROLLBACK = "update_rollback" + FATAL_ERROR = "fatal_error" class SuggestionType(str, Enum): @@ -46,4 +48,5 @@ class SuggestionType(str, Enum): CLEAR_FULL_SNAPSHOT = "clear_full_snapshot" CREATE_FULL_SNAPSHOT = "create_full_snapshot" - SYSTEM_REPAIR = "system_repair" + EXECUTE_UPDATE = "execute_update" + EXECUTE_REPAIR = "execute_repair" diff --git a/tests/resolution/test_resolution_manager.py b/tests/resolution/test_resolution_manager.py index dfd535d1a..6e3dbd8df 100644 --- a/tests/resolution/test_resolution_manager.py +++ b/tests/resolution/test_resolution_manager.py @@ -129,12 +129,12 @@ async def test_resolution_create_issue_suggestion(coresys: CoreSys): IssueType.UPDATE_ROLLBACK, ContextType.CORE, "slug", - [SuggestionType.SYSTEM_REPAIR], + [SuggestionType.EXECUTE_REPAIR], ) assert IssueType.UPDATE_ROLLBACK == coresys.resolution.issues[-1].type assert ContextType.CORE == coresys.resolution.issues[-1].context assert coresys.resolution.issues[-1].reference == "slug" - assert SuggestionType.SYSTEM_REPAIR == coresys.resolution.suggestions[-1].type + assert SuggestionType.EXECUTE_REPAIR == coresys.resolution.suggestions[-1].type assert ContextType.CORE == coresys.resolution.suggestions[-1].context