Events when unhealthy/unsupported changes (#3951)

* Events when unhealthy/unsupported changes

* called_once_with to called_once
This commit is contained in:
Mike Degatano 2022-10-16 05:07:15 -04:00 committed by GitHub
parent bde5c938a7
commit 5d2b5bada7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 144 additions and 3 deletions

View File

@ -32,6 +32,8 @@ class WSEvent(str, Enum):
"""Websocket events."""
ADDON = "addon"
HEALTH_CHANGED = "health_changed"
ISSUE_CHANGED = "issue_changed"
ISSUE_REMOVED = "issue_removed"
SUPERVISOR_UPDATE = "supervisor_update"
SUPPORTED_CHANGED = "supported_changed"

View File

@ -3,7 +3,13 @@ from uuid import UUID, uuid4
import attr
from .const import ContextType, IssueType, SuggestionType
from .const import (
ContextType,
IssueType,
SuggestionType,
UnhealthyReason,
UnsupportedReason,
)
@attr.s(frozen=True, slots=True)
@ -24,3 +30,19 @@ class Suggestion:
context: ContextType = attr.ib()
reference: str | None = attr.ib(default=None)
uuid: UUID = attr.ib(factory=lambda: uuid4().hex, eq=False, init=False)
@attr.s(frozen=True, slots=True)
class HealthChanged:
"""Describe change in system health."""
healthy: bool = attr.ib()
unhealthy_reasons: list[UnhealthyReason] | None = attr.ib(default=None)
@attr.s(frozen=True, slots=True)
class SupportedChanged:
"""Describe change in system supported."""
supported: bool = attr.ib()
unsupported_reasons: list[UnsupportedReason] | None = attr.ib(default=None)

View File

@ -18,7 +18,7 @@ from .const import (
UnhealthyReason,
UnsupportedReason,
)
from .data import Issue, Suggestion
from .data import HealthChanged, Issue, Suggestion, SupportedChanged
from .evaluate import ResolutionEvaluation
from .fixup import ResolutionFixup
from .notify import ResolutionNotify
@ -125,6 +125,10 @@ class ResolutionManager(FileConfiguration, CoreSysAttributes):
"""Add a reason for unsupported."""
if reason not in self._unsupported:
self._unsupported.append(reason)
self.sys_homeassistant.websocket.supervisor_event(
WSEvent.SUPPORTED_CHANGED,
attr.asdict(SupportedChanged(False, self.unsupported)),
)
@property
def unhealthy(self) -> list[UnhealthyReason]:
@ -136,6 +140,10 @@ class ResolutionManager(FileConfiguration, CoreSysAttributes):
"""Add a reason for unsupported."""
if reason not in self._unhealthy:
self._unhealthy.append(reason)
self.sys_homeassistant.websocket.supervisor_event(
WSEvent.HEALTH_CHANGED,
attr.asdict(HealthChanged(False, self.unhealthy)),
)
def get_suggestion(self, uuid: str) -> Suggestion:
"""Return suggestion with uuid."""
@ -228,6 +236,12 @@ class ResolutionManager(FileConfiguration, CoreSysAttributes):
if reason not in self._unsupported:
raise ResolutionError(f"The reason {reason} is not active", _LOGGER.warning)
self._unsupported.remove(reason)
self.sys_homeassistant.websocket.supervisor_event(
WSEvent.SUPPORTED_CHANGED,
attr.asdict(
SupportedChanged(self.sys_core.supported, self.unsupported or None)
),
)
def suggestions_for_issue(self, issue: Issue) -> set[Suggestion]:
"""Get suggestions that fix an issue."""

View File

@ -240,7 +240,7 @@ async def test_host_connectivity_disabled(coresys: CoreSys):
await asyncio.sleep(0)
assert coresys.host.network.connectivity is True
await asyncio.sleep(0)
client.async_send_command.assert_called_once_with(
client.async_send_command.assert_called_with(
{
"type": WSType.SUPERVISOR_EVENT,
"data": {

View File

@ -261,3 +261,106 @@ async def test_resolution_apply_suggestion_multiple_copies(coresys: CoreSys):
assert remove_store_1 in coresys.resolution.suggestions
assert remove_store_2 not in coresys.resolution.suggestions
assert remove_store_3 in coresys.resolution.suggestions
async def test_events_on_unsupported_changed(coresys: CoreSys):
"""Test events fired when unsupported changes."""
with patch.object(
type(coresys.homeassistant.websocket), "async_send_message"
) as send_message:
# Marking system as unsupported tells HA
assert coresys.resolution.unsupported == []
coresys.resolution.unsupported = UnsupportedReason.CONNECTIVITY_CHECK
await asyncio.sleep(0)
assert coresys.resolution.unsupported == [UnsupportedReason.CONNECTIVITY_CHECK]
send_message.assert_called_once_with(
_supervisor_event_message(
"supported_changed",
{"supported": False, "unsupported_reasons": ["connectivity_check"]},
)
)
# Adding the same reason again does nothing
send_message.reset_mock()
coresys.resolution.unsupported = UnsupportedReason.CONNECTIVITY_CHECK
await asyncio.sleep(0)
assert coresys.resolution.unsupported == [UnsupportedReason.CONNECTIVITY_CHECK]
send_message.assert_not_called()
# Adding and removing additional reasons tells HA unsupported reasons changed
coresys.resolution.unsupported = UnsupportedReason.JOB_CONDITIONS
await asyncio.sleep(0)
assert coresys.resolution.unsupported == [
UnsupportedReason.CONNECTIVITY_CHECK,
UnsupportedReason.JOB_CONDITIONS,
]
send_message.assert_called_once_with(
_supervisor_event_message(
"supported_changed",
{
"supported": False,
"unsupported_reasons": ["connectivity_check", "job_conditions"],
},
)
)
send_message.reset_mock()
coresys.resolution.dismiss_unsupported(UnsupportedReason.CONNECTIVITY_CHECK)
await asyncio.sleep(0)
assert coresys.resolution.unsupported == [UnsupportedReason.JOB_CONDITIONS]
send_message.assert_called_once_with(
_supervisor_event_message(
"supported_changed",
{"supported": False, "unsupported_reasons": ["job_conditions"]},
)
)
# Dismissing all unsupported reasons tells HA its supported again
send_message.reset_mock()
coresys.resolution.dismiss_unsupported(UnsupportedReason.JOB_CONDITIONS)
await asyncio.sleep(0)
assert coresys.resolution.unsupported == []
send_message.assert_called_once_with(
_supervisor_event_message(
"supported_changed", {"supported": True, "unsupported_reasons": None}
)
)
async def test_events_on_unhealthy_changed(coresys: CoreSys):
"""Test events fired when unhealthy changes."""
with patch.object(
type(coresys.homeassistant.websocket), "async_send_message"
) as send_message:
# Marking system as unhealthy tells HA
assert coresys.resolution.unhealthy == []
coresys.resolution.unhealthy = UnhealthyReason.DOCKER
await asyncio.sleep(0)
assert coresys.resolution.unhealthy == [UnhealthyReason.DOCKER]
send_message.assert_called_once_with(
_supervisor_event_message(
"health_changed",
{"healthy": False, "unhealthy_reasons": ["docker"]},
)
)
# Adding the same reason again does nothing
send_message.reset_mock()
coresys.resolution.unhealthy = UnhealthyReason.DOCKER
await asyncio.sleep(0)
assert coresys.resolution.unhealthy == [UnhealthyReason.DOCKER]
send_message.assert_not_called()
# Adding an additional reason tells HA unhealthy reasons changed
coresys.resolution.unhealthy = UnhealthyReason.UNTRUSTED
await asyncio.sleep(0)
assert coresys.resolution.unhealthy == [
UnhealthyReason.DOCKER,
UnhealthyReason.UNTRUSTED,
]
send_message.assert_called_once_with(
_supervisor_event_message(
"health_changed",
{"healthy": False, "unhealthy_reasons": ["docker", "untrusted"]},
)
)