Improve Supervisor backup error handling (#134346)
* Raise Home Assistant error in case backup restore fails This change raises a Home Assistant error in case the backup restore fails. The Supervisor is checking some common issues before starting the actual restore in background. This early checks raise an exception (represented by a HTTP 400 error). This change catches such errors and raises a Home Assistant error with the message from the Supervisor exception. * Add test coveragepull/134436/head
parent
3845acd0ce
commit
fb3105bdc0
|
@ -28,6 +28,9 @@ from homeassistant.components.backup import (
|
|||
NewBackup,
|
||||
WrittenBackup,
|
||||
)
|
||||
|
||||
# pylint: disable-next=hass-component-root-import
|
||||
from homeassistant.components.backup.manager import IncorrectPasswordError
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
@ -385,17 +388,24 @@ class SupervisorBackupReaderWriter(BackupReaderWriter):
|
|||
agent = cast(SupervisorBackupAgent, manager.backup_agents[agent_id])
|
||||
restore_location = agent.location
|
||||
|
||||
job = await self._client.backups.partial_restore(
|
||||
backup_id,
|
||||
supervisor_backups.PartialRestoreOptions(
|
||||
addons=restore_addons_set,
|
||||
folders=restore_folders_set,
|
||||
homeassistant=restore_homeassistant,
|
||||
password=password,
|
||||
background=True,
|
||||
location=restore_location,
|
||||
),
|
||||
)
|
||||
try:
|
||||
job = await self._client.backups.partial_restore(
|
||||
backup_id,
|
||||
supervisor_backups.PartialRestoreOptions(
|
||||
addons=restore_addons_set,
|
||||
folders=restore_folders_set,
|
||||
homeassistant=restore_homeassistant,
|
||||
password=password,
|
||||
background=True,
|
||||
location=restore_location,
|
||||
),
|
||||
)
|
||||
except SupervisorBadRequestError as err:
|
||||
# Supervisor currently does not transmit machine parsable error types
|
||||
message = err.args[0]
|
||||
if message.startswith("Invalid password for backup"):
|
||||
raise IncorrectPasswordError(message) from err
|
||||
raise HomeAssistantError(message) from err
|
||||
|
||||
restore_complete = asyncio.Event()
|
||||
|
||||
|
|
|
@ -997,6 +997,77 @@ async def test_reader_writer_restore(
|
|||
assert response["result"] is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("supervisor_error_string", "expected_error_code"),
|
||||
[
|
||||
(
|
||||
"Invalid password for backup",
|
||||
"password_incorrect",
|
||||
),
|
||||
(
|
||||
"Backup was made on supervisor version 2025.12.0, can't restore on 2024.12.0. Must update supervisor first.",
|
||||
"home_assistant_error",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("hassio_client", "setup_integration")
|
||||
async def test_reader_writer_restore_error(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
supervisor_client: AsyncMock,
|
||||
supervisor_error_string: str,
|
||||
expected_error_code: str,
|
||||
) -> None:
|
||||
"""Test restoring a backup."""
|
||||
client = await hass_ws_client(hass)
|
||||
supervisor_client.backups.partial_restore.side_effect = SupervisorBadRequestError(
|
||||
supervisor_error_string
|
||||
)
|
||||
supervisor_client.backups.list.return_value = [TEST_BACKUP]
|
||||
supervisor_client.backups.backup_info.return_value = TEST_BACKUP_DETAILS
|
||||
|
||||
await client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
response = await client.receive_json()
|
||||
assert response["event"] == {"manager_state": "idle"}
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{"type": "backup/restore", "agent_id": "hassio.local", "backup_id": "abc123"}
|
||||
)
|
||||
response = await client.receive_json()
|
||||
assert response["event"] == {
|
||||
"manager_state": "restore_backup",
|
||||
"stage": None,
|
||||
"state": "in_progress",
|
||||
}
|
||||
|
||||
supervisor_client.backups.partial_restore.assert_called_once_with(
|
||||
"abc123",
|
||||
supervisor_backups.PartialRestoreOptions(
|
||||
addons=None,
|
||||
background=True,
|
||||
folders=None,
|
||||
homeassistant=True,
|
||||
location=None,
|
||||
password=None,
|
||||
),
|
||||
)
|
||||
|
||||
response = await client.receive_json()
|
||||
assert response["event"] == {
|
||||
"manager_state": "restore_backup",
|
||||
"stage": None,
|
||||
"state": "failed",
|
||||
}
|
||||
|
||||
response = await client.receive_json()
|
||||
assert response["event"] == {"manager_state": "idle"}
|
||||
|
||||
response = await client.receive_json()
|
||||
assert response["error"]["code"] == expected_error_code
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("parameters", "expected_error"),
|
||||
[
|
||||
|
|
Loading…
Reference in New Issue