Store information about add-ons and folders which could not be backed up (#145367)

* Store information about add-ons and folders which could not be backed up

* Address review comments
This commit is contained in:
Erik Montnemery 2025-05-26 16:07:33 +02:00 committed by GitHub
parent 0802fc8a21
commit 0260a03447
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 621 additions and 92 deletions

View File

@ -62,6 +62,7 @@ from .const import (
LOGGER,
)
from .models import (
AddonInfo,
AgentBackup,
BackupError,
BackupManagerError,
@ -102,7 +103,9 @@ class ManagerBackup(BaseBackup):
"""Backup class."""
agents: dict[str, AgentBackupStatus]
failed_addons: list[AddonInfo]
failed_agent_ids: list[str]
failed_folders: list[Folder]
with_automatic_settings: bool | None
@ -110,7 +113,7 @@ class ManagerBackup(BaseBackup):
class AddonErrorData:
"""Addon error class."""
name: str
addon: AddonInfo
errors: list[tuple[str, str]]
@ -646,9 +649,13 @@ class BackupManager:
for agent_backup in result:
if (backup_id := agent_backup.backup_id) not in backups:
if known_backup := self.known_backups.get(backup_id):
failed_addons = known_backup.failed_addons
failed_agent_ids = known_backup.failed_agent_ids
failed_folders = known_backup.failed_folders
else:
failed_addons = []
failed_agent_ids = []
failed_folders = []
with_automatic_settings = self.is_our_automatic_backup(
agent_backup, await instance_id.async_get(self.hass)
)
@ -659,7 +666,9 @@ class BackupManager:
date=agent_backup.date,
database_included=agent_backup.database_included,
extra_metadata=agent_backup.extra_metadata,
failed_addons=failed_addons,
failed_agent_ids=failed_agent_ids,
failed_folders=failed_folders,
folders=agent_backup.folders,
homeassistant_included=agent_backup.homeassistant_included,
homeassistant_version=agent_backup.homeassistant_version,
@ -714,9 +723,13 @@ class BackupManager:
continue
if backup is None:
if known_backup := self.known_backups.get(backup_id):
failed_addons = known_backup.failed_addons
failed_agent_ids = known_backup.failed_agent_ids
failed_folders = known_backup.failed_folders
else:
failed_addons = []
failed_agent_ids = []
failed_folders = []
with_automatic_settings = self.is_our_automatic_backup(
result, await instance_id.async_get(self.hass)
)
@ -727,7 +740,9 @@ class BackupManager:
date=result.date,
database_included=result.database_included,
extra_metadata=result.extra_metadata,
failed_addons=failed_addons,
failed_agent_ids=failed_agent_ids,
failed_folders=failed_folders,
folders=result.folders,
homeassistant_included=result.homeassistant_included,
homeassistant_version=result.homeassistant_version,
@ -970,7 +985,7 @@ class BackupManager:
password=None,
)
await written_backup.release_stream()
self.known_backups.add(written_backup.backup, agent_errors, [])
self.known_backups.add(written_backup.backup, agent_errors, {}, {}, [])
return written_backup.backup.backup_id
async def async_create_backup(
@ -1208,7 +1223,11 @@ class BackupManager:
finally:
await written_backup.release_stream()
self.known_backups.add(
written_backup.backup, agent_errors, unavailable_agents
written_backup.backup,
agent_errors,
written_backup.addon_errors,
written_backup.folder_errors,
unavailable_agents,
)
if not agent_errors:
if with_automatic_settings:
@ -1416,7 +1435,12 @@ class BackupManager:
# No issues with agents or folders, but issues with add-ons
self._create_automatic_backup_failed_issue(
"automatic_backup_failed_addons",
{"failed_addons": ", ".join(val.name for val in addon_errors.values())},
{
"failed_addons": ", ".join(
val.addon.name or val.addon.slug
for val in addon_errors.values()
)
},
)
elif folder_errors and not (failed_agents or addon_errors):
# No issues with agents or add-ons, but issues with folders
@ -1431,7 +1455,11 @@ class BackupManager:
{
"failed_agents": ", ".join(failed_agents) or "-",
"failed_addons": (
", ".join(val.name for val in addon_errors.values()) or "-"
", ".join(
val.addon.name or val.addon.slug
for val in addon_errors.values()
)
or "-"
),
"failed_folders": ", ".join(f for f in folder_errors) or "-",
},
@ -1501,7 +1529,12 @@ class KnownBackups:
self._backups = {
backup["backup_id"]: KnownBackup(
backup_id=backup["backup_id"],
failed_addons=[
AddonInfo(name=a["name"], slug=a["slug"], version=a["version"])
for a in backup["failed_addons"]
],
failed_agent_ids=backup["failed_agent_ids"],
failed_folders=[Folder(f) for f in backup["failed_folders"]],
)
for backup in stored_backups
}
@ -1514,12 +1547,16 @@ class KnownBackups:
self,
backup: AgentBackup,
agent_errors: dict[str, Exception],
failed_addons: dict[str, AddonErrorData],
failed_folders: dict[Folder, list[tuple[str, str]]],
unavailable_agents: list[str],
) -> None:
"""Add a backup."""
self._backups[backup.backup_id] = KnownBackup(
backup_id=backup.backup_id,
failed_addons=[val.addon for val in failed_addons.values()],
failed_agent_ids=list(chain(agent_errors, unavailable_agents)),
failed_folders=list(failed_folders),
)
self._manager.store.save()
@ -1540,21 +1577,38 @@ class KnownBackup:
"""Persistent backup data."""
backup_id: str
failed_addons: list[AddonInfo]
failed_agent_ids: list[str]
failed_folders: list[Folder]
def to_dict(self) -> StoredKnownBackup:
"""Convert known backup to a dict."""
return {
"backup_id": self.backup_id,
"failed_addons": [
{"name": a.name, "slug": a.slug, "version": a.version}
for a in self.failed_addons
],
"failed_agent_ids": self.failed_agent_ids,
"failed_folders": [f.value for f in self.failed_folders],
}
class StoredAddonInfo(TypedDict):
"""Stored add-on info."""
name: str | None
slug: str
version: str | None
class StoredKnownBackup(TypedDict):
"""Stored persistent backup data."""
backup_id: str
failed_addons: list[StoredAddonInfo]
failed_agent_ids: list[str]
failed_folders: list[str]
class CoreBackupReaderWriter(BackupReaderWriter):

View File

@ -13,9 +13,9 @@ from homeassistant.exceptions import HomeAssistantError
class AddonInfo:
"""Addon information."""
name: str
name: str | None
slug: str
version: str
version: str | None
class Folder(StrEnum):

View File

@ -16,7 +16,7 @@ if TYPE_CHECKING:
STORE_DELAY_SAVE = 30
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
STORAGE_VERSION_MINOR = 6
STORAGE_VERSION_MINOR = 7
class StoredBackupData(TypedDict):
@ -76,6 +76,11 @@ class _BackupStore(Store[StoredBackupData]):
# Version 1.6 adds agent retention settings
for agent in data["config"]["agents"]:
data["config"]["agents"][agent]["retention"] = None
if old_minor_version < 7:
# Version 1.7 adds failing addons and folders
for backup in data["backups"]:
backup["failed_addons"] = []
backup["failed_folders"] = []
# Note: We allow reading data with major version 2 in which the unused key
# data["config"]["schedule"]["state"] will be removed. The bump to 2 is

View File

@ -429,10 +429,19 @@ class SupervisorBackupReaderWriter(BackupReaderWriter):
for slug, errors in _addon_errors.items():
try:
addon_info = await self._client.addons.addon_info(slug)
addon_errors[slug] = AddonErrorData(name=addon_info.name, errors=errors)
addon_errors[slug] = AddonErrorData(
addon=AddonInfo(
name=addon_info.name,
slug=addon_info.slug,
version=addon_info.version,
),
errors=errors,
)
except SupervisorError as err:
_LOGGER.debug("Error getting addon %s: %s", slug, err)
addon_errors[slug] = AddonErrorData(name=slug, errors=errors)
addon_errors[slug] = AddonErrorData(
addon=AddonInfo(name=None, slug=slug, version=None), errors=errors
)
_folder_errors = _collect_errors(
full_status, "backup_store_folders", "backup_folder_save"

View File

@ -114,21 +114,23 @@ async def test_agents_list_backups(
assert response["result"]["backups"] == [
{
"addons": test_backup.addons,
"backup_id": test_backup.backup_id,
"date": test_backup.date,
"database_included": test_backup.database_included,
"folders": test_backup.folders,
"homeassistant_included": test_backup.homeassistant_included,
"homeassistant_version": test_backup.homeassistant_version,
"name": test_backup.name,
"extra_metadata": test_backup.extra_metadata,
"agents": {
f"{DOMAIN}.{mock_config_entry.entry_id}": {
"protected": test_backup.protected,
"size": test_backup.size,
}
},
"backup_id": test_backup.backup_id,
"database_included": test_backup.database_included,
"date": test_backup.date,
"extra_metadata": test_backup.extra_metadata,
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": test_backup.folders,
"homeassistant_included": test_backup.homeassistant_included,
"homeassistant_version": test_backup.homeassistant_version,
"name": test_backup.name,
"with_automatic_settings": None,
}
]
@ -152,21 +154,23 @@ async def test_agents_get_backup(
assert response["result"]["agent_errors"] == {}
assert response["result"]["backup"] == {
"addons": test_backup.addons,
"backup_id": test_backup.backup_id,
"date": test_backup.date,
"database_included": test_backup.database_included,
"folders": test_backup.folders,
"homeassistant_included": test_backup.homeassistant_included,
"homeassistant_version": test_backup.homeassistant_version,
"name": test_backup.name,
"extra_metadata": test_backup.extra_metadata,
"agents": {
f"{DOMAIN}.{mock_config_entry.entry_id}": {
"protected": test_backup.protected,
"size": test_backup.size,
}
},
"backup_id": test_backup.backup_id,
"database_included": test_backup.database_included,
"date": test_backup.date,
"extra_metadata": test_backup.extra_metadata,
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": test_backup.folders,
"homeassistant_included": test_backup.homeassistant_included,
"homeassistant_version": test_backup.homeassistant_version,
"name": test_backup.name,
"with_automatic_settings": None,
}

View File

@ -93,14 +93,16 @@ async def test_agents_list_backups(
}
},
"backup_id": "23e64aec",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"extra_metadata": {},
"with_automatic_settings": None,
}
]
@ -129,14 +131,16 @@ async def test_agents_get_backup(
}
},
"backup_id": "23e64aec",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"extra_metadata": {},
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
}

