diff --git a/supervisor/resolution/fixups/addon_execute_repair.py b/supervisor/resolution/fixups/addon_execute_repair.py index e497a9883..90c101736 100644 --- a/supervisor/resolution/fixups/addon_execute_repair.py +++ b/supervisor/resolution/fixups/addon_execute_repair.py @@ -7,6 +7,7 @@ from ..const import ContextType, IssueType, SuggestionType from .base import FixupBase _LOGGER: logging.Logger = logging.getLogger(__name__) +MAX_AUTO_ATTEMPTS = 5 def setup(coresys: CoreSys) -> FixupBase: @@ -17,6 +18,11 @@ def setup(coresys: CoreSys) -> FixupBase: class FixupAddonExecuteRepair(FixupBase): """Storage class for fixup.""" + def __init__(self, coresys: CoreSys) -> None: + """Initialize the add-on execute repair fixup class.""" + super().__init__(coresys) + self.attempts = 0 + async def process_fixup(self, reference: str | None = None) -> None: """Pull the addons image.""" addon = self.sys_addons.get(reference, local_only=True) @@ -34,6 +40,7 @@ class FixupAddonExecuteRepair(FixupBase): return _LOGGER.info("Installing image for addon %s") + self.attempts += 1 await addon.instance.install(addon.version) @property @@ -54,4 +61,4 @@ class FixupAddonExecuteRepair(FixupBase): @property def auto(self) -> bool: """Return if a fixup can be apply as auto fix.""" - return True + return self.attempts < MAX_AUTO_ATTEMPTS diff --git a/tests/resolution/fixup/test_addon_execute_repair.py b/tests/resolution/fixup/test_addon_execute_repair.py index ea3ae5a0b..d5edfca89 100644 --- a/tests/resolution/fixup/test_addon_execute_repair.py +++ b/tests/resolution/fixup/test_addon_execute_repair.py @@ -3,12 +3,14 @@ from unittest.mock import MagicMock, patch from docker.errors import NotFound +import pytest from supervisor.addons.addon import Addon from supervisor.coresys import CoreSys from supervisor.docker.addon import DockerAddon from supervisor.docker.interface import DockerInterface from supervisor.docker.manager import DockerAPI +from supervisor.exceptions import DockerError from supervisor.resolution.const import ContextType, IssueType, SuggestionType from supervisor.resolution.fixups.addon_execute_repair import FixupAddonExecuteRepair @@ -35,6 +37,30 @@ async def test_fixup(docker: DockerAPI, coresys: CoreSys, install_addon_ssh: Add assert not coresys.resolution.suggestions +async def test_fixup_max_auto_attempts( + docker: DockerAPI, coresys: CoreSys, install_addon_ssh: Addon +): + """Test fixup stops being auto-applied after 5 failures.""" + docker.images.get.side_effect = NotFound("missing") + install_addon_ssh.data["image"] = "test_image" + + addon_execute_repair = FixupAddonExecuteRepair(coresys) + + coresys.resolution.create_issue( + IssueType.MISSING_IMAGE, + ContextType.ADDON, + reference="local_ssh", + suggestions=[SuggestionType.EXECUTE_REPAIR], + ) + with patch.object(DockerInterface, "install", side_effect=DockerError): + for _ in range(5): + assert addon_execute_repair.auto is True + with pytest.raises(DockerError): + await addon_execute_repair() + + assert addon_execute_repair.auto is False + + async def test_fixup_no_addon(coresys: CoreSys): """Test fixup dismisses if addon is missing.""" addon_execute_repair = FixupAddonExecuteRepair(coresys)