Skip to content

Commit

Permalink
Check if backup is still available when accessing
Browse files Browse the repository at this point in the history
When accessing a backup (e.g. to restore, download or delete it) check
if the locations of the backup file are still available. If not, force
a backup reload instead.
  • Loading branch information
agners committed Jan 14, 2025
1 parent f02d67e commit b68d381
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 8 deletions.
30 changes: 24 additions & 6 deletions supervisor/api/backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,29 @@ def _ensure_list(item: Any) -> list:
class APIBackups(CoreSysAttributes):
"""Handle RESTful API for backups functions."""

def _extract_slug(self, request):
async def _get_backup_by_slug(self, request):
"""Return backup, throw an exception if it doesn't exist."""
backup = self.sys_backups.get(request.match_info.get("slug"))
if not backup:
raise APINotFound("Backup does not exist")

# Reload in case locations of this backup are no longer in sync
# This usually means the user
def _check_need_reload():
for location in backup.all_locations:
if not Path(location).exists():
return False
return True

if not await self.sys_run_in_executor(_check_need_reload):
_LOGGER.warning(
"Backup %s missing in at least one location, scheduling reload",
backup.slug,
)
# Schedule a reload
self.sys_jobs.schedule_job(self.sys_backups.reload, JobSchedulerOptions())
raise APINotFound("Backup does not exist")

return backup

def _list_backups(self):
Expand Down Expand Up @@ -212,7 +230,7 @@ async def reload(self, _):
@api_process
async def backup_info(self, request):
"""Return backup info."""
backup = self._extract_slug(request)
backup = await self._get_backup_by_slug(request)

data_addons = []
for addon_data in backup.addons:
Expand Down Expand Up @@ -384,7 +402,7 @@ async def backup_partial(self, request: web.Request):
@api_process
async def restore_full(self, request: web.Request):
"""Full restore of a backup."""
backup = self._extract_slug(request)
backup = await self._get_backup_by_slug(request)
body = await api_validate(SCHEMA_RESTORE_FULL, request)
self._validate_cloud_backup_location(
request, body.get(ATTR_LOCATION, backup.location)
Expand All @@ -404,7 +422,7 @@ async def restore_full(self, request: web.Request):
@api_process
async def restore_partial(self, request: web.Request):
"""Partial restore a backup."""
backup = self._extract_slug(request)
backup = await self._get_backup_by_slug(request)
body = await api_validate(SCHEMA_RESTORE_PARTIAL, request)
self._validate_cloud_backup_location(
request, body.get(ATTR_LOCATION, backup.location)
Expand Down Expand Up @@ -435,7 +453,7 @@ async def thaw(self, request: web.Request):
@api_process
async def remove(self, request: web.Request):
"""Remove a backup."""
backup = self._extract_slug(request)
backup = await self._get_backup_by_slug(request)
body = await api_validate(SCHEMA_REMOVE, request)
locations: list[LOCATION_TYPE] | None = None

Expand All @@ -450,7 +468,7 @@ async def remove(self, request: web.Request):
@api_process
async def download(self, request: web.Request):
"""Download a backup file."""
backup = self._extract_slug(request)
backup = self._get_backup_by_slug(request)
# Query will give us '' for /backups, convert value to None
location = request.query.get(ATTR_LOCATION, backup.location) or None
self._validate_cloud_backup_location(request, location)
Expand Down
5 changes: 3 additions & 2 deletions supervisor/backups/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,18 +200,19 @@ def _create_backup(
return backup

def load(self) -> Awaitable[None]:
"""Load exists backups data.
"""Load existing backups data.
Return a coroutine.
"""
return self.reload()

@Job(name="backup_reload")
async def reload(
self,
location: LOCATION_TYPE | type[DEFAULT] = DEFAULT,
filename: str | None = None,
) -> bool:
"""Load exists backups."""
"""Load existing backups."""

async def _load_backup(location: str | None, tar_file: Path) -> bool:
"""Load the backup."""
Expand Down

0 comments on commit b68d381

Please sign in to comment.