1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2025-12-25 04:46:25 +00:00

Allow client to change boot slot via API (#4945)

* Allow client to change boot slot via API

* Wrap call to rauc in job that checks for OS

* Reboot after changing the active boot slot

* Add test cases and clean up

* BootName to BootSlot

* Fix test

* Rename boot_name to boot_slot

* Fix tests after field change
This commit is contained in:
Mike Degatano
2024-03-15 10:36:37 -04:00
committed by GitHub
parent c4143dacee
commit 2148de45a0
11 changed files with 315 additions and 10 deletions

View File

@@ -1,8 +1,9 @@
"""Test OS API."""
from unittest.mock import PropertyMock, patch
from unittest.mock import Mock, PropertyMock, patch
from aiohttp.test_utils import TestClient
from dbus_fast import DBusError, ErrorType
import pytest
from supervisor.coresys import CoreSys
@@ -19,6 +20,7 @@ from tests.dbus_service_mocks.agent_boards_yellow import Yellow as YellowService
from tests.dbus_service_mocks.agent_datadisk import DataDisk as DataDiskService
from tests.dbus_service_mocks.agent_system import System as SystemService
from tests.dbus_service_mocks.base import DBusServiceMock
from tests.dbus_service_mocks.rauc import Rauc as RaucService
@pytest.fixture(name="boards_service")
@@ -30,7 +32,7 @@ async def fixture_boards_service(
async def test_api_os_info(api_client: TestClient):
"""Test docker info api."""
"""Test os info api."""
resp = await api_client.get("/os/info")
result = await resp.json()
@@ -41,18 +43,37 @@ async def test_api_os_info(api_client: TestClient):
"board",
"boot",
"data_disk",
"boot_slots",
):
assert attr in result["data"]
async def test_api_os_info_with_agent(api_client: TestClient, coresys: CoreSys):
"""Test docker info api."""
"""Test os info api for data disk."""
resp = await api_client.get("/os/info")
result = await resp.json()
assert result["data"]["data_disk"] == "BJTD4R-0x97cde291"
async def test_api_os_info_boot_slots(
api_client: TestClient, coresys: CoreSys, os_available
):
"""Test os info api for boot slots."""
await coresys.os.load()
resp = await api_client.get("/os/info")
result = await resp.json()
assert result["data"]["boot_slots"] == {
"A": {
"state": "inactive",
"status": "good",
"version": "9.0.dev20220818",
},
"B": {"state": "booted", "status": "good", "version": "9.0.dev20220824"},
}
@pytest.mark.parametrize(
"new_disk",
["/dev/sdaaaa", "/dev/mmcblk1", "Generic-Flash-Disk-61BCDDB6"],
@@ -131,6 +152,56 @@ async def test_api_os_datadisk_wipe(
reboot.assert_called_once()
async def test_api_set_boot_slot(
api_client: TestClient,
all_dbus_services: dict[str, DBusServiceMock],
coresys: CoreSys,
os_available,
):
"""Test changing the boot slot via API."""
rauc_service: RaucService = all_dbus_services["rauc"]
await coresys.os.load()
with patch.object(SystemControl, "reboot") as reboot:
resp = await api_client.post("/os/boot-slot", json={"boot_slot": "A"})
assert resp.status == 200
reboot.assert_called_once()
assert rauc_service.Mark.calls == [("active", "kernel.0")]
async def test_api_set_boot_slot_invalid(api_client: TestClient):
"""Test invalid calls to set boot slot."""
resp = await api_client.post("/os/boot-slot", json={"boot_slot": "C"})
assert resp.status == 400
result = await resp.json()
assert "expected BootSlot or one of 'A', 'B'" in result["message"]
resp = await api_client.post("/os/boot-slot", json={"boot_slot": "A"})
assert resp.status == 400
result = await resp.json()
assert "no Home Assistant OS available" in result["message"]
async def test_api_set_boot_slot_error(
api_client: TestClient,
all_dbus_services: dict[str, DBusServiceMock],
coresys: CoreSys,
capture_exception: Mock,
os_available,
):
"""Test changing the boot slot via API."""
rauc_service: RaucService = all_dbus_services["rauc"]
rauc_service.response_mark = DBusError(ErrorType.FAILED, "fail")
await coresys.os.load()
resp = await api_client.post("/os/boot-slot", json={"boot_slot": "A"})
assert resp.status == 400
result = await resp.json()
assert result["message"] == "Can't mark A as active!"
capture_exception.assert_called_once()
async def test_api_board_yellow_info(api_client: TestClient, coresys: CoreSys):
"""Test yellow board info."""
resp = await api_client.get("/os/boards/yellow")