Refactor BackupManager (#130947)

* Refactor BackupManager

* Adjust

* Adjust backup creation

* Copy in executor
This commit is contained in:
Erik Montnemery
2024-11-19 19:09:15 +01:00
committed by GitHub
parent 4a44d19c53
commit 20488721bd
6 changed files with 232 additions and 155 deletions

View File

@@ -11,7 +11,12 @@ from homeassistant.helpers.typing import ConfigType
from .agent import BackupAgent, BackupAgentPlatformProtocol
from .const import DOMAIN, LOGGER
from .http import async_register_http_views
from .manager import Backup, BackupManager, BackupPlatformProtocol
from .manager import (
Backup,
BackupManager,
BackupPlatformProtocol,
CoreBackupReaderWriter,
)
from .models import BackupUploadMetadata, BaseBackup
from .websocket import async_register_websocket_handlers
@@ -31,7 +36,9 @@ SERVICE_CREATE_SCHEMA = vol.Schema({vol.Optional(CONF_PASSWORD): str})
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Backup integration."""
hass.data[DOMAIN] = backup_manager = BackupManager(hass)
hass.data[DOMAIN] = backup_manager = BackupManager(
hass, CoreBackupReaderWriter(hass)
)
await backup_manager.async_setup()
with_hassio = is_hassio(hass)

View File

@@ -8,12 +8,11 @@ from typing import TYPE_CHECKING
from homeassistant.util.hass_dict import HassKey
if TYPE_CHECKING:
from .manager import BaseBackupManager
from .models import BaseBackup
from .manager import BackupManager
BUF_SIZE = 2**20 * 4 # 4MB
DOMAIN = "backup"
DATA_MANAGER: HassKey[BaseBackupManager[BaseBackup]] = HassKey(DOMAIN)
DATA_MANAGER: HassKey[BackupManager] = HassKey(DOMAIN)
LOGGER = getLogger(__package__)
EXCLUDE_FROM_BACKUP = [

View File

@@ -15,7 +15,6 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.util import slugify
from .const import DATA_MANAGER
from .manager import BackupManager
# pylint: disable=fixme
# TODO: Don't forget to remove this when the implementation is complete
@@ -47,7 +46,7 @@ class DownloadBackupView(HomeAssistantView):
except KeyError:
return Response(status=HTTPStatus.BAD_REQUEST)
manager = cast(BackupManager, request.app[KEY_HASS].data[DATA_MANAGER])
manager = request.app[KEY_HASS].data[DATA_MANAGER]
if agent_id not in manager.backup_agents:
return Response(status=HTTPStatus.BAD_REQUEST)
agent = manager.backup_agents[agent_id]

View File

@@ -15,11 +15,10 @@ import shutil
import tarfile
from tempfile import TemporaryDirectory
import time
from typing import Any, Generic, Protocol
from typing import TYPE_CHECKING, Any, Protocol
import aiohttp
from securetar import SecureTarFile, atomic_contents_add
from typing_extensions import TypeVar
from homeassistant.backup_restore import RESTORE_BACKUP_FILE, password_to_key
from homeassistant.const import __version__ as HAVERSION
@@ -38,6 +37,7 @@ from .agent import (
from .config import BackupConfig
from .const import (
BUF_SIZE,
DATA_MANAGER,
DOMAIN,
EXCLUDE_DATABASE_FROM_BACKUP,
EXCLUDE_FROM_BACKUP,
@@ -46,8 +46,6 @@ from .const import (
from .models import BackupUploadMetadata, BaseBackup
from .util import read_backup
_BackupT = TypeVar("_BackupT", bound=BaseBackup, default=BaseBackup)
@dataclass(slots=True)
class NewBackup:
@@ -82,24 +80,62 @@ class BackupPlatformProtocol(Protocol):
"""Perform operations after a backup finishes."""
class BaseBackupManager(abc.ABC, Generic[_BackupT]):
class BackupReaderWriter(abc.ABC):
"""Abstract class for reading and writing backups."""
temp_backup_dir: Path
@abc.abstractmethod
async def async_create_backup(
self,
*,
addons_included: list[str] | None,
agent_ids: list[str],
database_included: bool,
backup_name: str,
folders_included: list[str] | None,
on_progress: Callable[[BackupProgress], None] | None,
password: str | None,
) -> tuple[NewBackup, asyncio.Task[tuple[BaseBackup, Path]]]:
"""Create a backup."""
@abc.abstractmethod
async def async_restore_backup(
self,
backup_id: str,
*,
agent_id: str,
password: str | None,
**kwargs: Any,
) -> None:
"""Restore a backup."""
class BackupManager:
"""Define the format that backup managers can have."""
def __init__(self, hass: HomeAssistant) -> None:
def __init__(self, hass: HomeAssistant, reader_writer: BackupReaderWriter) -> None:
"""Initialize the backup manager."""
self.hass = hass
self.backup_task: asyncio.Task | None = None
self.backup_task: asyncio.Task[tuple[BaseBackup, Path]] | None = None
self.finish_backup_task: asyncio.Task[None] | None = None
self.platforms: dict[str, BackupPlatformProtocol] = {}
self.backup_agents: dict[str, BackupAgent] = {}
self.local_backup_agents: dict[str, LocalBackupAgent] = {}
self.config = BackupConfig(hass)
self.syncing = False
self._reader_writer = reader_writer
async def async_setup(self) -> None:
"""Set up the backup manager."""
await self.config.load()
await self.load_platforms()
@property
def temp_backup_dir(self) -> Path:
"""Return the temporary backup directory."""
return self._reader_writer.temp_backup_dir
@callback
def _add_platform_pre_post_handler(
self,
@@ -182,74 +218,6 @@ class BaseBackupManager(abc.ABC, Generic[_BackupT]):
LOGGER.debug("Loaded %s platforms", len(self.platforms))
LOGGER.debug("Loaded %s agents", len(self.backup_agents))
@abc.abstractmethod
async def async_restore_backup(
self,
backup_id: str,
*,
agent_id: str,
password: str | None,
**kwargs: Any,
) -> None:
"""Restore a backup."""
@abc.abstractmethod
async def async_create_backup(
self,
*,
addons_included: list[str] | None,
agent_ids: list[str],
database_included: bool,
folders_included: list[str] | None,
name: str | None,
on_progress: Callable[[BackupProgress], None] | None,
password: str | None,
**kwargs: Any,
) -> NewBackup:
"""Initiate generating a backup.
:param on_progress: A callback that will be called with the progress of the
backup.
"""
@abc.abstractmethod
async def async_get_backups(
self, **kwargs: Any
) -> tuple[dict[str, Backup], dict[str, Exception]]:
"""Get backups.
Return a dictionary of Backup instances keyed by their ID.
"""
@abc.abstractmethod
async def async_get_backup(
self, backup_id: str, **kwargs: Any
) -> tuple[_BackupT | None, dict[str, Exception]]:
"""Get a backup."""
@abc.abstractmethod
async def async_delete_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Delete a backup."""
@abc.abstractmethod
async def async_receive_backup(
self,
*,
agent_ids: list[str],
contents: aiohttp.BodyPartReader,
**kwargs: Any,
) -> None:
"""Receive and store a backup file from upload."""
class BackupManager(BaseBackupManager[Backup]):
"""Backup manager for the Backup integration."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the backup manager."""
super().__init__(hass=hass)
self.temp_backup_dir = Path(hass.config.path("tmp_backups"))
async def _async_upload_backup(
self,
*,
@@ -288,7 +256,10 @@ class BackupManager(BaseBackupManager[Backup]):
async def async_get_backups(
self, **kwargs: Any
) -> tuple[dict[str, Backup], dict[str, Exception]]:
"""Return backups."""
"""Get backups.
Return a dictionary of Backup instances keyed by their ID.
"""
backups: dict[str, Backup] = {}
agent_errors: dict[str, Exception] = {}
agent_ids = list(self.backup_agents)
@@ -320,7 +291,7 @@ class BackupManager(BaseBackupManager[Backup]):
async def async_get_backup(
self, backup_id: str, **kwargs: Any
) -> tuple[Backup | None, dict[str, Exception]]:
"""Return a backup."""
"""Get a backup."""
backup: Backup | None = None
agent_errors: dict[str, Exception] = {}
agent_ids = list(self.backup_agents.keys())
@@ -453,7 +424,11 @@ class BackupManager(BaseBackupManager[Backup]):
password: str | None,
**kwargs: Any,
) -> NewBackup:
"""Initiate generating a backup."""
"""Initiate generating a backup.
:param on_progress: A callback that will be called with the progress of the
backup.
"""
if self.backup_task:
raise HomeAssistantError("Backup already in progress")
if not agent_ids:
@@ -461,9 +436,118 @@ class BackupManager(BaseBackupManager[Backup]):
if any(agent_id not in self.backup_agents for agent_id in agent_ids):
raise HomeAssistantError("Invalid agent selected")
backup_name = name or f"Core {HAVERSION}"
new_backup, self.backup_task = await self._reader_writer.async_create_backup(
addons_included=addons_included,
agent_ids=agent_ids,
backup_name=backup_name,
database_included=database_included,
folders_included=folders_included,
on_progress=on_progress,
password=password,
)
self.finish_backup_task = self.hass.async_create_task(
self._async_finish_backup(agent_ids),
name="backup_manager_finish_backup",
)
return new_backup
async def _async_finish_backup(self, agent_ids: list[str]) -> None:
if TYPE_CHECKING:
assert self.backup_task is not None
try:
backup, tar_file_path = await self.backup_task
except Exception as err: # noqa: BLE001
LOGGER.debug("Backup upload failed", exc_info=err)
else:
LOGGER.debug(
"Generated new backup with backup_id %s, uploading to agents %s",
backup.backup_id,
agent_ids,
)
local_file_paths = [
self.local_backup_agents[agent_id].get_backup_path(backup.backup_id)
for agent_id in agent_ids
if agent_id in self.local_backup_agents
]
keep_path = False
for local_path in local_file_paths:
if local_path == tar_file_path:
keep_path = True
continue
await self.hass.async_add_executor_job(
shutil.copy, tar_file_path, local_path
)
await self._async_upload_backup(
backup=backup, agent_ids=agent_ids, path=tar_file_path
)
if not keep_path:
await self.hass.async_add_executor_job(tar_file_path.unlink, True)
finally:
self.backup_task = None
self.finish_backup_task = None
async def async_restore_backup(
self,
backup_id: str,
*,
agent_id: str,
password: str | None,
**kwargs: Any,
) -> None:
"""Initiate restoring a backup.
:param on_progress: A callback that will be called with the progress of the
restore. Home Assistant Core may need to be restarted during the backup
restore process, which means the restore process may not be able to report
when it's done.
"""
if agent_id in self.local_backup_agents:
local_agent = self.local_backup_agents[agent_id]
if not await local_agent.async_get_backup(backup_id):
raise HomeAssistantError(
f"Backup {backup_id} not found in agent {agent_id}"
)
else:
path = self.temp_backup_dir / f"{backup_id}.tar"
agent = self.backup_agents[agent_id]
if not await agent.async_get_backup(backup_id):
raise HomeAssistantError(
f"Backup {backup_id} not found in agent {agent_id}"
)
await agent.async_download_backup(backup_id, path=path)
await self._reader_writer.async_restore_backup(
backup_id=backup_id,
agent_id=agent_id,
password=password,
)
class CoreBackupReaderWriter(BackupReaderWriter):
"""Class for reading and writing backups in core and container installations."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the backup reader/writer."""
self._hass = hass
self.temp_backup_dir = Path(hass.config.path("tmp_backups"))
async def async_create_backup(
self,
*,
addons_included: list[str] | None,
agent_ids: list[str],
database_included: bool,
backup_name: str,
folders_included: list[str] | None,
on_progress: Callable[[BackupProgress], None] | None,
password: str | None,
) -> tuple[NewBackup, asyncio.Task[tuple[BaseBackup, Path]]]:
"""Initiate generating a backup."""
date_str = dt_util.now().isoformat()
backup_id = _generate_backup_id(date_str, backup_name)
self.backup_task = self.hass.async_create_task(
backup_task = self._hass.async_create_task(
self._async_create_backup(
addons_included=addons_included,
agent_ids=agent_ids,
@@ -478,32 +562,35 @@ class BackupManager(BaseBackupManager[Backup]):
name="backup_manager_create_backup",
eager_start=False, # To ensure the task is not started before we return
)
return NewBackup(backup_id=backup_id)
return (NewBackup(backup_id=backup_id), backup_task)
async def _async_create_backup(
self,
*,
addons_included: list[str] | None,
agent_ids: list[str],
backup_id: str,
database_included: bool,
backup_name: str,
date_str: str,
folders_included: list[str] | None,
on_progress: Callable[[BackupProgress], None] | None,
password: str | None,
backup_id: str,
) -> BaseBackup:
) -> tuple[BaseBackup, Path]:
"""Generate a backup."""
manager = self._hass.data[DATA_MANAGER]
success = False
local_file_paths = [
self.local_backup_agents[agent_id].get_backup_path(backup_id)
for agent_id in agent_ids
if agent_id in self.local_backup_agents
]
suggested_tar_file_path = None
for agent_id in agent_ids:
if local_agent := manager.local_backup_agents.get(agent_id):
suggested_tar_file_path = local_agent.get_backup_path(backup_id)
break
try:
await self.async_pre_backup_actions()
# Inform integrations a backup is about to be made
await manager.async_pre_backup_actions()
backup_data = {
"compressed": True,
@@ -519,12 +606,12 @@ class BackupManager(BaseBackupManager[Backup]):
"type": "partial",
}
tar_file_path, size_in_bytes = await self.hass.async_add_executor_job(
tar_file_path, size_in_bytes = await self._hass.async_add_executor_job(
self._mkdir_and_generate_backup_contents,
local_file_paths,
backup_data,
database_included,
password,
suggested_tar_file_path,
)
backup = BaseBackup(
backup_id=backup_id,
@@ -533,35 +620,23 @@ class BackupManager(BaseBackupManager[Backup]):
protected=password is not None,
size=round(size_in_bytes / 1_048_576, 2),
)
LOGGER.debug(
"Generated new backup with backup_id %s, uploading to agents %s",
backup_id,
agent_ids,
)
await self._async_upload_backup(
backup=backup, agent_ids=agent_ids, path=tar_file_path
)
if not local_file_paths:
await self.hass.async_add_executor_job(tar_file_path.unlink, True)
success = True
return backup
return (backup, tar_file_path)
finally:
if on_progress:
on_progress(BackupProgress(done=True, stage=None, success=success))
self.backup_task = None
await self.async_post_backup_actions()
# Inform integrations the backup is done
await manager.async_post_backup_actions()
def _mkdir_and_generate_backup_contents(
self,
tar_file_paths: list[Path],
backup_data: dict[str, Any],
database_included: bool,
password: str | None,
tar_file_path: Path | None,
) -> tuple[Path, int]:
"""Generate backup contents and return the size."""
if tar_file_paths:
tar_file_path = tar_file_paths[0]
else:
if not tar_file_path:
tar_file_path = self.temp_backup_dir / f"{backup_data['slug']}.tar"
if not (backup_dir := tar_file_path.parent).exists():
LOGGER.debug("Creating backup directory %s", backup_dir)
@@ -588,12 +663,10 @@ class BackupManager(BaseBackupManager[Backup]):
) as core_tar:
atomic_contents_add(
tar_file=core_tar,
origin_path=Path(self.hass.config.path()),
origin_path=Path(self._hass.config.path()),
excludes=excludes,
arcname="data",
)
for local_path in tar_file_paths[1:]:
shutil.copy(tar_file_path, local_path)
return (tar_file_path, tar_file_path.stat().st_size)
async def async_restore_backup(
@@ -610,31 +683,22 @@ class BackupManager(BaseBackupManager[Backup]):
will be handled during startup by the restore_backup module.
"""
if agent_id in self.local_backup_agents:
local_agent = self.local_backup_agents[agent_id]
if not await local_agent.async_get_backup(backup_id):
raise HomeAssistantError(
f"Backup {backup_id} not found in agent {agent_id}"
)
manager = self._hass.data[DATA_MANAGER]
if agent_id in manager.local_backup_agents:
local_agent = manager.local_backup_agents[agent_id]
path = local_agent.get_backup_path(backup_id)
else:
path = self.temp_backup_dir / f"{backup_id}.tar"
agent = self.backup_agents[agent_id]
if not await agent.async_get_backup(backup_id):
raise HomeAssistantError(
f"Backup {backup_id} not found in agent {agent_id}"
)
await agent.async_download_backup(backup_id, path=path)
def _write_restore_file() -> None:
"""Write the restore file."""
Path(self.hass.config.path(RESTORE_BACKUP_FILE)).write_text(
Path(self._hass.config.path(RESTORE_BACKUP_FILE)).write_text(
json.dumps({"path": path.as_posix(), "password": password}),
encoding="utf-8",
)
await self.hass.async_add_executor_job(_write_restore_file)
await self.hass.services.async_call("homeassistant", "restart", {})
await self._hass.async_add_executor_job(_write_restore_file)
await self._hass.services.async_call("homeassistant", "restart", {})
def _generate_backup_id(date: str, name: str) -> str:

View File

@@ -1,6 +1,6 @@
"""Websocket commands for the Backup integration."""
from typing import Any, cast
from typing import Any
import voluptuous as vol
@@ -8,7 +8,7 @@ from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from .const import DATA_MANAGER, LOGGER
from .manager import BackupManager, BackupProgress
from .manager import BackupProgress
@callback
@@ -254,7 +254,7 @@ async def backup_agents_download(
msg: dict[str, Any],
) -> None:
"""Download an uploaded backup."""
manager = cast(BackupManager, hass.data[DATA_MANAGER])
manager = hass.data[DATA_MANAGER]
if not (agent := manager.backup_agents.get(msg["agent_id"])):
connection.send_error(
msg["id"], "unknown_agent", f"Agent {msg['agent_id']} not found"

View File

@@ -18,7 +18,11 @@ from homeassistant.components.backup import (
BaseBackup,
backup as local_backup_platform,
)
from homeassistant.components.backup.manager import BackupProgress
from homeassistant.components.backup.const import DATA_MANAGER
from homeassistant.components.backup.manager import (
BackupProgress,
CoreBackupReaderWriter,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
@@ -71,7 +75,8 @@ async def _mock_backup_generation(
assert manager.backup_task is not None
assert progress == []
backup = await manager.backup_task
backup, _ = await manager.backup_task
await manager.finish_backup_task
assert progress == [BackupProgress(done=True, stage=None, success=True)]
assert mocked_json_bytes.call_count == 1
@@ -132,13 +137,13 @@ async def _setup_backup_platform(
async def test_constructor(hass: HomeAssistant) -> None:
"""Test BackupManager constructor."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
assert manager.temp_backup_dir.as_posix() == hass.config.path("tmp_backups")
async def test_load_backups(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None:
"""Test loading backups."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -170,7 +175,7 @@ async def test_load_backups_with_exception(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test loading backups with exception."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -194,7 +199,7 @@ async def test_deleting_backup(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test deleting backup."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -213,7 +218,7 @@ async def test_deleting_non_existing_backup(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test deleting not existing backup."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -227,7 +232,7 @@ async def test_getting_backup_that_does_not_exist(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test getting backup that does not exist."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -253,7 +258,7 @@ async def test_getting_backup_that_does_not_exist(
async def test_async_create_backup_when_backing_up(hass: HomeAssistant) -> None:
"""Test generate backup."""
event = asyncio.Event()
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
manager.backup_task = hass.async_create_task(event.wait())
with pytest.raises(HomeAssistantError, match="Backup already in progress"):
await manager.async_create_backup(
@@ -279,7 +284,7 @@ async def test_async_create_backup_wrong_agent_id(
hass: HomeAssistant, agent_ids: list[str], expected_error: str
) -> None:
"""Test generate backup."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
with pytest.raises(HomeAssistantError, match=expected_error):
await manager.async_create_backup(
addons_included=[],
@@ -320,7 +325,8 @@ async def test_async_create_backup(
backup_directory: str,
) -> None:
"""Test generate backup."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
hass.data[DATA_MANAGER] = manager
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await _setup_backup_platform(
@@ -358,7 +364,7 @@ async def test_loading_platforms(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test loading backup platforms."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
assert not manager.platforms
@@ -383,7 +389,7 @@ async def test_loading_agents(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test loading backup agents."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
assert not manager.platforms
@@ -407,7 +413,7 @@ async def test_not_loading_bad_platforms(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test loading backup platforms."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
assert not manager.platforms
@@ -424,7 +430,7 @@ async def test_exception_plaform_pre(
hass: HomeAssistant, mocked_json_bytes: Mock, mocked_tarfile: Mock
) -> None:
"""Test exception in pre step."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
manager.loaded_backups = True
async def _mock_step(hass: HomeAssistant) -> None:
@@ -447,7 +453,7 @@ async def test_exception_plaform_post(
hass: HomeAssistant, mocked_json_bytes: Mock, mocked_tarfile: Mock
) -> None:
"""Test exception in post step."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
manager.loaded_backups = True
async def _mock_step(hass: HomeAssistant) -> None:
@@ -471,7 +477,7 @@ async def test_async_receive_backup(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test receiving a backup file."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -516,7 +522,8 @@ async def test_async_trigger_restore(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test trigger restore."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
hass.data[DATA_MANAGER] = manager
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -545,7 +552,8 @@ async def test_async_trigger_restore_with_password(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test trigger restore."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
hass.data[DATA_MANAGER] = manager
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()
@@ -573,7 +581,7 @@ async def test_async_trigger_restore_with_password(
async def test_async_trigger_restore_missing_backup(hass: HomeAssistant) -> None:
"""Test trigger restore."""
manager = BackupManager(hass)
manager = BackupManager(hass, CoreBackupReaderWriter(hass))
await _setup_backup_platform(hass, domain=DOMAIN, platform=local_backup_platform)
await manager.load_platforms()