From 27b092aed0f937dcdb99e69949497537b9cf4fe8 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 31 Jul 2025 11:23:57 +0200 Subject: [PATCH] Block OS updates when the system is unhealthy (#6053) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Block OS updates when the system is unhealthy In #6024 we mark a system as unhealthy when multiple OS installations were found. The idea was to block OS updates in this case. However, it turns out that the OS update job was not checking the system health and thus allowed updates even when the system was marked as unhealthy. This commit adds the `JobCondition.HEALTHY` condition to the OS update job, ensuring that OS updates are only performed when the system is healthy. Users can force an OS update still by using `ha jobs options --ignore-conditions healthy`. * Add test for update of unhealthy system --------- Co-authored-by: Jan Čermák --- supervisor/os/manager.py | 1 + tests/os/test_manager.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/supervisor/os/manager.py b/supervisor/os/manager.py index 5763f538f..f99673925 100644 --- a/supervisor/os/manager.py +++ b/supervisor/os/manager.py @@ -272,6 +272,7 @@ class OSManager(CoreSysAttributes): name="os_manager_update", conditions=[ JobCondition.HAOS, + JobCondition.HEALTHY, JobCondition.INTERNET_SYSTEM, JobCondition.RUNNING, JobCondition.SUPERVISOR_UPDATED, diff --git a/tests/os/test_manager.py b/tests/os/test_manager.py index 70370064a..8970c9ace 100644 --- a/tests/os/test_manager.py +++ b/tests/os/test_manager.py @@ -9,6 +9,7 @@ import pytest from supervisor.const import CoreState from supervisor.coresys import CoreSys from supervisor.exceptions import HassOSJobError +from supervisor.resolution.const import UnhealthyReason from tests.common import MockResponse from tests.dbus_service_mocks.base import DBusServiceMock @@ -85,6 +86,21 @@ async def test_update_fails_if_out_of_date( await coresys.os.update() +async def test_update_fails_if_unhealthy( + coresys: CoreSys, +) -> None: + """Test update of OS fails if Supervisor is unhealthy.""" + await coresys.core.set_state(CoreState.RUNNING) + coresys.resolution.add_unhealthy_reason(UnhealthyReason.DUPLICATE_OS_INSTALLATION) + with ( + patch.object( + type(coresys.os), "available", new=PropertyMock(return_value=True) + ), + pytest.raises(HassOSJobError), + ): + await coresys.os.update() + + async def test_board_name_supervised(coresys: CoreSys) -> None: """Test board name is supervised when not on haos.""" with patch("supervisor.os.manager.CPE.get_product", return_value=["not-hassos"]):