Skip to content

Commit 533a61c

Browse files
committed
Extend backup API with file name field
Allow to specify a backup file name when creating a backup. This allows for user friendly backup file names. If none is specified, the current behavior remains (backup file name is the backup slug).
1 parent 805017e commit 533a61c

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

supervisor/api/backups.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
ATTR_DATE,
2727
ATTR_DAYS_UNTIL_STALE,
2828
ATTR_EXTRA,
29+
ATTR_FILENAME,
2930
ATTR_FOLDERS,
3031
ATTR_HOMEASSISTANT,
3132
ATTR_HOMEASSISTANT_EXCLUDE_DATABASE,
@@ -98,6 +99,7 @@ def _ensure_list(item: Any) -> list:
9899
SCHEMA_BACKUP_FULL = vol.Schema(
99100
{
100101
vol.Optional(ATTR_NAME): str,
102+
vol.Optional(ATTR_FILENAME): str,
101103
vol.Optional(ATTR_PASSWORD): vol.Maybe(str),
102104
vol.Optional(ATTR_COMPRESSED): vol.Maybe(vol.Boolean()),
103105
vol.Optional(ATTR_LOCATION): vol.All(

supervisor/backups/manager.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def _list_backup_files(self, path: Path) -> Iterable[Path]:
184184
def _create_backup(
185185
self,
186186
name: str,
187+
filename: str | None,
187188
sys_type: BackupType,
188189
password: str | None,
189190
compressed: bool = True,
@@ -196,7 +197,11 @@ def _create_backup(
196197
"""
197198
date_str = utcnow().isoformat()
198199
slug = create_slug(name, date_str)
199-
tar_file = Path(self._get_base_path(location), f"{slug}.tar")
200+
201+
if filename:
202+
tar_file = Path(self._get_base_path(location), Path(filename).name)
203+
else:
204+
tar_file = Path(self._get_base_path(location), f"{slug}.tar")
200205

201206
# init object
202207
backup = Backup(self.coresys, tar_file, slug, self._get_location_name(location))
@@ -482,6 +487,7 @@ async def _do_backup(
482487
async def do_backup_full(
483488
self,
484489
name: str = "",
490+
filename: str | None = None,
485491
*,
486492
password: str | None = None,
487493
compressed: bool = True,
@@ -500,7 +506,7 @@ async def do_backup_full(
500506
)
501507

502508
backup = self._create_backup(
503-
name, BackupType.FULL, password, compressed, location, extra
509+
name, filename, BackupType.FULL, password, compressed, location, extra
504510
)
505511

506512
_LOGGER.info("Creating new full backup with slug %s", backup.slug)
@@ -526,6 +532,7 @@ async def do_backup_full(
526532
async def do_backup_partial(
527533
self,
528534
name: str = "",
535+
filename: str | None = None,
529536
*,
530537
addons: list[str] | None = None,
531538
folders: list[str] | None = None,
@@ -558,7 +565,7 @@ async def do_backup_partial(
558565
_LOGGER.error("Nothing to create backup for")
559566

560567
backup = self._create_backup(
561-
name, BackupType.PARTIAL, password, compressed, location, extra
568+
name, filename, BackupType.PARTIAL, password, compressed, location, extra
562569
)
563570

564571
_LOGGER.info("Creating new partial backup with slug %s", backup.slug)

tests/backups/test_backup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ async def test_new_backup_stays_in_folder(coresys: CoreSys, tmp_path: Path):
1919
assert backup.tarfile.exists()
2020

2121
assert len(listdir(tmp_path)) == 1
22-
assert backup.tarfile.exists()
22+
assert backup.tarfile.exists()

tests/backups/test_manager.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,31 @@ async def test_do_backup_full(coresys: CoreSys, backup_mock, install_addon_ssh):
7373
assert coresys.core.state == CoreState.RUNNING
7474

7575

76+
@pytest.mark.parametrize(
77+
("filename", "filename_expected"),
78+
[("../my file.tar", "/data/backup/my file.tar"), (None, "/data/backup/{}.tar")],
79+
)
80+
async def test_do_backup_full_with_filename(
81+
coresys: CoreSys,
82+
filename: str,
83+
filename_expected: str,
84+
backup_mock
85+
):
86+
"""Test creating Backup with a specific file name."""
87+
coresys.core.state = CoreState.RUNNING
88+
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
89+
90+
manager = BackupManager(coresys)
91+
92+
# backup_mock fixture causes Backup() to be a MagicMock
93+
await manager.do_backup_full(filename=filename)
94+
95+
slug = backup_mock.call_args[0][2]
96+
assert str(backup_mock.call_args[0][1]) == filename_expected.format(slug)
97+
98+
assert coresys.core.state == CoreState.RUNNING
99+
100+
76101
async def test_do_backup_full_uncompressed(
77102
coresys: CoreSys, backup_mock, install_addon_ssh
78103
):

0 commit comments

Comments
 (0)