From 5933b66b1c9706f4bb7db69f08bb6cc1b9efd4da Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 22 Sep 2021 15:59:35 +0200 Subject: [PATCH] Add os-agent to evaluations for supported systems (#3138) --- supervisor/host/const.py | 2 +- supervisor/host/manager.py | 2 +- supervisor/jobs/decorator.py | 2 +- supervisor/resolution/const.py | 11 ++-- supervisor/resolution/evaluations/os_agent.py | 35 ++++++++++++ supervisor/resolution/evaluations/systemd.py | 1 + .../evaluation/test_evaluate_os_agent.py | 53 +++++++++++++++++++ .../evaluation/test_evaluate_systemd.py | 4 +- 8 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 supervisor/resolution/evaluations/os_agent.py create mode 100644 tests/resolution/evaluation/test_evaluate_os_agent.py diff --git a/supervisor/host/const.py b/supervisor/host/const.py index 1d41dd8dd..e2f9ff896 100644 --- a/supervisor/host/const.py +++ b/supervisor/host/const.py @@ -44,5 +44,5 @@ class HostFeature(str, Enum): REBOOT = "reboot" SERVICES = "services" SHUTDOWN = "shutdown" - AGENT = "agent" + OS_AGENT = "os_agent" TIMEDATE = "timedate" diff --git a/supervisor/host/manager.py b/supervisor/host/manager.py index 64ce046f6..0c06d78f4 100644 --- a/supervisor/host/manager.py +++ b/supervisor/host/manager.py @@ -88,7 +88,7 @@ class HostManager(CoreSysAttributes): features.append(HostFeature.TIMEDATE) if self.sys_dbus.agent.is_connected: - features.append(HostFeature.AGENT) + features.append(HostFeature.OS_AGENT) if self.sys_os.available: features.append(HostFeature.HAOS) diff --git a/supervisor/jobs/decorator.py b/supervisor/jobs/decorator.py index bc610c2f8..f30dde891 100644 --- a/supervisor/jobs/decorator.py +++ b/supervisor/jobs/decorator.py @@ -175,7 +175,7 @@ class Job(CoreSysAttributes): if ( JobCondition.OS_AGENT in self.conditions - and HostFeature.AGENT not in self.sys_host.features + and HostFeature.OS_AGENT not in self.sys_host.features ): raise JobConditionException( f"'{self._method.__qualname__}' blocked from execution, no Home Assistant OS-Agent available" diff --git a/supervisor/resolution/const.py b/supervisor/resolution/const.py index 8dd026ecd..48ccc2c76 100644 --- a/supervisor/resolution/const.py +++ b/supervisor/resolution/const.py @@ -27,19 +27,20 @@ class ContextType(str, Enum): class UnsupportedReason(str, Enum): """Reasons for unsupported status.""" - CONTAINER = "container" - DBUS = "dbus" APPARMOR = "apparmor" + CONTAINER = "container" + CONTENT_TRUST = "content_trust" + DBUS = "dbus" DOCKER_CONFIGURATION = "docker_configuration" DOCKER_VERSION = "docker_version" + JOB_CONDITIONS = "job_conditions" LXC = "lxc" NETWORK_MANAGER = "network_manager" OS = "os" + OS_AGENT = "os_agent" PRIVILEGED = "privileged" - SYSTEMD = "systemd" - JOB_CONDITIONS = "job_conditions" - CONTENT_TRUST = "content_trust" SOURCE_MODS = "source_mods" + SYSTEMD = "systemd" class UnhealthyReason(str, Enum): diff --git a/supervisor/resolution/evaluations/os_agent.py b/supervisor/resolution/evaluations/os_agent.py new file mode 100644 index 000000000..37fedddb2 --- /dev/null +++ b/supervisor/resolution/evaluations/os_agent.py @@ -0,0 +1,35 @@ +"""Evaluation class for host agent.""" + +from ...const import CoreState +from ...coresys import CoreSys +from ...host.const import HostFeature +from ..const import UnsupportedReason +from .base import EvaluateBase + + +def setup(coresys: CoreSys) -> EvaluateBase: + """Initialize evaluation-setup function.""" + return EvaluateOSAgent(coresys) + + +class EvaluateOSAgent(EvaluateBase): + """Evaluate host agent support.""" + + @property + def reason(self) -> UnsupportedReason: + """Return a UnsupportedReason enum.""" + return UnsupportedReason.OS_AGENT + + @property + def on_failure(self) -> str: + """Return a string that is printed when self.evaluate is False.""" + return "OS-Agent is not correctly working" + + @property + def states(self) -> list[CoreState]: + """Return a list of valid states when this evaluation can run.""" + return [CoreState.SETUP] + + async def evaluate(self): + """Run evaluation.""" + return HostFeature.OS_AGENT not in self.sys_host.features diff --git a/supervisor/resolution/evaluations/systemd.py b/supervisor/resolution/evaluations/systemd.py index e0db4c43d..c72734bdf 100644 --- a/supervisor/resolution/evaluations/systemd.py +++ b/supervisor/resolution/evaluations/systemd.py @@ -39,5 +39,6 @@ class EvaluateSystemd(EvaluateBase): HostFeature.SERVICES, HostFeature.SHUTDOWN, HostFeature.REBOOT, + HostFeature.TIMEDATE, ) ) diff --git a/tests/resolution/evaluation/test_evaluate_os_agent.py b/tests/resolution/evaluation/test_evaluate_os_agent.py new file mode 100644 index 000000000..34665ae96 --- /dev/null +++ b/tests/resolution/evaluation/test_evaluate_os_agent.py @@ -0,0 +1,53 @@ +"""Test evaluation base.""" +# pylint: disable=import-error,protected-access +from unittest.mock import MagicMock, patch + +from supervisor.const import CoreState +from supervisor.coresys import CoreSys +from supervisor.host.const import HostFeature +from supervisor.resolution.evaluations.os_agent import EvaluateOSAgent + + +async def test_evaluation(coresys: CoreSys): + """Test evaluation.""" + agent = EvaluateOSAgent(coresys) + coresys.core.state = CoreState.SETUP + + assert agent.reason not in coresys.resolution.unsupported + + coresys._host = MagicMock() + + coresys.host.features = [HostFeature.HOSTNAME] + await agent() + assert agent.reason in coresys.resolution.unsupported + + coresys.host.features = [ + HostFeature.OS_AGENT, + ] + await agent() + assert agent.reason not in coresys.resolution.unsupported + + +async def test_did_run(coresys: CoreSys): + """Test that the evaluation ran as expected.""" + agent = EvaluateOSAgent(coresys) + should_run = agent.states + should_not_run = [state for state in CoreState if state not in should_run] + assert len(should_run) != 0 + assert len(should_not_run) != 0 + + with patch( + "supervisor.resolution.evaluations.os_agent.EvaluateOSAgent.evaluate", + return_value=None, + ) as evaluate: + for state in should_run: + coresys.core.state = state + await agent() + evaluate.assert_called_once() + evaluate.reset_mock() + + for state in should_not_run: + coresys.core.state = state + await agent() + evaluate.assert_not_called() + evaluate.reset_mock() diff --git a/tests/resolution/evaluation/test_evaluate_systemd.py b/tests/resolution/evaluation/test_evaluate_systemd.py index 5a79c4ca5..0bd1dd750 100644 --- a/tests/resolution/evaluation/test_evaluate_systemd.py +++ b/tests/resolution/evaluation/test_evaluate_systemd.py @@ -22,12 +22,14 @@ async def test_evaluation(coresys: CoreSys): assert systemd.reason in coresys.resolution.unsupported coresys.host.features = [ + HostFeature.HOSTNAME, HostFeature.SERVICES, HostFeature.SHUTDOWN, HostFeature.REBOOT, + HostFeature.TIMEDATE, ] await systemd() - assert systemd.reason in coresys.resolution.unsupported + assert systemd.reason not in coresys.resolution.unsupported async def test_did_run(coresys: CoreSys):