mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-13 12:16:29 +00:00
Delete Backup files on error (#5880)
This commit is contained in:
parent
6bac751c4c
commit
c855eaab52
@ -244,11 +244,6 @@ class Backup(JobGroup):
|
|||||||
"""Return backup size in bytes."""
|
"""Return backup size in bytes."""
|
||||||
return self._locations[self.location].size_bytes
|
return self._locations[self.location].size_bytes
|
||||||
|
|
||||||
@property
|
|
||||||
def is_new(self) -> bool:
|
|
||||||
"""Return True if there is new."""
|
|
||||||
return not self.tarfile.exists()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tarfile(self) -> Path:
|
def tarfile(self) -> Path:
|
||||||
"""Return path to backup tarfile."""
|
"""Return path to backup tarfile."""
|
||||||
|
@ -518,7 +518,8 @@ class BackupManager(FileConfiguration, JobGroup):
|
|||||||
) -> Backup | None:
|
) -> Backup | None:
|
||||||
"""Create a backup.
|
"""Create a backup.
|
||||||
|
|
||||||
Must be called from an existing backup job.
|
Must be called from an existing backup job. If the backup failed, the
|
||||||
|
backup file is being deleted and None is returned.
|
||||||
"""
|
"""
|
||||||
addon_start_tasks: list[Awaitable[None]] | None = None
|
addon_start_tasks: list[Awaitable[None]] | None = None
|
||||||
|
|
||||||
@ -548,9 +549,12 @@ class BackupManager(FileConfiguration, JobGroup):
|
|||||||
self._change_stage(BackupJobStage.FINISHING_FILE, backup)
|
self._change_stage(BackupJobStage.FINISHING_FILE, backup)
|
||||||
|
|
||||||
except BackupError as err:
|
except BackupError as err:
|
||||||
|
await self.sys_run_in_executor(backup.tarfile.unlink)
|
||||||
|
_LOGGER.error("Backup %s error: %s", backup.slug, err)
|
||||||
self.sys_jobs.current.capture_error(err)
|
self.sys_jobs.current.capture_error(err)
|
||||||
return None
|
return None
|
||||||
except Exception as err: # pylint: disable=broad-except
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
await self.sys_run_in_executor(backup.tarfile.unlink)
|
||||||
_LOGGER.exception("Backup %s error", backup.slug)
|
_LOGGER.exception("Backup %s error", backup.slug)
|
||||||
await async_capture_exception(err)
|
await async_capture_exception(err)
|
||||||
self.sys_jobs.current.capture_error(
|
self.sys_jobs.current.capture_error(
|
||||||
|
@ -16,7 +16,7 @@ from supervisor.addons.addon import Addon
|
|||||||
from supervisor.addons.const import AddonBackupMode
|
from supervisor.addons.const import AddonBackupMode
|
||||||
from supervisor.addons.model import AddonModel
|
from supervisor.addons.model import AddonModel
|
||||||
from supervisor.backups.backup import Backup, BackupLocation
|
from supervisor.backups.backup import Backup, BackupLocation
|
||||||
from supervisor.backups.const import LOCATION_TYPE, BackupType
|
from supervisor.backups.const import LOCATION_TYPE, BackupJobStage, BackupType
|
||||||
from supervisor.backups.manager import BackupManager
|
from supervisor.backups.manager import BackupManager
|
||||||
from supervisor.const import FOLDER_HOMEASSISTANT, FOLDER_SHARE, AddonState, CoreState
|
from supervisor.const import FOLDER_HOMEASSISTANT, FOLDER_SHARE, AddonState, CoreState
|
||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
@ -36,6 +36,7 @@ from supervisor.homeassistant.api import HomeAssistantAPI
|
|||||||
from supervisor.homeassistant.const import WSType
|
from supervisor.homeassistant.const import WSType
|
||||||
from supervisor.homeassistant.core import HomeAssistantCore
|
from supervisor.homeassistant.core import HomeAssistantCore
|
||||||
from supervisor.homeassistant.module import HomeAssistant
|
from supervisor.homeassistant.module import HomeAssistant
|
||||||
|
from supervisor.jobs import JobSchedulerOptions
|
||||||
from supervisor.jobs.const import JobCondition
|
from supervisor.jobs.const import JobCondition
|
||||||
from supervisor.mounts.mount import Mount
|
from supervisor.mounts.mount import Mount
|
||||||
from supervisor.resolution.const import UnhealthyReason
|
from supervisor.resolution.const import UnhealthyReason
|
||||||
@ -405,7 +406,63 @@ async def test_fail_invalid_partial_backup(
|
|||||||
await manager.do_restore_partial(backup_instance)
|
await manager.do_restore_partial(backup_instance)
|
||||||
|
|
||||||
|
|
||||||
async def test_backup_error(
|
async def test_backup_error_homeassistant(
|
||||||
|
coresys: CoreSys,
|
||||||
|
backup_mock: MagicMock,
|
||||||
|
install_addon_ssh: Addon,
|
||||||
|
capture_exception: Mock,
|
||||||
|
):
|
||||||
|
"""Test error collected and file deleted when Home Assistant Core backup fails."""
|
||||||
|
await coresys.core.set_state(CoreState.RUNNING)
|
||||||
|
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||||
|
|
||||||
|
backup_instance = backup_mock.return_value
|
||||||
|
|
||||||
|
backup_instance.store_homeassistant.side_effect = (
|
||||||
|
err := BackupError("Error while storing homeassistant")
|
||||||
|
)
|
||||||
|
|
||||||
|
job, backup_task = coresys.jobs.schedule_job(
|
||||||
|
coresys.backups.do_backup_full, JobSchedulerOptions()
|
||||||
|
)
|
||||||
|
assert await backup_task is None
|
||||||
|
|
||||||
|
assert job.errors[0].type_ is type(err)
|
||||||
|
assert job.errors[0].message == str(err)
|
||||||
|
assert job.errors[0].stage == BackupJobStage.HOME_ASSISTANT
|
||||||
|
|
||||||
|
backup_instance.tarfile.unlink.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_backup_error_folder(
|
||||||
|
coresys: CoreSys,
|
||||||
|
backup_mock: MagicMock,
|
||||||
|
install_addon_ssh: Addon,
|
||||||
|
capture_exception: Mock,
|
||||||
|
):
|
||||||
|
"""Test error collected and file deleted when folder backup fails."""
|
||||||
|
await coresys.core.set_state(CoreState.RUNNING)
|
||||||
|
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||||
|
|
||||||
|
backup_instance = backup_mock.return_value
|
||||||
|
|
||||||
|
backup_instance.store_folders.side_effect = (
|
||||||
|
err := BackupError("Error while storing folders")
|
||||||
|
)
|
||||||
|
|
||||||
|
job, backup_task = coresys.jobs.schedule_job(
|
||||||
|
coresys.backups.do_backup_full, JobSchedulerOptions()
|
||||||
|
)
|
||||||
|
assert await backup_task is None
|
||||||
|
|
||||||
|
assert job.errors[0].type_ is type(err)
|
||||||
|
assert job.errors[0].message == str(err)
|
||||||
|
assert job.errors[0].stage == BackupJobStage.FOLDERS
|
||||||
|
|
||||||
|
backup_instance.tarfile.unlink.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_backup_error_capture(
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
backup_mock: MagicMock,
|
backup_mock: MagicMock,
|
||||||
install_addon_ssh: Addon,
|
install_addon_ssh: Addon,
|
||||||
@ -416,7 +473,9 @@ async def test_backup_error(
|
|||||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||||
|
|
||||||
backup_mock.return_value.store_folders.side_effect = (err := OSError())
|
backup_mock.return_value.store_folders.side_effect = (err := OSError())
|
||||||
await coresys.backups.do_backup_full()
|
backup = await coresys.backups.do_backup_full()
|
||||||
|
|
||||||
|
assert backup is None
|
||||||
|
|
||||||
capture_exception.assert_called_once_with(err)
|
capture_exception.assert_called_once_with(err)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user