View File

@ -75,8 +75,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -102,8 +106,12 @@
'instance_id': 'unknown_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',

View File

@ -23,8 +23,12 @@
'instance_id': 'abc123',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -50,8 +54,12 @@
'instance_id': 'unknown_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',

View File

@ -5,9 +5,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -40,7 +44,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -50,9 +54,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -86,7 +94,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -96,9 +104,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -131,7 +143,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -141,9 +153,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -177,7 +193,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -187,9 +203,19 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
}),
]),
'config': dict({
@ -226,7 +252,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -236,9 +262,19 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
}),
]),
'config': dict({
@ -276,7 +312,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -286,9 +322,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -325,7 +365,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -335,9 +375,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -375,7 +419,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -385,9 +429,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -424,7 +472,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -434,9 +482,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -474,7 +526,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -484,9 +536,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -526,7 +582,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -536,9 +592,13 @@
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
]),
}),
]),
'config': dict({
@ -579,7 +639,132 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
# name: test_store_migration[store_data6]
dict({
'data': dict({
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
}),
]),
'config': dict({
'agents': dict({
'test.remote': dict({
'protected': True,
'retention': dict({
'copies': None,
'days': None,
}),
}),
}),
'automatic_backups_configured': True,
'create_backup': dict({
'agent_ids': list([
]),
'include_addons': None,
'include_all_addons': False,
'include_database': True,
'include_folders': None,
'name': None,
'password': 'hunter2',
}),
'last_attempted_automatic_backup': None,
'last_completed_automatic_backup': None,
'retention': dict({
'copies': None,
'days': None,
}),
'schedule': dict({
'days': list([
]),
'recurrence': 'never',
'state': 'never',
'time': None,
}),
}),
}),
'key': 'backup',
'minor_version': 7,
'version': 1,
})
# ---
# name: test_store_migration[store_data6].1
dict({
'data': dict({
'backups': list([
dict({
'backup_id': 'abc123',
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
}),
]),
'config': dict({
'agents': dict({
'test.remote': dict({
'protected': True,
'retention': dict({
'copies': None,
'days': None,
}),
}),
}),
'automatic_backups_configured': True,
'create_backup': dict({
'agent_ids': list([
'test-agent',
]),
'include_addons': None,
'include_all_addons': False,
'include_database': True,
'include_folders': None,
'name': None,
'password': 'hunter2',
}),
'last_attempted_automatic_backup': None,
'last_completed_automatic_backup': None,
'retention': dict({
'copies': None,
'days': None,
}),
'schedule': dict({
'days': list([
]),
'recurrence': 'never',
'state': 'never',
'time': None,
}),
}),
}),
'key': 'backup',
'minor_version': 7,
'version': 1,
})
# ---

