mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 07:07:28 +00:00
Improve error message when failing to create backups (#139262)
* Improve error message when failing to create backups * Check for expected error message in tests
This commit is contained in:
parent
0f827fbf22
commit
ee01aa73b8
@ -1620,7 +1620,13 @@ class CoreBackupReaderWriter(BackupReaderWriter):
|
|||||||
"""Generate backup contents and return the size."""
|
"""Generate backup contents and return the size."""
|
||||||
if not tar_file_path:
|
if not tar_file_path:
|
||||||
tar_file_path = self.temp_backup_dir / f"{backup_data['slug']}.tar"
|
tar_file_path = self.temp_backup_dir / f"{backup_data['slug']}.tar"
|
||||||
make_backup_dir(tar_file_path.parent)
|
try:
|
||||||
|
make_backup_dir(tar_file_path.parent)
|
||||||
|
except OSError as err:
|
||||||
|
raise BackupReaderWriterError(
|
||||||
|
f"Failed to create dir {tar_file_path.parent}: "
|
||||||
|
f"{err} ({err.__class__.__name__})"
|
||||||
|
) from err
|
||||||
|
|
||||||
excludes = EXCLUDE_FROM_BACKUP
|
excludes = EXCLUDE_FROM_BACKUP
|
||||||
if not database_included:
|
if not database_included:
|
||||||
@ -1658,7 +1664,14 @@ class CoreBackupReaderWriter(BackupReaderWriter):
|
|||||||
file_filter=is_excluded_by_filter,
|
file_filter=is_excluded_by_filter,
|
||||||
arcname="data",
|
arcname="data",
|
||||||
)
|
)
|
||||||
return (tar_file_path, tar_file_path.stat().st_size)
|
try:
|
||||||
|
stat_result = tar_file_path.stat()
|
||||||
|
except OSError as err:
|
||||||
|
raise BackupReaderWriterError(
|
||||||
|
f"Error getting size of {tar_file_path}: "
|
||||||
|
f"{err} ({err.__class__.__name__})"
|
||||||
|
) from err
|
||||||
|
return (tar_file_path, stat_result.st_size)
|
||||||
|
|
||||||
async def async_receive_backup(
|
async def async_receive_backup(
|
||||||
self,
|
self,
|
||||||
|
@ -1311,7 +1311,7 @@ async def test_initiate_backup_with_task_error(
|
|||||||
(1, None, 1, None, 1, None, 1, OSError("Boom!")),
|
(1, None, 1, None, 1, None, 1, OSError("Boom!")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_initiate_backup_file_error(
|
async def test_initiate_backup_file_error_upload_to_agents(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
generate_backup_id: MagicMock,
|
generate_backup_id: MagicMock,
|
||||||
@ -1325,7 +1325,7 @@ async def test_initiate_backup_file_error(
|
|||||||
unlink_call_count: int,
|
unlink_call_count: int,
|
||||||
unlink_exception: Exception | None,
|
unlink_exception: Exception | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test file error during generate backup."""
|
"""Test file error during generate backup, while uploading to agents."""
|
||||||
agent_ids = ["test.remote"]
|
agent_ids = ["test.remote"]
|
||||||
|
|
||||||
await setup_backup_integration(hass, remote_agents=["test.remote"])
|
await setup_backup_integration(hass, remote_agents=["test.remote"])
|
||||||
@ -1418,6 +1418,122 @@ async def test_initiate_backup_file_error(
|
|||||||
assert unlink_mock.call_count == unlink_call_count
|
assert unlink_mock.call_count == unlink_call_count
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_backup_generation")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
(
|
||||||
|
"mkdir_call_count",
|
||||||
|
"mkdir_exception",
|
||||||
|
"atomic_contents_add_call_count",
|
||||||
|
"atomic_contents_add_exception",
|
||||||
|
"stat_call_count",
|
||||||
|
"stat_exception",
|
||||||
|
"error_message",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
(1, OSError("Boom!"), 0, None, 0, None, "Failed to create dir"),
|
||||||
|
(1, None, 1, OSError("Boom!"), 0, None, "Boom!"),
|
||||||
|
(1, None, 1, None, 1, OSError("Boom!"), "Error getting size"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_initiate_backup_file_error_create_backup(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
generate_backup_id: MagicMock,
|
||||||
|
path_glob: MagicMock,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
mkdir_call_count: int,
|
||||||
|
mkdir_exception: Exception | None,
|
||||||
|
atomic_contents_add_call_count: int,
|
||||||
|
atomic_contents_add_exception: Exception | None,
|
||||||
|
stat_call_count: int,
|
||||||
|
stat_exception: Exception | None,
|
||||||
|
error_message: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test file error during generate backup, while creating backup."""
|
||||||
|
agent_ids = ["test.remote"]
|
||||||
|
|
||||||
|
await setup_backup_integration(hass, remote_agents=["test.remote"])
|
||||||
|
|
||||||
|
ws_client = await hass_ws_client(hass)
|
||||||
|
|
||||||
|
path_glob.return_value = []
|
||||||
|
|
||||||
|
await ws_client.send_json_auto_id({"type": "backup/info"})
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
|
||||||
|
assert result["success"] is True
|
||||||
|
assert result["result"] == {
|
||||||
|
"backups": [],
|
||||||
|
"agent_errors": {},
|
||||||
|
"last_attempted_automatic_backup": None,
|
||||||
|
"last_completed_automatic_backup": None,
|
||||||
|
"last_non_idle_event": None,
|
||||||
|
"next_automatic_backup": None,
|
||||||
|
"next_automatic_backup_additional": False,
|
||||||
|
"state": "idle",
|
||||||
|
}
|
||||||
|
|
||||||
|
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||||
|
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["event"] == {"manager_state": BackupManagerState.IDLE}
|
||||||
|
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["success"] is True
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.backup.manager.atomic_contents_add",
|
||||||
|
side_effect=atomic_contents_add_exception,
|
||||||
|
) as atomic_contents_add_mock,
|
||||||
|
patch("pathlib.Path.mkdir", side_effect=mkdir_exception) as mkdir_mock,
|
||||||
|
patch("pathlib.Path.stat", side_effect=stat_exception) as stat_mock,
|
||||||
|
):
|
||||||
|
await ws_client.send_json_auto_id(
|
||||||
|
{"type": "backup/generate", "agent_ids": agent_ids}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["event"] == {
|
||||||
|
"manager_state": BackupManagerState.CREATE_BACKUP,
|
||||||
|
"reason": None,
|
||||||
|
"stage": None,
|
||||||
|
"state": CreateBackupState.IN_PROGRESS,
|
||||||
|
}
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["success"] is True
|
||||||
|
|
||||||
|
backup_id = result["result"]["backup_job_id"]
|
||||||
|
assert backup_id == generate_backup_id.return_value
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["event"] == {
|
||||||
|
"manager_state": BackupManagerState.CREATE_BACKUP,
|
||||||
|
"reason": None,
|
||||||
|
"stage": CreateBackupStage.HOME_ASSISTANT,
|
||||||
|
"state": CreateBackupState.IN_PROGRESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["event"] == {
|
||||||
|
"manager_state": BackupManagerState.CREATE_BACKUP,
|
||||||
|
"reason": "upload_failed",
|
||||||
|
"stage": None,
|
||||||
|
"state": CreateBackupState.FAILED,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await ws_client.receive_json()
|
||||||
|
assert result["event"] == {"manager_state": BackupManagerState.IDLE}
|
||||||
|
|
||||||
|
assert atomic_contents_add_mock.call_count == atomic_contents_add_call_count
|
||||||
|
assert mkdir_mock.call_count == mkdir_call_count
|
||||||
|
assert stat_mock.call_count == stat_call_count
|
||||||
|
|
||||||
|
assert error_message in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def _mock_local_backup_agent(name: str) -> Mock:
|
def _mock_local_backup_agent(name: str) -> Mock:
|
||||||
local_agent = mock_backup_agent(name)
|
local_agent = mock_backup_agent(name)
|
||||||
# This makes the local_agent pass isinstance checks for LocalBackupAgent
|
# This makes the local_agent pass isinstance checks for LocalBackupAgent
|
||||||
|
Loading…
x
Reference in New Issue
Block a user