mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
Simplify error handling in BackupAgent when a backup is not found (#139754)
Simplify error handling in BackupAgent when backup is not found
This commit is contained in:
parent
c51a2317e1
commit
e55757dc82
@ -41,6 +41,8 @@ class BackupAgent(abc.ABC):
|
|||||||
) -> AsyncIterator[bytes]:
|
) -> AsyncIterator[bytes]:
|
||||||
"""Download a backup file.
|
"""Download a backup file.
|
||||||
|
|
||||||
|
Raises BackupNotFound if the backup does not exist.
|
||||||
|
|
||||||
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
||||||
:return: An async iterator that yields bytes.
|
:return: An async iterator that yields bytes.
|
||||||
"""
|
"""
|
||||||
@ -67,6 +69,8 @@ class BackupAgent(abc.ABC):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Delete a backup file.
|
"""Delete a backup file.
|
||||||
|
|
||||||
|
Raises BackupNotFound if the backup does not exist.
|
||||||
|
|
||||||
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -80,7 +84,10 @@ class BackupAgent(abc.ABC):
|
|||||||
backup_id: str,
|
backup_id: str,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> AgentBackup | None:
|
) -> AgentBackup | None:
|
||||||
"""Return a backup."""
|
"""Return a backup.
|
||||||
|
|
||||||
|
Raises BackupNotFound if the backup does not exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class LocalBackupAgent(BackupAgent):
|
class LocalBackupAgent(BackupAgent):
|
||||||
|
@ -88,13 +88,13 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
self,
|
self,
|
||||||
backup_id: str,
|
backup_id: str,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> AgentBackup | None:
|
) -> AgentBackup:
|
||||||
"""Return a backup."""
|
"""Return a backup."""
|
||||||
if not self._loaded_backups:
|
if not self._loaded_backups:
|
||||||
await self._load_backups()
|
await self._load_backups()
|
||||||
|
|
||||||
if backup_id not in self._backups:
|
if backup_id not in self._backups:
|
||||||
return None
|
raise BackupNotFound(f"Backup {backup_id} not found")
|
||||||
|
|
||||||
backup, backup_path = self._backups[backup_id]
|
backup, backup_path = self._backups[backup_id]
|
||||||
if not await self._hass.async_add_executor_job(backup_path.exists):
|
if not await self._hass.async_add_executor_job(backup_path.exists):
|
||||||
@ -107,7 +107,7 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
backup_path,
|
backup_path,
|
||||||
)
|
)
|
||||||
self._backups.pop(backup_id)
|
self._backups.pop(backup_id)
|
||||||
return None
|
raise BackupNotFound(f"Backup {backup_id} not found")
|
||||||
|
|
||||||
return backup
|
return backup
|
||||||
|
|
||||||
@ -130,10 +130,7 @@ class CoreLocalBackupAgent(LocalBackupAgent):
|
|||||||
if not self._loaded_backups:
|
if not self._loaded_backups:
|
||||||
await self._load_backups()
|
await self._load_backups()
|
||||||
|
|
||||||
try:
|
backup_path = self.get_backup_path(backup_id)
|
||||||
backup_path = self.get_backup_path(backup_id)
|
|
||||||
except BackupNotFound:
|
|
||||||
return
|
|
||||||
await self._hass.async_add_executor_job(backup_path.unlink, True)
|
await self._hass.async_add_executor_job(backup_path.unlink, True)
|
||||||
LOGGER.debug("Deleted backup located at %s", backup_path)
|
LOGGER.debug("Deleted backup located at %s", backup_path)
|
||||||
self._backups.pop(backup_id)
|
self._backups.pop(backup_id)
|
||||||
|
@ -59,10 +59,13 @@ class DownloadBackupView(HomeAssistantView):
|
|||||||
if agent_id not in manager.backup_agents:
|
if agent_id not in manager.backup_agents:
|
||||||
return Response(status=HTTPStatus.BAD_REQUEST)
|
return Response(status=HTTPStatus.BAD_REQUEST)
|
||||||
agent = manager.backup_agents[agent_id]
|
agent = manager.backup_agents[agent_id]
|
||||||
backup = await agent.async_get_backup(backup_id)
|
try:
|
||||||
|
backup = await agent.async_get_backup(backup_id)
|
||||||
|
except BackupNotFound:
|
||||||
|
return Response(status=HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
# We don't need to check if the path exists, aiohttp.FileResponse will handle
|
# Check for None to be backwards compatible with the old BackupAgent API,
|
||||||
# that
|
# this can be removed in HA Core 2025.10
|
||||||
if backup is None:
|
if backup is None:
|
||||||
return Response(status=HTTPStatus.NOT_FOUND)
|
return Response(status=HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
@ -92,6 +95,8 @@ class DownloadBackupView(HomeAssistantView):
|
|||||||
) -> StreamResponse | FileResponse | Response:
|
) -> StreamResponse | FileResponse | Response:
|
||||||
if agent_id in manager.local_backup_agents:
|
if agent_id in manager.local_backup_agents:
|
||||||
local_agent = manager.local_backup_agents[agent_id]
|
local_agent = manager.local_backup_agents[agent_id]
|
||||||
|
# We don't need to check if the path exists, aiohttp.FileResponse will
|
||||||
|
# handle that
|
||||||
path = local_agent.get_backup_path(backup_id)
|
path = local_agent.get_backup_path(backup_id)
|
||||||
return FileResponse(path=path.as_posix(), headers=headers)
|
return FileResponse(path=path.as_posix(), headers=headers)
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ from .models import (
|
|||||||
AgentBackup,
|
AgentBackup,
|
||||||
BackupError,
|
BackupError,
|
||||||
BackupManagerError,
|
BackupManagerError,
|
||||||
|
BackupNotFound,
|
||||||
BackupReaderWriterError,
|
BackupReaderWriterError,
|
||||||
BaseBackup,
|
BaseBackup,
|
||||||
Folder,
|
Folder,
|
||||||
@ -648,6 +649,8 @@ class BackupManager:
|
|||||||
)
|
)
|
||||||
for idx, result in enumerate(get_backup_results):
|
for idx, result in enumerate(get_backup_results):
|
||||||
agent_id = agent_ids[idx]
|
agent_id = agent_ids[idx]
|
||||||
|
if isinstance(result, BackupNotFound):
|
||||||
|
continue
|
||||||
if isinstance(result, BackupAgentError):
|
if isinstance(result, BackupAgentError):
|
||||||
agent_errors[agent_id] = result
|
agent_errors[agent_id] = result
|
||||||
continue
|
continue
|
||||||
@ -659,6 +662,8 @@ class BackupManager:
|
|||||||
continue
|
continue
|
||||||
if isinstance(result, BaseException):
|
if isinstance(result, BaseException):
|
||||||
raise result # unexpected error
|
raise result # unexpected error
|
||||||
|
# Check for None to be backwards compatible with the old BackupAgent API,
|
||||||
|
# this can be removed in HA Core 2025.10
|
||||||
if not result:
|
if not result:
|
||||||
continue
|
continue
|
||||||
if backup is None:
|
if backup is None:
|
||||||
@ -723,6 +728,8 @@ class BackupManager:
|
|||||||
)
|
)
|
||||||
for idx, result in enumerate(delete_backup_results):
|
for idx, result in enumerate(delete_backup_results):
|
||||||
agent_id = agent_ids[idx]
|
agent_id = agent_ids[idx]
|
||||||
|
if isinstance(result, BackupNotFound):
|
||||||
|
continue
|
||||||
if isinstance(result, BackupAgentError):
|
if isinstance(result, BackupAgentError):
|
||||||
agent_errors[agent_id] = result
|
agent_errors[agent_id] = result
|
||||||
continue
|
continue
|
||||||
@ -832,7 +839,7 @@ class BackupManager:
|
|||||||
agent_errors = {
|
agent_errors = {
|
||||||
backup_id: error
|
backup_id: error
|
||||||
for backup_id, error in zip(backup_ids, delete_results, strict=True)
|
for backup_id, error in zip(backup_ids, delete_results, strict=True)
|
||||||
if error
|
if error and not isinstance(error, BackupNotFound)
|
||||||
}
|
}
|
||||||
if agent_errors:
|
if agent_errors:
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
@ -1264,7 +1271,15 @@ class BackupManager:
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initiate restoring a backup."""
|
"""Initiate restoring a backup."""
|
||||||
agent = self.backup_agents[agent_id]
|
agent = self.backup_agents[agent_id]
|
||||||
if not await agent.async_get_backup(backup_id):
|
try:
|
||||||
|
backup = await agent.async_get_backup(backup_id)
|
||||||
|
except BackupNotFound as err:
|
||||||
|
raise BackupManagerError(
|
||||||
|
f"Backup {backup_id} not found in agent {agent_id}"
|
||||||
|
) from err
|
||||||
|
# Check for None to be backwards compatible with the old BackupAgent API,
|
||||||
|
# this can be removed in HA Core 2025.10
|
||||||
|
if not backup:
|
||||||
raise BackupManagerError(
|
raise BackupManagerError(
|
||||||
f"Backup {backup_id} not found in agent {agent_id}"
|
f"Backup {backup_id} not found in agent {agent_id}"
|
||||||
)
|
)
|
||||||
@ -1352,7 +1367,15 @@ class BackupManager:
|
|||||||
agent = self.backup_agents[agent_id]
|
agent = self.backup_agents[agent_id]
|
||||||
except KeyError as err:
|
except KeyError as err:
|
||||||
raise BackupManagerError(f"Invalid agent selected: {agent_id}") from err
|
raise BackupManagerError(f"Invalid agent selected: {agent_id}") from err
|
||||||
if not await agent.async_get_backup(backup_id):
|
try:
|
||||||
|
backup = await agent.async_get_backup(backup_id)
|
||||||
|
except BackupNotFound as err:
|
||||||
|
raise BackupManagerError(
|
||||||
|
f"Backup {backup_id} not found in agent {agent_id}"
|
||||||
|
) from err
|
||||||
|
# Check for None to be backwards compatible with the old BackupAgent API,
|
||||||
|
# this can be removed in HA Core 2025.10
|
||||||
|
if not backup:
|
||||||
raise BackupManagerError(
|
raise BackupManagerError(
|
||||||
f"Backup {backup_id} not found in agent {agent_id}"
|
f"Backup {backup_id} not found in agent {agent_id}"
|
||||||
)
|
)
|
||||||
|
@ -67,15 +67,20 @@ async def aiter_from_iter(iterable: Iterable) -> AsyncIterator:
|
|||||||
def mock_backup_agent(name: str, backups: list[AgentBackup] | None = None) -> Mock:
|
def mock_backup_agent(name: str, backups: list[AgentBackup] | None = None) -> Mock:
|
||||||
"""Create a mock backup agent."""
|
"""Create a mock backup agent."""
|
||||||
|
|
||||||
|
async def delete_backup(backup_id: str, **kwargs: Any) -> None:
|
||||||
|
"""Mock delete."""
|
||||||
|
get_backup(backup_id)
|
||||||
|
|
||||||
async def download_backup(backup_id: str, **kwargs: Any) -> AsyncIterator[bytes]:
|
async def download_backup(backup_id: str, **kwargs: Any) -> AsyncIterator[bytes]:
|
||||||
"""Mock download."""
|
"""Mock download."""
|
||||||
if not await get_backup(backup_id):
|
|
||||||
raise BackupNotFound
|
|
||||||
return aiter_from_iter((backups_data.get(backup_id, b"backup data"),))
|
return aiter_from_iter((backups_data.get(backup_id, b"backup data"),))
|
||||||
|
|
||||||
async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup | None:
|
async def get_backup(backup_id: str, **kwargs: Any) -> AgentBackup:
|
||||||
"""Get a backup."""
|
"""Get a backup."""
|
||||||
return next((b for b in backups if b.backup_id == backup_id), None)
|
backup = next((b for b in backups if b.backup_id == backup_id), None)
|
||||||
|
if backup is None:
|
||||||
|
raise BackupNotFound
|
||||||
|
return backup
|
||||||
|
|
||||||
async def upload_backup(
|
async def upload_backup(
|
||||||
*,
|
*,
|
||||||
@ -99,7 +104,7 @@ def mock_backup_agent(name: str, backups: list[AgentBackup] | None = None) -> Mo
|
|||||||
mock_agent.unique_id = name
|
mock_agent.unique_id = name
|
||||||
type(mock_agent).agent_id = BackupAgent.agent_id
|
type(mock_agent).agent_id = BackupAgent.agent_id
|
||||||
mock_agent.async_delete_backup = AsyncMock(
|
mock_agent.async_delete_backup = AsyncMock(
|
||||||
spec_set=[BackupAgent.async_delete_backup]
|
side_effect=delete_backup, spec_set=[BackupAgent.async_delete_backup]
|
||||||
)
|
)
|
||||||
mock_agent.async_download_backup = AsyncMock(
|
mock_agent.async_download_backup = AsyncMock(
|
||||||
side_effect=download_backup, spec_set=[BackupAgent.async_download_backup]
|
side_effect=download_backup, spec_set=[BackupAgent.async_download_backup]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user