Add agent delete backup (#130921)

* Add backup agent delete backup

* Remove agents delete websocket command

* Update docstring

Co-authored-by: Erik Montnemery <erik@montnemery.com>

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
Martin Hjelmare
2024-11-19 08:43:13 +01:00
committed by GitHub
parent 3cd69c32ec
commit 1a3a089cdd
8 changed files with 94 additions and 6 deletions

View File

@@ -55,6 +55,17 @@ class BackupAgent(abc.ABC):
:param metadata: Metadata about the backup that should be uploaded. :param metadata: Metadata about the backup that should be uploaded.
""" """
@abc.abstractmethod
async def async_delete_backup(
self,
backup_id: str,
**kwargs: Any,
) -> None:
"""Delete a backup file.
:param backup_id: The ID of the backup that was returned in async_list_backups.
"""
@abc.abstractmethod @abc.abstractmethod
async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]: async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
"""List backups.""" """List backups."""

View File

@@ -124,8 +124,8 @@ class CoreLocalBackupAgent(LocalBackupAgent):
"""Return the local path to a backup.""" """Return the local path to a backup."""
return self._backup_dir / f"{backup_id}.tar" return self._backup_dir / f"{backup_id}.tar"
async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None: async def async_delete_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Remove a backup.""" """Delete a backup file."""
if await self.async_get_backup(backup_id) is None: if await self.async_get_backup(backup_id) is None:
return return

View File

@@ -356,9 +356,7 @@ class BackupManager(BaseBackupManager[Backup]):
async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None: async def async_remove_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Remove a backup.""" """Remove a backup."""
for agent in self.backup_agents.values(): for agent in self.backup_agents.values():
if not hasattr(agent, "async_remove_backup"): await agent.async_delete_backup(backup_id)
continue
await agent.async_remove_backup(backup_id)
async def async_receive_backup( async def async_receive_backup(
self, self,

View File

@@ -43,6 +43,7 @@ class KitchenSinkBackupAgent(BackupAgent):
async def async_download_backup( async def async_download_backup(
self, self,
backup_id: str, backup_id: str,
*,
path: Path, path: Path,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
@@ -68,6 +69,17 @@ class KitchenSinkBackupAgent(BackupAgent):
) )
) )
async def async_delete_backup(
self,
backup_id: str,
**kwargs: Any,
) -> None:
"""Delete a backup file."""
self._uploads = [
upload for upload in self._uploads if upload.backup_id != backup_id
]
LOGGER.info("Deleted backup %s", backup_id)
async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]: async def async_list_backups(self, **kwargs: Any) -> list[BaseBackup]:
"""List synced backups.""" """List synced backups."""
return self._uploads return self._uploads

View File

@@ -99,6 +99,13 @@ class BackupAgentTest(BackupAgent):
"""Return a backup.""" """Return a backup."""
return self._backups.get(backup_id) return self._backups.get(backup_id)
async def async_delete_backup(
self,
backup_id: str,
**kwargs: Any,
) -> None:
"""Delete a backup file."""
async def setup_backup_integration( async def setup_backup_integration(
hass: HomeAssistant, hass: HomeAssistant,

View File

@@ -1227,6 +1227,14 @@
'type': 'result', 'type': 'result',
}) })
# --- # ---
# name: test_remove_agents_delete
dict({
'id': 1,
'result': None,
'success': True,
'type': 'result',
})
# ---
# name: test_restore_local_agent[with_hassio-backups0] # name: test_restore_local_agent[with_hassio-backups0]
dict({ dict({
'error': dict({ 'error': dict({

View File

@@ -2,7 +2,7 @@
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from unittest.mock import ANY, patch from unittest.mock import ANY, call, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
@@ -224,6 +224,30 @@ async def test_remove(
assert await client.receive_json() == snapshot assert await client.receive_json() == snapshot
async def test_remove_agents_delete(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
snapshot: SnapshotAssertion,
) -> None:
"""Test removing a backup file with a mock agent."""
await setup_backup_integration(hass)
hass.data[DATA_MANAGER].backup_agents = {"domain.test": BackupAgentTest("test")}
client = await hass_ws_client(hass)
await hass.async_block_till_done()
with patch.object(BackupAgentTest, "async_delete_backup") as delete_mock:
await client.send_json_auto_id(
{
"type": "backup/remove",
"backup_id": "abc123",
}
)
assert await client.receive_json() == snapshot
assert delete_mock.call_args == call("abc123")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"data", "data",
[ [

View File

@@ -152,3 +152,31 @@ async def test_agents_upload(
"protected": test_backup.protected, "protected": test_backup.protected,
"size": 0.0, "size": 0.0,
} }
async def test_agents_delete(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test backup agents delete."""
client = await hass_ws_client(hass)
backup_id = "abc123"
await client.send_json_auto_id(
{
"type": "backup/remove",
"backup_id": backup_id,
}
)
response = await client.receive_json()
assert response["success"]
assert f"Deleted backup {backup_id}" in caplog.text
await client.send_json_auto_id({"type": "backup/agents/list_backups"})
response = await client.receive_json()
assert response["success"]
backup_list = response["result"]
assert not backup_list