Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions homeassistant/components/hassio/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

import asyncio
from dataclasses import dataclass, field
from datetime import datetime
import logging
from typing import Any, NotRequired, TypedDict

from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HassJob, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
Expand Down Expand Up @@ -36,6 +38,7 @@
EVENT_SUPPORTED_CHANGED,
ISSUE_KEY_SYSTEM_DOCKER_CONFIG,
PLACEHOLDER_KEY_REFERENCE,
REQUEST_REFRESH_DELAY,
UPDATE_KEY_SUPERVISOR,
SupervisorIssueContext,
)
Expand Down Expand Up @@ -303,12 +306,17 @@ async def setup(self) -> None:
self._hass, EVENT_SUPERVISOR_EVENT, self._supervisor_events_to_issues
)

async def update(self) -> None:
async def update(self, _: datetime | None = None) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's too late, but I'd consider making this method protected to avoid outside usage that could spawn multiple retries in the same time span.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could cancel any already scheduled retry before scheduling a new one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm yea good call. I'll make a PR for that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Update issues from Supervisor resolution center."""
try:
data = await self._client.get_resolution_info()
except HassioAPIError as err:
_LOGGER.error("Failed to update supervisor issues: %r", err)
async_call_later(
self._hass,
REQUEST_REFRESH_DELAY,
HassJob(self.update, cancel_on_shutdown=True),
)
return
self.unhealthy_reasons = set(data[ATTR_UNHEALTHY])
self.unsupported_reasons = set(data[ATTR_UNSUPPORTED])
Expand Down
78 changes: 77 additions & 1 deletion tests/components/hassio/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

import asyncio
from http import HTTPStatus
import os
from typing import Any
from unittest.mock import ANY, patch
Expand All @@ -14,7 +16,7 @@

from .test_init import MOCK_ENVIRON

from tests.test_util.aiohttp import AiohttpClientMocker
from tests.test_util.aiohttp import AiohttpClientMocker, AiohttpClientMockResponse
from tests.typing import WebSocketGenerator


Expand Down Expand Up @@ -529,6 +531,80 @@ async def test_supervisor_issues(
)


async def test_supervisor_issues_initial_failure(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test issues manager retries after initial update failure."""
responses = [
AiohttpClientMockResponse(
method="get",
url="http://127.0.0.1/resolution/info",
status=HTTPStatus.BAD_REQUEST,
json={
"result": "error",
"message": "System is not ready with state: setup",
},
),
AiohttpClientMockResponse(
method="get",
url="http://127.0.0.1/resolution/info",
status=HTTPStatus.OK,
json={
"result": "ok",
"data": {
"unsupported": [],
"unhealthy": [],
"suggestions": [],
"issues": [
{
"uuid": "1234",
"type": "reboot_required",
"context": "system",
"reference": None,
},
],
"checks": [
{"enabled": True, "slug": "supervisor_trust"},
{"enabled": True, "slug": "free_space"},
],
},
},
),
]

async def mock_responses(*args):
nonlocal responses
return responses.pop(0)

aioclient_mock.get(
"http://127.0.0.1/resolution/info",
side_effect=mock_responses,
)
aioclient_mock.get(
"http://127.0.0.1/resolution/issue/1234/suggestions",
json={"result": "ok", "data": {"suggestions": []}},
)

with patch("homeassistant.components.hassio.issues.REQUEST_REFRESH_DELAY", new=0.1):
result = await async_setup_component(hass, "hassio", {})
assert result

client = await hass_ws_client(hass)

await client.send_json({"id": 1, "type": "repairs/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert len(msg["result"]["issues"]) == 0

await asyncio.sleep(0.1)
await client.send_json({"id": 2, "type": "repairs/list_issues"})
msg = await client.receive_json()
assert msg["success"]
assert len(msg["result"]["issues"]) == 1


async def test_supervisor_issues_add_remove(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
Expand Down