Remove class backup.backup.LocalBackup (#130919)

This commit is contained in:
Erik Montnemery
2024-11-19 01:32:51 +01:00
committed by GitHub
parent 1ea2ace356
commit 3cd69c32ec
5 changed files with 39 additions and 69 deletions

View File

@@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import asdict, dataclass
import json import json
from pathlib import Path from pathlib import Path
from tarfile import TarError from tarfile import TarError
@@ -24,17 +23,6 @@ async def async_get_backup_agents(
return [CoreLocalBackupAgent(hass)] return [CoreLocalBackupAgent(hass)]
@dataclass(slots=True)
class LocalBackup(BaseBackup):
"""Local backup class."""
path: Path
def as_dict(self) -> dict:
"""Return a dict representation of this backup."""
return {**asdict(self), "path": self.path.as_posix()}
class CoreLocalBackupAgent(LocalBackupAgent): class CoreLocalBackupAgent(LocalBackupAgent):
"""Local backup agent for Core and Container installations.""" """Local backup agent for Core and Container installations."""
@@ -45,7 +33,7 @@ class CoreLocalBackupAgent(LocalBackupAgent):
super().__init__() super().__init__()
self._hass = hass self._hass = hass
self._backup_dir = Path(hass.config.path("backups")) self._backup_dir = Path(hass.config.path("backups"))
self._backups: dict[str, LocalBackup] = {} self._backups: dict[str, BaseBackup] = {}
self._loaded_backups = False self._loaded_backups = False
async def load_backups(self) -> None: async def load_backups(self) -> None:
@@ -55,17 +43,16 @@ class CoreLocalBackupAgent(LocalBackupAgent):
self._backups = backups self._backups = backups
self._loaded_backups = True self._loaded_backups = True
def _read_backups(self) -> dict[str, LocalBackup]: def _read_backups(self) -> dict[str, BaseBackup]:
"""Read backups from disk.""" """Read backups from disk."""
backups: dict[str, LocalBackup] = {} backups: dict[str, BaseBackup] = {}
for backup_path in self._backup_dir.glob("*.tar"): for backup_path in self._backup_dir.glob("*.tar"):
try: try:
base_backup = read_backup(backup_path) base_backup = read_backup(backup_path)
backup = LocalBackup( backup = BaseBackup(
backup_id=base_backup.backup_id, backup_id=base_backup.backup_id,
name=base_backup.name, name=base_backup.name,
date=base_backup.date, date=base_backup.date,
path=backup_path,
size=round(backup_path.stat().st_size / 1_048_576, 2), size=round(backup_path.stat().st_size / 1_048_576, 2),
protected=base_backup.protected, protected=base_backup.protected,
) )
@@ -92,11 +79,10 @@ class CoreLocalBackupAgent(LocalBackupAgent):
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Upload a backup.""" """Upload a backup."""
self._backups[metadata.backup_id] = LocalBackup( self._backups[metadata.backup_id] = BaseBackup(
backup_id=metadata.backup_id, backup_id=metadata.backup_id,
date=metadata.date, date=metadata.date,
name=metadata.name, name=metadata.name,
path=path,
protected=metadata.protected, protected=metadata.protected,
size=round(path.stat().st_size / 1_048_576, 2), size=round(path.stat().st_size / 1_048_576, 2),
) )
@@ -111,7 +97,7 @@ class CoreLocalBackupAgent(LocalBackupAgent):
self, self,
backup_id: str, backup_id: str,
**kwargs: Any, **kwargs: Any,
) -> LocalBackup | None: ) -> BaseBackup | None:
"""Return a backup.""" """Return a backup."""
if not self._loaded_backups: if not self._loaded_backups:
await self.load_backups() await self.load_backups()
@@ -119,14 +105,15 @@ class CoreLocalBackupAgent(LocalBackupAgent):
if not (backup := self._backups.get(backup_id)): if not (backup := self._backups.get(backup_id)):
return None return None
if not await self._hass.async_add_executor_job(backup.path.exists): backup_path = self.get_backup_path(backup_id)
if not await self._hass.async_add_executor_job(backup_path.exists):
LOGGER.debug( LOGGER.debug(
( (
"Removing tracked backup (%s) that does not exists on the expected" "Removing tracked backup (%s) that does not exists on the expected"
" path %s" " path %s"
), ),
backup.backup_id, backup.backup_id,
backup.path, backup_path,
) )
self._backups.pop(backup_id) self._backups.pop(backup_id)
return None return None
@@ -139,9 +126,10 @@ class CoreLocalBackupAgent(LocalBackupAgent):
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."""
if (backup := await self.async_get_backup(backup_id)) is None: if await self.async_get_backup(backup_id) is None:
return return
await self._hass.async_add_executor_job(backup.path.unlink, True) backup_path = self.get_backup_path(backup_id)
LOGGER.debug("Removed backup located at %s", backup.path) await self._hass.async_add_executor_job(backup_path.unlink, True)
LOGGER.debug("Removed backup located at %s", backup_path)
self._backups.pop(backup_id) self._backups.pop(backup_id)

View File

@@ -13,7 +13,6 @@ from homeassistant.components.backup import (
BackupUploadMetadata, BackupUploadMetadata,
BaseBackup, BaseBackup,
) )
from homeassistant.components.backup.backup import LocalBackup
from homeassistant.components.backup.const import DATA_MANAGER from homeassistant.components.backup.const import DATA_MANAGER
from homeassistant.components.backup.manager import Backup from homeassistant.components.backup.manager import Backup
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@@ -32,14 +31,6 @@ TEST_BASE_BACKUP_ABC123 = BaseBackup(
size=0.0, size=0.0,
) )
TEST_BACKUP_PATH_ABC123 = Path("abc123.tar") TEST_BACKUP_PATH_ABC123 = Path("abc123.tar")
TEST_LOCAL_BACKUP_ABC123 = LocalBackup(
date="1970-01-01T00:00:00.000Z",
backup_id="abc123",
name="Test",
path=Path("abc123.tar"),
protected=False,
size=0.0,
)
TEST_BASE_BACKUP_DEF456 = BaseBackup( TEST_BASE_BACKUP_DEF456 = BaseBackup(
backup_id="def456", backup_id="def456",

View File

@@ -9,7 +9,7 @@ import pytest
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .common import TEST_LOCAL_BACKUP_ABC123, setup_backup_integration from .common import TEST_BASE_BACKUP_ABC123, setup_backup_integration
from tests.common import MockUser from tests.common import MockUser
from tests.typing import ClientSessionGenerator from tests.typing import ClientSessionGenerator
@@ -27,7 +27,7 @@ async def test_downloading_backup(
with ( with (
patch( patch(
"homeassistant.components.backup.backup.CoreLocalBackupAgent.async_get_backup", "homeassistant.components.backup.backup.CoreLocalBackupAgent.async_get_backup",
return_value=TEST_LOCAL_BACKUP_ABC123, return_value=TEST_BASE_BACKUP_ABC123,
), ),
patch("pathlib.Path.exists", return_value=True), patch("pathlib.Path.exists", return_value=True),
patch( patch(

View File

@@ -27,7 +27,6 @@ from .common import (
LOCAL_AGENT_ID, LOCAL_AGENT_ID,
TEST_BACKUP_PATH_ABC123, TEST_BACKUP_PATH_ABC123,
TEST_BASE_BACKUP_ABC123, TEST_BASE_BACKUP_ABC123,
TEST_LOCAL_BACKUP_ABC123,
BackupAgentTest, BackupAgentTest,
) )
@@ -150,14 +149,14 @@ async def test_load_backups(hass: HomeAssistant, snapshot: SnapshotAssertion) ->
patch( patch(
"homeassistant.components.backup.util.json_loads_object", "homeassistant.components.backup.util.json_loads_object",
return_value={ return_value={
"date": TEST_LOCAL_BACKUP_ABC123.date, "date": TEST_BASE_BACKUP_ABC123.date,
"name": TEST_LOCAL_BACKUP_ABC123.name, "name": TEST_BASE_BACKUP_ABC123.name,
"slug": TEST_LOCAL_BACKUP_ABC123.backup_id, "slug": TEST_BASE_BACKUP_ABC123.backup_id,
}, },
), ),
patch( patch(
"pathlib.Path.stat", "pathlib.Path.stat",
return_value=MagicMock(st_size=TEST_LOCAL_BACKUP_ABC123.size), return_value=MagicMock(st_size=TEST_BASE_BACKUP_ABC123.size),
), ),
): ):
await manager.backup_agents[LOCAL_AGENT_ID].load_backups() await manager.backup_agents[LOCAL_AGENT_ID].load_backups()
@@ -201,13 +200,11 @@ async def test_removing_backup(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = { local_agent._backups = {TEST_BASE_BACKUP_ABC123.backup_id: TEST_BASE_BACKUP_ABC123}
TEST_LOCAL_BACKUP_ABC123.backup_id: TEST_LOCAL_BACKUP_ABC123
}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with patch("pathlib.Path.exists", return_value=True): with patch("pathlib.Path.exists", return_value=True):
await manager.async_remove_backup(TEST_LOCAL_BACKUP_ABC123.backup_id) await manager.async_remove_backup(TEST_BASE_BACKUP_ABC123.backup_id)
assert "Removed backup located at" in caplog.text assert "Removed backup located at" in caplog.text
@@ -236,21 +233,20 @@ async def test_getting_backup_that_does_not_exist(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = { local_agent._backups = {TEST_BASE_BACKUP_ABC123.backup_id: TEST_BASE_BACKUP_ABC123}
TEST_LOCAL_BACKUP_ABC123.backup_id: TEST_LOCAL_BACKUP_ABC123
}
local_agent._loaded_backups = True local_agent._loaded_backups = True
path = local_agent.get_backup_path(TEST_BASE_BACKUP_ABC123.backup_id)
with patch("pathlib.Path.exists", return_value=False): with patch("pathlib.Path.exists", return_value=False):
backup, agent_errors = await manager.async_get_backup( backup, agent_errors = await manager.async_get_backup(
TEST_LOCAL_BACKUP_ABC123.backup_id TEST_BASE_BACKUP_ABC123.backup_id
) )
assert backup is None assert backup is None
assert agent_errors == {} assert agent_errors == {}
assert ( assert (
f"Removing tracked backup ({TEST_LOCAL_BACKUP_ABC123.backup_id}) that " f"Removing tracked backup ({TEST_BASE_BACKUP_ABC123.backup_id}) that "
f"does not exists on the expected path {TEST_LOCAL_BACKUP_ABC123.path}" f"does not exists on the expected path {path}"
) in caplog.text ) in caplog.text
@@ -526,9 +522,7 @@ async def test_async_trigger_restore(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = { local_agent._backups = {TEST_BASE_BACKUP_ABC123.backup_id: TEST_BASE_BACKUP_ABC123}
TEST_LOCAL_BACKUP_ABC123.backup_id: TEST_LOCAL_BACKUP_ABC123
}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with ( with (
@@ -537,7 +531,7 @@ async def test_async_trigger_restore(
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
): ):
await manager.async_restore_backup( await manager.async_restore_backup(
TEST_LOCAL_BACKUP_ABC123.backup_id, agent_id=LOCAL_AGENT_ID, password=None TEST_BASE_BACKUP_ABC123.backup_id, agent_id=LOCAL_AGENT_ID, password=None
) )
assert ( assert (
mocked_write_text.call_args[0][0] mocked_write_text.call_args[0][0]
@@ -557,9 +551,7 @@ async def test_async_trigger_restore_with_password(
await manager.load_platforms() await manager.load_platforms()
local_agent = manager.backup_agents[LOCAL_AGENT_ID] local_agent = manager.backup_agents[LOCAL_AGENT_ID]
local_agent._backups = { local_agent._backups = {TEST_BASE_BACKUP_ABC123.backup_id: TEST_BASE_BACKUP_ABC123}
TEST_LOCAL_BACKUP_ABC123.backup_id: TEST_LOCAL_BACKUP_ABC123
}
local_agent._loaded_backups = True local_agent._loaded_backups = True
with ( with (
@@ -568,7 +560,7 @@ async def test_async_trigger_restore_with_password(
patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call, patch("homeassistant.core.ServiceRegistry.async_call") as mocked_service_call,
): ):
await manager.async_restore_backup( await manager.async_restore_backup(
TEST_LOCAL_BACKUP_ABC123.backup_id, TEST_BASE_BACKUP_ABC123.backup_id,
agent_id=LOCAL_AGENT_ID, agent_id=LOCAL_AGENT_ID,
password="abc123", password="abc123",
) )
@@ -591,5 +583,5 @@ async def test_async_trigger_restore_missing_backup(hass: HomeAssistant) -> None
with pytest.raises(HomeAssistantError, match="Backup abc123 not found"): with pytest.raises(HomeAssistantError, match="Backup abc123 not found"):
await manager.async_restore_backup( await manager.async_restore_backup(
TEST_LOCAL_BACKUP_ABC123.backup_id, agent_id=LOCAL_AGENT_ID, password=None TEST_BASE_BACKUP_ABC123.backup_id, agent_id=LOCAL_AGENT_ID, password=None
) )

View File

@@ -19,7 +19,6 @@ from .common import (
LOCAL_AGENT_ID, LOCAL_AGENT_ID,
TEST_BASE_BACKUP_ABC123, TEST_BASE_BACKUP_ABC123,
TEST_BASE_BACKUP_DEF456, TEST_BASE_BACKUP_DEF456,
TEST_LOCAL_BACKUP_ABC123,
BackupAgentTest, BackupAgentTest,
setup_backup_integration, setup_backup_integration,
) )
@@ -68,7 +67,7 @@ async def test_info(
await setup_backup_integration( await setup_backup_integration(
hass, hass,
with_hassio=with_hassio, with_hassio=with_hassio,
backups={LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123]} | remote_backups, backups={LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123]} | remote_backups,
remote_agents=remote_agents, remote_agents=remote_agents,
) )
@@ -90,7 +89,7 @@ async def test_info_with_errors(
) -> None: ) -> None:
"""Test getting backup info with one unavailable agent.""" """Test getting backup info with one unavailable agent."""
await setup_backup_integration( await setup_backup_integration(
hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123]} hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123]}
) )
hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test") hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test")
@@ -106,13 +105,13 @@ async def test_info_with_errors(
("remote_agents", "backups"), ("remote_agents", "backups"),
[ [
([], {}), ([], {}),
(["remote"], {LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123]}), (["remote"], {LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123]}),
(["remote"], {"test.remote": [TEST_BASE_BACKUP_ABC123]}), (["remote"], {"test.remote": [TEST_BASE_BACKUP_ABC123]}),
(["remote"], {"test.remote": [TEST_BASE_BACKUP_DEF456]}), (["remote"], {"test.remote": [TEST_BASE_BACKUP_DEF456]}),
( (
["remote"], ["remote"],
{ {
LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123], LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123],
"test.remote": [TEST_BASE_BACKUP_ABC123], "test.remote": [TEST_BASE_BACKUP_ABC123],
}, },
), ),
@@ -159,7 +158,7 @@ async def test_details_with_errors(
) -> None: ) -> None:
"""Test getting backup info with one unavailable agent.""" """Test getting backup info with one unavailable agent."""
await setup_backup_integration( await setup_backup_integration(
hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123]} hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123]}
) )
hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test") hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest("test")
@@ -180,13 +179,13 @@ async def test_details_with_errors(
("remote_agents", "backups"), ("remote_agents", "backups"),
[ [
([], {}), ([], {}),
(["remote"], {LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123]}), (["remote"], {LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123]}),
(["remote"], {"test.remote": [TEST_BASE_BACKUP_ABC123]}), (["remote"], {"test.remote": [TEST_BASE_BACKUP_ABC123]}),
(["remote"], {"test.remote": [TEST_BASE_BACKUP_DEF456]}), (["remote"], {"test.remote": [TEST_BASE_BACKUP_DEF456]}),
( (
["remote"], ["remote"],
{ {
LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123], LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123],
"test.remote": [TEST_BASE_BACKUP_ABC123], "test.remote": [TEST_BASE_BACKUP_ABC123],
}, },
), ),
@@ -325,7 +324,7 @@ async def test_generate_without_hassio(
"backups", "backups",
[ [
{}, {},
{LOCAL_AGENT_ID: [TEST_LOCAL_BACKUP_ABC123]}, {LOCAL_AGENT_ID: [TEST_BASE_BACKUP_ABC123]},
], ],
) )
@pytest.mark.parametrize( @pytest.mark.parametrize(