View File

@ -1312,7 +1312,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -1429,7 +1429,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -1546,7 +1546,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -1677,7 +1677,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -1955,7 +1955,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2070,7 +2070,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2185,7 +2185,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2302,7 +2302,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2421,7 +2421,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2538,7 +2538,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2659,7 +2659,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2784,7 +2784,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -2901,7 +2901,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -3018,7 +3018,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -3135,7 +3135,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -3252,7 +3252,7 @@
}),
}),
'key': 'backup',
'minor_version': 6,
'minor_version': 7,
'version': 1,
})
# ---
@ -4397,8 +4397,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4478,8 +4482,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4540,8 +4548,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4586,8 +4598,12 @@
'instance_id': 'unknown_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4643,8 +4659,12 @@
'instance_id': 'unknown_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4698,8 +4718,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4760,8 +4784,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4823,8 +4851,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -4886,9 +4918,19 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
'folders': list([
'media',
'share',
@ -4949,8 +4991,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5011,8 +5057,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5074,8 +5124,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5137,9 +5191,19 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
dict({
'name': 'Test add-on',
'slug': 'test_addon',
'version': '1.0.0',
}),
]),
'failed_agent_ids': list([
'test.remote',
]),
'failed_folders': list([
'ssl',
]),
'folders': list([
'media',
'share',
@ -5200,8 +5264,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5243,8 +5311,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5302,8 +5374,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5358,8 +5434,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5402,8 +5482,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5446,8 +5530,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5715,8 +5803,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5766,8 +5858,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5821,8 +5917,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5867,8 +5967,12 @@
'instance_id': 'unknown_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5899,8 +6003,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -5951,8 +6059,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -6003,8 +6115,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',
@ -6055,8 +6171,12 @@
'instance_id': 'our_uuid',
'with_automatic_settings': True,
}),
'failed_addons': list([
]),
'failed_agent_ids': list([
]),
'failed_folders': list([
]),
'folders': list([
'media',
'share',

View File

@ -36,6 +36,7 @@ from homeassistant.components.backup.agent import BackupAgentError
from homeassistant.components.backup.const import DATA_MANAGER
from homeassistant.components.backup.manager import (
AddonErrorData,
AddonInfo,
BackupManagerError,
BackupManagerExceptionGroup,
BackupManagerState,
@ -653,7 +654,9 @@ async def test_initiate_backup(
"database_included": include_database,
"date": ANY,
"extra_metadata": {"instance_id": "our_uuid", "with_automatic_settings": False},
"failed_addons": [],
"failed_agent_ids": expected_failed_agent_ids,
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.1.0",
@ -706,7 +709,9 @@ async def test_initiate_backup_with_agent_error(
"instance_id": "our_uuid",
"with_automatic_settings": True,
},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [
"media",
"share",
@ -726,7 +731,9 @@ async def test_initiate_backup_with_agent_error(
"instance_id": "unknown_uuid",
"with_automatic_settings": True,
},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [
"media",
"share",
@ -752,7 +759,9 @@ async def test_initiate_backup_with_agent_error(
"instance_id": "our_uuid",
"with_automatic_settings": True,
},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [
"media",
"share",
@ -857,7 +866,9 @@ async def test_initiate_backup_with_agent_error(
"database_included": True,
"date": ANY,
"extra_metadata": {"instance_id": "our_uuid", "with_automatic_settings": False},
"failed_addons": [],
"failed_agent_ids": ["test.remote"],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.1.0",
@ -890,7 +901,9 @@ async def test_initiate_backup_with_agent_error(
assert hass_storage[DOMAIN]["data"]["backups"] == [
{
"backup_id": "abc123",
"failed_addons": [],
"failed_agent_ids": ["test.remote"],
"failed_folders": [],
}
]
@ -1121,7 +1134,8 @@ async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
{"type": "backup/generate", "agent_ids": ["test.remote"]},
{
"test_addon": AddonErrorData(
name="Test Add-on", errors=[("test_error", "Boom!")]
addon=AddonInfo(name="Test Add-on", slug="test", version="0.0"),
errors=[("test_error", "Boom!")],
)
},
{},
@ -1135,7 +1149,8 @@ async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
{"type": "backup/generate_with_automatic_settings"},
{
"test_addon": AddonErrorData(
name="Test Add-on", errors=[("test_error", "Boom!")]
addon=AddonInfo(name="Test Add-on", slug="test", version="0.0"),
errors=[("test_error", "Boom!")],
)
},
{},
@ -1181,7 +1196,8 @@ async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
{"type": "backup/generate", "agent_ids": ["test.remote"]},
{
"test_addon": AddonErrorData(
name="Test Add-on", errors=[("test_error", "Boom!")]
addon=AddonInfo(name="Test Add-on", slug="test", version="0.0"),
errors=[("test_error", "Boom!")],
)
},
{Folder.MEDIA: [("test_error", "Boom!")]},
@ -1195,7 +1211,8 @@ async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
{"type": "backup/generate_with_automatic_settings"},
{
"test_addon": AddonErrorData(
name="Test Add-on", errors=[("test_error", "Boom!")]
addon=AddonInfo(name="Test Add-on", slug="test", version="0.0"),
errors=[("test_error", "Boom!")],
)
},
{Folder.MEDIA: [("test_error", "Boom!")]},
@ -1219,7 +1236,8 @@ async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
{"type": "backup/generate", "agent_ids": ["test.remote"]},
{
"test_addon": AddonErrorData(
name="Test Add-on", errors=[("test_error", "Boom!")]
addon=AddonInfo(name="Test Add-on", slug="test", version="0.0"),
errors=[("test_error", "Boom!")],
)
},
{Folder.MEDIA: [("test_error", "Boom!")]},
@ -1241,7 +1259,8 @@ async def delayed_boom(*args, **kwargs) -> tuple[NewBackup, Any]:
{"type": "backup/generate_with_automatic_settings"},
{
"test_addon": AddonErrorData(
name="Test Add-on", errors=[("test_error", "Boom!")]
addon=AddonInfo(name="Test Add-on", slug="test", version="0.0"),
errors=[("test_error", "Boom!")],
)
},
{Folder.MEDIA: [("test_error", "Boom!")]},
@ -2080,7 +2099,9 @@ async def test_receive_backup_agent_error(
"instance_id": "our_uuid",
"with_automatic_settings": True,
},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [
"media",
"share",
@ -2100,7 +2121,9 @@ async def test_receive_backup_agent_error(
"instance_id": "unknown_uuid",
"with_automatic_settings": True,
},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [
"media",
"share",
@ -2126,7 +2149,9 @@ async def test_receive_backup_agent_error(
"instance_id": "our_uuid",
"with_automatic_settings": True,
},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [
"media",
"share",
@ -2256,7 +2281,9 @@ async def test_receive_backup_agent_error(
assert hass_storage[DOMAIN]["data"]["backups"] == [
{
"backup_id": "abc123",
"failed_addons": [],
"failed_agent_ids": ["test.remote"],
"failed_folders": [],
}
]
@ -3571,7 +3598,9 @@ async def test_initiate_backup_per_agent_encryption(
"database_included": True,
"date": ANY,
"extra_metadata": {"instance_id": "our_uuid", "with_automatic_settings": False},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.1.0",

View File

@ -124,14 +124,16 @@ async def test_onboarding_backup_info(
"backup.local": backup.manager.AgentBackupStatus(protected=True, size=0)
},
backup_id="abc123",
date="1970-01-01T00:00:00.000Z",
database_included=True,
date="1970-01-01T00:00:00.000Z",
extra_metadata={"instance_id": "abc123", "with_automatic_settings": True},
failed_addons=[],
failed_agent_ids=[],
failed_folders=[],
folders=[backup.Folder.MEDIA, backup.Folder.SHARE],
homeassistant_included=True,
homeassistant_version="2024.12.0",
name="Test",
failed_agent_ids=[],
with_automatic_settings=True,
),
"def456": backup.ManagerBackup(
@ -140,17 +142,19 @@ async def test_onboarding_backup_info(
"test.remote": backup.manager.AgentBackupStatus(protected=True, size=0)
},
backup_id="def456",
date="1980-01-01T00:00:00.000Z",
database_included=False,
date="1980-01-01T00:00:00.000Z",
extra_metadata={
"instance_id": "unknown_uuid",
"with_automatic_settings": True,
},
failed_addons=[],
failed_agent_ids=[],
failed_folders=[],
folders=[backup.Folder.MEDIA, backup.Folder.SHARE],
homeassistant_included=True,
homeassistant_version="2024.12.0",
name="Test 2",
failed_agent_ids=[],
with_automatic_settings=None,
),
}

View File

@ -94,7 +94,15 @@ def mock_delay_save() -> Generator[None]:
"backups": [
{
"backup_id": "abc123",
"failed_addons": [
{
"name": "Test add-on",
"slug": "test_addon",
"version": "1.0.0",
}
],
"failed_agent_ids": ["test.remote"],
"failed_folders": ["ssl"],
}
],
"config": {
@ -243,6 +251,57 @@ def mock_delay_save() -> Generator[None]:
"minor_version": 6,
"version": 1,
},
{
"data": {
"backups": [
{
"backup_id": "abc123",
"failed_addons": [
{
"name": "Test add-on",
"slug": "test_addon",
"version": "1.0.0",
}
],
"failed_agent_ids": ["test.remote"],
"failed_folders": ["ssl"],
}
],
"config": {
"agents": {
"test.remote": {
"protected": True,
"retention": {"copies": None, "days": None},
}
},
"automatic_backups_configured": True,
"create_backup": {
"agent_ids": [],
"include_addons": None,
"include_all_addons": False,
"include_database": True,
"include_folders": None,
"name": None,
"password": "hunter2",
},
"last_attempted_automatic_backup": None,
"last_completed_automatic_backup": None,
"retention": {
"copies": None,
"days": None,
},
"schedule": {
"days": [],
"recurrence": "never",
"state": "never",
"time": None,
},
},
},
"key": DOMAIN,
"minor_version": 7,
"version": 1,
},
],
)
async def test_store_migration(

View File

@ -87,14 +87,16 @@ TEST_MANAGER_BACKUP = ManagerBackup(
addons=[AddonInfo(name="Test", slug="test", version="1.0.0")],
agents={"test.test-agent": AgentBackupStatus(protected=True, size=0)},
backup_id="backup-1",
date="1970-01-01T00:00:00.000Z",
database_included=True,
date="1970-01-01T00:00:00.000Z",
extra_metadata={"instance_id": "abc123", "with_automatic_settings": True},
failed_addons=[],
failed_agent_ids=[],
failed_folders=[],
folders=[Folder.MEDIA, Folder.SHARE],
homeassistant_included=True,
homeassistant_version="2024.12.0",
name="Test",
failed_agent_ids=[],
with_automatic_settings=True,
)
@ -326,7 +328,15 @@ async def test_delete(
"backups": [
{
"backup_id": "abc123",
"failed_addons": [
{
"name": "Test add-on",
"slug": "test_addon",
"version": "1.0.0",
}
],
"failed_agent_ids": ["test.remote"],
"failed_folders": ["ssl"],
}
]
},

View File

@ -152,28 +152,32 @@ async def test_agents_list_backups(
"addons": [],
"agents": {"cloud.cloud": {"protected": False, "size": 34519040}},
"backup_id": "23e64aec",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
},
{
"addons": [],
"agents": {"cloud.cloud": {"protected": False, "size": 34519040}},
"backup_id": "23e64aed",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
},
]
@ -216,14 +220,16 @@ async def test_agents_list_backups_fail_cloud(
"addons": [],
"agents": {"cloud.cloud": {"protected": False, "size": 34519040}},
"backup_id": "23e64aec",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
},
),

View File

@ -49,11 +49,13 @@ TEST_AGENT_BACKUP_RESULT = {
"database_included": True,
"date": "2025-01-01T01:23:45.678Z",
"extra_metadata": {"with_automatic_settings": False},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0",
"name": "Test",
"failed_agent_ids": [],
"with_automatic_settings": None,
}

View File

@ -497,7 +497,9 @@ async def test_agent_info(
"database_included": True,
"date": "1970-01-01T00:00:00+00:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": ["share"],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0",
@ -517,7 +519,9 @@ async def test_agent_info(
"database_included": False,
"date": "1970-01-01T00:00:00+00:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": ["share"],
"homeassistant_included": False,
"homeassistant_version": None,
@ -653,7 +657,9 @@ async def test_agent_get_backup(
"database_included": True,
"date": "1970-01-01T00:00:00+00:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": ["share"],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0",
@ -992,7 +998,7 @@ async def test_reader_writer_create(
@pytest.mark.parametrize(
"addon_info_side_effect",
# Getting info fails for one of the addons, should fall back to slug
[[Mock(), SupervisorError("Boom")]],
[[Mock(slug="core_ssh", version="0.0.0"), SupervisorError("Boom")]],
)
async def test_reader_writer_create_addon_folder_error(
hass: HomeAssistant,

View File

@ -109,7 +109,9 @@ async def test_agents_list_backups(
"database_included": False,
"date": "1970-01-01T00:00:00Z",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": ["media", "share"],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0",
@ -191,7 +193,9 @@ async def test_agents_upload(
"database_included": True,
"date": "1970-01-01T00:00:00.000Z",
"extra_metadata": {"instance_id": ANY, "with_automatic_settings": False},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": ["media", "share"],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0",

View File

@ -91,14 +91,16 @@ async def test_agents_list_backups(
"onedrive.mock_drive_id": {"protected": False, "size": 34519040}
},
"backup_id": "23e64aec",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
}
]
@ -143,14 +145,16 @@ async def test_agents_get_backup(
}
},
"backup_id": "23e64aec",
"date": "2024-11-22T11:48:48.727189+01:00",
"database_included": True,
"date": "2024-11-22T11:48:48.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2024.12.0.dev0",
"name": "Core 2024.12.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
}

View File

@ -342,14 +342,16 @@ async def test_agents_list_backups(
}
},
"backup_id": "abcd12ef",
"date": "2025-01-09T20:14:35.457323+01:00",
"database_included": True,
"date": "2025-01-09T20:14:35.457323+01:00",
"extra_metadata": {"instance_id": ANY, "with_automatic_settings": True},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.2.0.dev0",
"name": "Automatic backup 2025.2.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
}
]
@ -413,14 +415,16 @@ async def test_agents_list_backups_disabled_filestation(
}
},
"backup_id": "abcd12ef",
"date": "2025-01-09T20:14:35.457323+01:00",
"database_included": True,
"date": "2025-01-09T20:14:35.457323+01:00",
"extra_metadata": {"instance_id": ANY, "with_automatic_settings": True},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.2.0.dev0",
"name": "Automatic backup 2025.2.0.dev0",
"failed_agent_ids": [],
"with_automatic_settings": None,
},
),

View File

@ -86,14 +86,16 @@ async def test_agents_list_backups(
}
},
"backup_id": "23e64aec",
"date": "2025-02-10T17:47:22.727189+01:00",
"database_included": True,
"date": "2025-02-10T17:47:22.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.2.1",
"name": "Automatic backup 2025.2.1",
"failed_agent_ids": [],
"with_automatic_settings": None,
}
]
@ -122,14 +124,16 @@ async def test_agents_get_backup(
}
},
"backup_id": "23e64aec",
"date": "2025-02-10T17:47:22.727189+01:00",
"database_included": True,
"date": "2025-02-10T17:47:22.727189+01:00",
"extra_metadata": {},
"failed_addons": [],
"failed_agent_ids": [],
"failed_folders": [],
"folders": [],
"homeassistant_included": True,
"homeassistant_version": "2025.2.1",
"name": "Automatic backup 2025.2.1",
"failed_agent_ids": [],
"with_automatic_settings": None,
}