mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-16 13:46:31 +00:00
Add all addons flag to partial backups (#5490)
This commit is contained in:
parent
5e6bef7189
commit
90590ae2de
@ -61,6 +61,8 @@ from .utils import api_process, api_validate
|
|||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ALL_ADDONS_FLAG = "ALL"
|
||||||
|
|
||||||
RE_SLUGIFY_NAME = re.compile(r"[^A-Za-z0-9]+")
|
RE_SLUGIFY_NAME = re.compile(r"[^A-Za-z0-9]+")
|
||||||
RE_BACKUP_FILENAME = re.compile(r"^[^\\\/]+\.tar$")
|
RE_BACKUP_FILENAME = re.compile(r"^[^\\\/]+\.tar$")
|
||||||
|
|
||||||
@ -108,7 +110,9 @@ SCHEMA_BACKUP_FULL = vol.Schema(
|
|||||||
|
|
||||||
SCHEMA_BACKUP_PARTIAL = SCHEMA_BACKUP_FULL.extend(
|
SCHEMA_BACKUP_PARTIAL = SCHEMA_BACKUP_FULL.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(ATTR_ADDONS): vol.All([str], vol.Unique()),
|
vol.Optional(ATTR_ADDONS): vol.Or(
|
||||||
|
ALL_ADDONS_FLAG, vol.All([str], vol.Unique())
|
||||||
|
),
|
||||||
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(_ALL_FOLDERS)], vol.Unique()),
|
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(_ALL_FOLDERS)], vol.Unique()),
|
||||||
vol.Optional(ATTR_HOMEASSISTANT): vol.Boolean(),
|
vol.Optional(ATTR_HOMEASSISTANT): vol.Boolean(),
|
||||||
}
|
}
|
||||||
@ -352,6 +356,9 @@ class APIBackups(CoreSysAttributes):
|
|||||||
if locations:
|
if locations:
|
||||||
body[ATTR_ADDITIONAL_LOCATIONS] = locations
|
body[ATTR_ADDITIONAL_LOCATIONS] = locations
|
||||||
|
|
||||||
|
if body.get(ATTR_ADDONS) == ALL_ADDONS_FLAG:
|
||||||
|
body[ATTR_ADDONS] = list(self.sys_addons.local)
|
||||||
|
|
||||||
background = body.pop(ATTR_BACKGROUND)
|
background = body.pop(ATTR_BACKGROUND)
|
||||||
backup_task, job_id = await self._background_backup_task(
|
backup_task, job_id = await self._background_backup_task(
|
||||||
self.sys_backups.do_backup_partial, **body
|
self.sys_backups.do_backup_partial, **body
|
||||||
|
@ -25,9 +25,8 @@ from tests.common import get_fixture_path
|
|||||||
from tests.const import TEST_ADDON_SLUG
|
from tests.const import TEST_ADDON_SLUG
|
||||||
|
|
||||||
|
|
||||||
async def test_info(
|
@pytest.mark.usefixtures("mock_full_backup")
|
||||||
api_client, coresys: CoreSys, mock_full_backup: Backup, tmp_path: Path
|
async def test_info(api_client: TestClient, coresys: CoreSys, tmp_path: Path):
|
||||||
):
|
|
||||||
"""Test info endpoint."""
|
"""Test info endpoint."""
|
||||||
copy(get_fixture_path("backup_example.tar"), tmp_path / "test_backup.tar")
|
copy(get_fixture_path("backup_example.tar"), tmp_path / "test_backup.tar")
|
||||||
|
|
||||||
@ -43,8 +42,9 @@ async def test_info(
|
|||||||
assert result["data"]["backups"][0]["size_bytes"] == 10240
|
assert result["data"]["backups"][0]["size_bytes"] == 10240
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_full_backup")
|
||||||
async def test_backup_more_info(
|
async def test_backup_more_info(
|
||||||
api_client, coresys: CoreSys, mock_full_backup: Backup, tmp_path: Path
|
api_client: TestClient, coresys: CoreSys, tmp_path: Path
|
||||||
):
|
):
|
||||||
"""Test info endpoint."""
|
"""Test info endpoint."""
|
||||||
copy(get_fixture_path("backup_example.tar"), tmp_path / "test_backup.tar")
|
copy(get_fixture_path("backup_example.tar"), tmp_path / "test_backup.tar")
|
||||||
@ -65,9 +65,8 @@ async def test_backup_more_info(
|
|||||||
assert result["data"]["homeassistant_exclude_database"] is False
|
assert result["data"]["homeassistant_exclude_database"] is False
|
||||||
|
|
||||||
|
|
||||||
async def test_list(
|
@pytest.mark.usefixtures("mock_full_backup")
|
||||||
api_client, coresys: CoreSys, mock_full_backup: Backup, tmp_path: Path
|
async def test_list(api_client: TestClient, coresys: CoreSys, tmp_path: Path):
|
||||||
):
|
|
||||||
"""Test list endpoint."""
|
"""Test list endpoint."""
|
||||||
copy(get_fixture_path("backup_example.tar"), tmp_path / "test_backup.tar")
|
copy(get_fixture_path("backup_example.tar"), tmp_path / "test_backup.tar")
|
||||||
|
|
||||||
@ -82,7 +81,7 @@ async def test_list(
|
|||||||
assert result["data"]["backups"][0]["size_bytes"] == 10240
|
assert result["data"]["backups"][0]["size_bytes"] == 10240
|
||||||
|
|
||||||
|
|
||||||
async def test_options(api_client, coresys: CoreSys):
|
async def test_options(api_client: TestClient, coresys: CoreSys):
|
||||||
"""Test options endpoint."""
|
"""Test options endpoint."""
|
||||||
assert coresys.backups.days_until_stale == 30
|
assert coresys.backups.days_until_stale == 30
|
||||||
|
|
||||||
@ -102,15 +101,13 @@ async def test_options(api_client, coresys: CoreSys):
|
|||||||
"location,backup_dir",
|
"location,backup_dir",
|
||||||
[("backup_test", PurePath("mounts", "backup_test")), (None, PurePath("backup"))],
|
[("backup_test", PurePath("mounts", "backup_test")), (None, PurePath("backup"))],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.usefixtures("path_extern", "mount_propagation", "mock_is_mount")
|
||||||
async def test_backup_to_location(
|
async def test_backup_to_location(
|
||||||
api_client: TestClient,
|
api_client: TestClient,
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
location: str | None,
|
location: str | None,
|
||||||
backup_dir: PurePath,
|
backup_dir: PurePath,
|
||||||
tmp_supervisor_data: Path,
|
tmp_supervisor_data: Path,
|
||||||
path_extern,
|
|
||||||
mount_propagation,
|
|
||||||
mock_is_mount,
|
|
||||||
):
|
):
|
||||||
"""Test making a backup to a specific location with default mount."""
|
"""Test making a backup to a specific location with default mount."""
|
||||||
await coresys.mounts.load()
|
await coresys.mounts.load()
|
||||||
@ -149,14 +146,10 @@ async def test_backup_to_location(
|
|||||||
assert result["data"]["location"] == location
|
assert result["data"]["location"] == location
|
||||||
|
|
||||||
|
|
||||||
async def test_backup_to_default(
|
@pytest.mark.usefixtures(
|
||||||
api_client: TestClient,
|
"tmp_supervisor_data", "path_extern", "mount_propagation", "mock_is_mount"
|
||||||
coresys: CoreSys,
|
)
|
||||||
tmp_supervisor_data,
|
async def test_backup_to_default(api_client: TestClient, coresys: CoreSys):
|
||||||
path_extern,
|
|
||||||
mount_propagation,
|
|
||||||
mock_is_mount,
|
|
||||||
):
|
|
||||||
"""Test making backup to default mount."""
|
"""Test making backup to default mount."""
|
||||||
await coresys.mounts.load()
|
await coresys.mounts.load()
|
||||||
(mount_dir := coresys.config.path_mounts / "backup_test").mkdir()
|
(mount_dir := coresys.config.path_mounts / "backup_test").mkdir()
|
||||||
@ -186,12 +179,9 @@ async def test_backup_to_default(
|
|||||||
assert (mount_dir / f"{slug}.tar").exists()
|
assert (mount_dir / f"{slug}.tar").exists()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern")
|
||||||
async def test_api_freeze_thaw(
|
async def test_api_freeze_thaw(
|
||||||
api_client: TestClient,
|
api_client: TestClient, coresys: CoreSys, ha_ws_client: AsyncMock
|
||||||
coresys: CoreSys,
|
|
||||||
ha_ws_client: AsyncMock,
|
|
||||||
tmp_supervisor_data,
|
|
||||||
path_extern,
|
|
||||||
):
|
):
|
||||||
"""Test manual freeze and thaw for external backup via API."""
|
"""Test manual freeze and thaw for external backup via API."""
|
||||||
coresys.core.state = CoreState.RUNNING
|
coresys.core.state = CoreState.RUNNING
|
||||||
@ -220,13 +210,12 @@ async def test_api_freeze_thaw(
|
|||||||
"partial_backup,exclude_db_setting",
|
"partial_backup,exclude_db_setting",
|
||||||
[(False, True), (True, True), (False, False), (True, False)],
|
[(False, True), (True, True), (False, False), (True, False)],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern")
|
||||||
async def test_api_backup_exclude_database(
|
async def test_api_backup_exclude_database(
|
||||||
api_client: TestClient,
|
api_client: TestClient,
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
partial_backup: bool,
|
partial_backup: bool,
|
||||||
exclude_db_setting: bool,
|
exclude_db_setting: bool,
|
||||||
tmp_supervisor_data,
|
|
||||||
path_extern,
|
|
||||||
):
|
):
|
||||||
"""Test backups exclude the database when specified."""
|
"""Test backups exclude the database when specified."""
|
||||||
coresys.core.state = CoreState.RUNNING
|
coresys.core.state = CoreState.RUNNING
|
||||||
@ -268,13 +257,13 @@ async def _get_job_info(api_client: TestClient, job_id: str) -> dict[str, Any]:
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.usefixtures("path_extern")
|
||||||
async def test_api_backup_restore_background(
|
async def test_api_backup_restore_background(
|
||||||
api_client: TestClient,
|
api_client: TestClient,
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
backup_type: str,
|
backup_type: str,
|
||||||
options: dict[str, Any],
|
options: dict[str, Any],
|
||||||
tmp_supervisor_data: Path,
|
tmp_supervisor_data: Path,
|
||||||
path_extern,
|
|
||||||
):
|
):
|
||||||
"""Test background option on backup/restore APIs."""
|
"""Test background option on backup/restore APIs."""
|
||||||
coresys.core.state = CoreState.RUNNING
|
coresys.core.state = CoreState.RUNNING
|
||||||
@ -354,14 +343,13 @@ async def test_api_backup_restore_background(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.usefixtures("install_addon_ssh", "path_extern")
|
||||||
async def test_api_backup_errors(
|
async def test_api_backup_errors(
|
||||||
api_client: TestClient,
|
api_client: TestClient,
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
backup_type: str,
|
backup_type: str,
|
||||||
options: dict[str, Any],
|
options: dict[str, Any],
|
||||||
tmp_supervisor_data: Path,
|
tmp_supervisor_data: Path,
|
||||||
install_addon_ssh,
|
|
||||||
path_extern,
|
|
||||||
):
|
):
|
||||||
"""Test error reporting in backup job."""
|
"""Test error reporting in backup job."""
|
||||||
coresys.core.state = CoreState.RUNNING
|
coresys.core.state = CoreState.RUNNING
|
||||||
@ -604,14 +592,13 @@ async def test_upload_download(
|
|||||||
assert backup == out_backup
|
assert backup == out_backup
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("path_extern")
|
@pytest.mark.usefixtures("path_extern", "tmp_supervisor_data")
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("backup_type", "inputs"), [("full", {}), ("partial", {"folders": ["ssl"]})]
|
("backup_type", "inputs"), [("full", {}), ("partial", {"folders": ["ssl"]})]
|
||||||
)
|
)
|
||||||
async def test_backup_to_multiple_locations(
|
async def test_backup_to_multiple_locations(
|
||||||
api_client: TestClient,
|
api_client: TestClient,
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
tmp_supervisor_data: Path,
|
|
||||||
backup_type: str,
|
backup_type: str,
|
||||||
inputs: dict[str, Any],
|
inputs: dict[str, Any],
|
||||||
):
|
):
|
||||||
@ -643,10 +630,10 @@ async def test_backup_to_multiple_locations(
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("backup_type", "inputs"), [("full", {}), ("partial", {"folders": ["ssl"]})]
|
("backup_type", "inputs"), [("full", {}), ("partial", {"folders": ["ssl"]})]
|
||||||
)
|
)
|
||||||
|
@pytest.mark.usefixtures("tmp_supervisor_data")
|
||||||
async def test_backup_with_extras(
|
async def test_backup_with_extras(
|
||||||
api_client: TestClient,
|
api_client: TestClient,
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
tmp_supervisor_data: Path,
|
|
||||||
backup_type: str,
|
backup_type: str,
|
||||||
inputs: dict[str, Any],
|
inputs: dict[str, Any],
|
||||||
):
|
):
|
||||||
@ -671,11 +658,8 @@ async def test_backup_with_extras(
|
|||||||
slug = result["data"]["extra"] == {"user": "test", "scheduled": True}
|
slug = result["data"]["extra"] == {"user": "test", "scheduled": True}
|
||||||
|
|
||||||
|
|
||||||
async def test_upload_to_multiple_locations(
|
@pytest.mark.usefixtures("tmp_supervisor_data")
|
||||||
api_client: TestClient,
|
async def test_upload_to_multiple_locations(api_client: TestClient, coresys: CoreSys):
|
||||||
coresys: CoreSys,
|
|
||||||
tmp_supervisor_data: Path,
|
|
||||||
):
|
|
||||||
"""Test uploading a backup to multiple locations."""
|
"""Test uploading a backup to multiple locations."""
|
||||||
backup_file = get_fixture_path("backup_example.tar")
|
backup_file = get_fixture_path("backup_example.tar")
|
||||||
|
|
||||||
@ -700,10 +684,9 @@ async def test_upload_to_multiple_locations(
|
|||||||
assert coresys.backups.get("7fed74c8").location is None
|
assert coresys.backups.get("7fed74c8").location is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("tmp_supervisor_data")
|
||||||
async def test_upload_duplicate_backup_new_location(
|
async def test_upload_duplicate_backup_new_location(
|
||||||
api_client: TestClient,
|
api_client: TestClient, coresys: CoreSys
|
||||||
coresys: CoreSys,
|
|
||||||
tmp_supervisor_data: Path,
|
|
||||||
):
|
):
|
||||||
"""Test uploading a backup that already exists to a new location."""
|
"""Test uploading a backup that already exists to a new location."""
|
||||||
backup_file = get_fixture_path("backup_example.tar")
|
backup_file = get_fixture_path("backup_example.tar")
|
||||||
@ -809,3 +792,21 @@ async def test_download_backup_from_invalid_location(api_client: TestClient):
|
|||||||
assert resp.status == 400
|
assert resp.status == 400
|
||||||
body = await resp.json()
|
body = await resp.json()
|
||||||
assert body["message"] == "Backup test is not in location .cloud_backup"
|
assert body["message"] == "Backup test is not in location .cloud_backup"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("tmp_supervisor_data")
|
||||||
|
async def test_partial_backup_all_addons(
|
||||||
|
api_client: TestClient,
|
||||||
|
coresys: CoreSys,
|
||||||
|
install_addon_ssh: Addon,
|
||||||
|
):
|
||||||
|
"""Test backup including extra metdata."""
|
||||||
|
coresys.core.state = CoreState.RUNNING
|
||||||
|
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||||
|
|
||||||
|
with patch.object(Backup, "store_addons") as store_addons:
|
||||||
|
resp = await api_client.post(
|
||||||
|
"/backups/new/partial", json={"name": "All addons test", "addons": "ALL"}
|
||||||
|
)
|
||||||
|
assert resp.status == 200
|
||||||
|
store_addons.assert_called_once_with([install_addon_ssh])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user