mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Handle cloud subscription expired for backup upload (#141564)
Handle cloud backup subscription expired for upload
This commit is contained in:
parent
e8aa3e6d34
commit
e9e95f45d8
@ -4,13 +4,14 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncIterator, Callable, Coroutine, Mapping
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
import random
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from hass_nabucasa import Cloud, CloudError
|
||||
from hass_nabucasa.api import CloudApiNonRetryableError
|
||||
from hass_nabucasa.api import CloudApiError, CloudApiNonRetryableError
|
||||
from hass_nabucasa.cloud_api import (
|
||||
FilesHandlerListEntry,
|
||||
async_files_delete_file,
|
||||
@ -120,6 +121,8 @@ class CloudBackupAgent(BackupAgent):
|
||||
"""
|
||||
if not backup.protected:
|
||||
raise BackupAgentError("Cloud backups must be protected")
|
||||
if self._cloud.subscription_expired:
|
||||
raise BackupAgentError("Cloud subscription has expired")
|
||||
|
||||
size = backup.size
|
||||
try:
|
||||
@ -152,6 +155,13 @@ class CloudBackupAgent(BackupAgent):
|
||||
) from err
|
||||
raise BackupAgentError(f"Failed to upload backup {err}") from err
|
||||
except CloudError as err:
|
||||
if (
|
||||
isinstance(err, CloudApiError)
|
||||
and isinstance(err.orig_exc, ClientResponseError)
|
||||
and err.orig_exc.status == HTTPStatus.FORBIDDEN
|
||||
and self._cloud.subscription_expired
|
||||
):
|
||||
raise BackupAgentError("Cloud subscription has expired") from err
|
||||
if tries == _RETRY_LIMIT:
|
||||
raise BackupAgentError(f"Failed to upload backup {err}") from err
|
||||
tries += 1
|
||||
|
@ -5,9 +5,9 @@ from io import StringIO
|
||||
from typing import Any
|
||||
from unittest.mock import ANY, Mock, PropertyMock, patch
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from hass_nabucasa import CloudError
|
||||
from hass_nabucasa.api import CloudApiNonRetryableError
|
||||
from hass_nabucasa.api import CloudApiError, CloudApiNonRetryableError
|
||||
from hass_nabucasa.files import FilesError, StorageType
|
||||
import pytest
|
||||
|
||||
@ -547,6 +547,120 @@ async def test_agents_upload_not_protected(
|
||||
assert stored_backup["failed_agent_ids"] == ["cloud.cloud"]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("cloud_logged_in", "mock_list_files")
|
||||
async def test_agents_upload_not_subscribed(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
cloud: Mock,
|
||||
) -> None:
|
||||
"""Test upload backup when cloud user is not subscribed."""
|
||||
cloud.subscription_expired = True
|
||||
client = await hass_client()
|
||||
backup_data = "test"
|
||||
backup_id = "test-backup"
|
||||
test_backup = AgentBackup(
|
||||
addons=[AddonInfo(name="Test", slug="test", version="1.0.0")],
|
||||
backup_id=backup_id,
|
||||
database_included=True,
|
||||
date="1970-01-01T00:00:00.000Z",
|
||||
extra_metadata={},
|
||||
folders=[Folder.MEDIA, Folder.SHARE],
|
||||
homeassistant_included=True,
|
||||
homeassistant_version="2024.12.0",
|
||||
name="Test",
|
||||
protected=True,
|
||||
size=len(backup_data),
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.backup.manager.BackupManager.async_get_backup",
|
||||
) as fetch_backup,
|
||||
patch(
|
||||
"homeassistant.components.backup.manager.read_backup",
|
||||
return_value=test_backup,
|
||||
),
|
||||
patch("pathlib.Path.open") as mocked_open,
|
||||
):
|
||||
mocked_open.return_value.read = Mock(side_effect=[backup_data.encode(), b""])
|
||||
fetch_backup.return_value = test_backup
|
||||
resp = await client.post(
|
||||
"/api/backup/upload?agent_id=cloud.cloud",
|
||||
data={"file": StringIO(backup_data)},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert resp.status == 201
|
||||
assert cloud.files.upload.call_count == 0
|
||||
store_backups = hass_storage[BACKUP_DOMAIN]["data"]["backups"]
|
||||
assert len(store_backups) == 1
|
||||
stored_backup = store_backups[0]
|
||||
assert stored_backup["backup_id"] == backup_id
|
||||
assert stored_backup["failed_agent_ids"] == ["cloud.cloud"]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("cloud_logged_in", "mock_list_files")
|
||||
async def test_agents_upload_not_subscribed_midway(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
cloud: Mock,
|
||||
) -> None:
|
||||
"""Test upload backup when cloud subscription expires during the call."""
|
||||
client = await hass_client()
|
||||
backup_data = "test"
|
||||
backup_id = "test-backup"
|
||||
test_backup = AgentBackup(
|
||||
addons=[AddonInfo(name="Test", slug="test", version="1.0.0")],
|
||||
backup_id=backup_id,
|
||||
database_included=True,
|
||||
date="1970-01-01T00:00:00.000Z",
|
||||
extra_metadata={},
|
||||
folders=[Folder.MEDIA, Folder.SHARE],
|
||||
homeassistant_included=True,
|
||||
homeassistant_version="2024.12.0",
|
||||
name="Test",
|
||||
protected=True,
|
||||
size=len(backup_data),
|
||||
)
|
||||
|
||||
async def mock_upload(*args: Any, **kwargs: Any) -> None:
|
||||
"""Mock file upload."""
|
||||
cloud.subscription_expired = True
|
||||
raise CloudApiError(
|
||||
"Boom!", orig_exc=ClientResponseError(Mock(), Mock(), status=403)
|
||||
)
|
||||
|
||||
cloud.files.upload.side_effect = mock_upload
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.backup.manager.BackupManager.async_get_backup",
|
||||
) as fetch_backup,
|
||||
patch(
|
||||
"homeassistant.components.backup.manager.read_backup",
|
||||
return_value=test_backup,
|
||||
),
|
||||
patch("pathlib.Path.open") as mocked_open,
|
||||
):
|
||||
mocked_open.return_value.read = Mock(side_effect=[backup_data.encode(), b""])
|
||||
fetch_backup.return_value = test_backup
|
||||
resp = await client.post(
|
||||
"/api/backup/upload?agent_id=cloud.cloud",
|
||||
data={"file": StringIO(backup_data)},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert resp.status == 201
|
||||
assert cloud.files.upload.call_count == 1
|
||||
store_backups = hass_storage[BACKUP_DOMAIN]["data"]["backups"]
|
||||
assert len(store_backups) == 1
|
||||
stored_backup = store_backups[0]
|
||||
assert stored_backup["backup_id"] == backup_id
|
||||
assert stored_backup["failed_agent_ids"] == ["cloud.cloud"]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("cloud_logged_in", "mock_list_files")
|
||||
async def test_agents_upload_wrong_size(
|
||||
hass: HomeAssistant,
|
||||
|
Loading…
x
Reference in New Issue
Block a user