mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-16 05:36:29 +00:00
Allow all job conditions to be ignored (#4107)
* Allow all job conditions to be ignored * Clear features cache in test * patch out OS Agent supported feature
This commit is contained in:
parent
83e5359bd2
commit
72d81e43dd
@ -199,14 +199,14 @@ class Job(CoreSysAttributes):
|
||||
f"'{self._method.__qualname__}' blocked from execution, not enough free space ({self.sys_host.info.free_space}GB) left on the device"
|
||||
)
|
||||
|
||||
if JobCondition.INTERNET_SYSTEM in self.conditions:
|
||||
if JobCondition.INTERNET_SYSTEM in used_conditions:
|
||||
await self.sys_supervisor.check_connectivity()
|
||||
if not self.sys_supervisor.connectivity:
|
||||
raise JobConditionException(
|
||||
f"'{self._method.__qualname__}' blocked from execution, no supervisor internet connection"
|
||||
)
|
||||
|
||||
if JobCondition.INTERNET_HOST in self.conditions:
|
||||
if JobCondition.INTERNET_HOST in used_conditions:
|
||||
await self.sys_host.network.check_connectivity()
|
||||
if (
|
||||
self.sys_host.network.connectivity is not None
|
||||
@ -216,13 +216,13 @@ class Job(CoreSysAttributes):
|
||||
f"'{self._method.__qualname__}' blocked from execution, no host internet connection"
|
||||
)
|
||||
|
||||
if JobCondition.HAOS in self.conditions and not self.sys_os.available:
|
||||
if JobCondition.HAOS in used_conditions and not self.sys_os.available:
|
||||
raise JobConditionException(
|
||||
f"'{self._method.__qualname__}' blocked from execution, no Home Assistant OS available"
|
||||
)
|
||||
|
||||
if (
|
||||
JobCondition.OS_AGENT in self.conditions
|
||||
JobCondition.OS_AGENT in used_conditions
|
||||
and HostFeature.OS_AGENT not in self.sys_host.features
|
||||
):
|
||||
raise JobConditionException(
|
||||
@ -230,7 +230,7 @@ class Job(CoreSysAttributes):
|
||||
)
|
||||
|
||||
if (
|
||||
JobCondition.HOST_NETWORK in self.conditions
|
||||
JobCondition.HOST_NETWORK in used_conditions
|
||||
and not self.sys_dbus.network.is_connected
|
||||
):
|
||||
raise JobConditionException(
|
||||
@ -238,7 +238,7 @@ class Job(CoreSysAttributes):
|
||||
)
|
||||
|
||||
if (
|
||||
JobCondition.AUTO_UPDATE in self.conditions
|
||||
JobCondition.AUTO_UPDATE in used_conditions
|
||||
and not self.sys_updater.auto_update
|
||||
):
|
||||
raise JobConditionException(
|
||||
@ -246,14 +246,14 @@ class Job(CoreSysAttributes):
|
||||
)
|
||||
|
||||
if (
|
||||
JobCondition.SUPERVISOR_UPDATED in self.conditions
|
||||
JobCondition.SUPERVISOR_UPDATED in used_conditions
|
||||
and self.sys_supervisor.need_update
|
||||
):
|
||||
raise JobConditionException(
|
||||
f"'{self._method.__qualname__}' blocked from execution, supervisor needs to be updated first"
|
||||
)
|
||||
|
||||
if JobCondition.PLUGINS_UPDATED in self.conditions and (
|
||||
if JobCondition.PLUGINS_UPDATED in used_conditions and (
|
||||
out_of_date := [
|
||||
plugin for plugin in self.sys_plugins.all_plugins if plugin.need_update
|
||||
]
|
||||
|
@ -16,13 +16,16 @@ from supervisor.exceptions import (
|
||||
JobException,
|
||||
PluginJobError,
|
||||
)
|
||||
from supervisor.host.const import HostFeature
|
||||
from supervisor.host.manager import HostManager
|
||||
from supervisor.jobs.const import JobExecutionLimit
|
||||
from supervisor.jobs.decorator import Job, JobCondition
|
||||
from supervisor.plugins.audio import PluginAudio
|
||||
from supervisor.resolution.const import UnhealthyReason
|
||||
from supervisor.utils.dt import utcnow
|
||||
|
||||
|
||||
async def test_healthy(coresys: CoreSys):
|
||||
async def test_healthy(coresys: CoreSys, caplog: pytest.LogCaptureFixture):
|
||||
"""Test the healty decorator."""
|
||||
|
||||
class TestClass:
|
||||
@ -42,6 +45,10 @@ async def test_healthy(coresys: CoreSys):
|
||||
|
||||
coresys.resolution.unhealthy = UnhealthyReason.DOCKER
|
||||
assert not await test.execute()
|
||||
assert "blocked from execution, system is not healthy - docker" in caplog.text
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.HEALTHY]
|
||||
assert await test.execute()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -93,6 +100,13 @@ async def test_internet(
|
||||
assert await test.execute_host() is host_result
|
||||
assert await test.execute_system() is system_result
|
||||
|
||||
coresys.jobs.ignore_conditions = [
|
||||
JobCondition.INTERNET_HOST,
|
||||
JobCondition.INTERNET_SYSTEM,
|
||||
]
|
||||
assert await test.execute_host()
|
||||
assert await test.execute_system()
|
||||
|
||||
|
||||
async def test_free_space(coresys: CoreSys):
|
||||
"""Test the free_space decorator."""
|
||||
@ -116,6 +130,9 @@ async def test_free_space(coresys: CoreSys):
|
||||
with patch("shutil.disk_usage", return_value=(42, 42, (512.0**3))):
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.FREE_SPACE]
|
||||
assert await test.execute()
|
||||
|
||||
|
||||
async def test_haos(coresys: CoreSys):
|
||||
"""Test the haos decorator."""
|
||||
@ -139,9 +156,12 @@ async def test_haos(coresys: CoreSys):
|
||||
coresys.os._available = False
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.HAOS]
|
||||
assert await test.execute()
|
||||
|
||||
async def test_exception(coresys: CoreSys):
|
||||
"""Test the healty decorator."""
|
||||
|
||||
async def test_exception(coresys: CoreSys, capture_exception: Mock):
|
||||
"""Test handled exception."""
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
@ -160,9 +180,12 @@ async def test_exception(coresys: CoreSys):
|
||||
with pytest.raises(HassioError):
|
||||
assert await test.execute()
|
||||
|
||||
capture_exception.assert_not_called()
|
||||
|
||||
async def test_exception_not_handle(coresys: CoreSys):
|
||||
"""Test the healty decorator."""
|
||||
|
||||
async def test_exception_not_handle(coresys: CoreSys, capture_exception: Mock):
|
||||
"""Test unhandled exception."""
|
||||
err = Exception()
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
@ -174,13 +197,15 @@ async def test_exception_not_handle(coresys: CoreSys):
|
||||
@Job(conditions=[JobCondition.HEALTHY])
|
||||
async def execute(self):
|
||||
"""Execute the class method."""
|
||||
raise Exception()
|
||||
raise err
|
||||
|
||||
test = TestClass(coresys)
|
||||
|
||||
with pytest.raises(JobException):
|
||||
assert await test.execute()
|
||||
|
||||
capture_exception.assert_called_once_with(err)
|
||||
|
||||
|
||||
async def test_running(coresys: CoreSys):
|
||||
"""Test the running decorator."""
|
||||
@ -205,30 +230,6 @@ async def test_running(coresys: CoreSys):
|
||||
coresys.core.state = CoreState.FREEZE
|
||||
assert not await test.execute()
|
||||
|
||||
|
||||
async def test_ignore_conditions(coresys: CoreSys):
|
||||
"""Test the ignore conditions decorator."""
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
|
||||
def __init__(self, coresys: CoreSys):
|
||||
"""Initialize the test class."""
|
||||
self.coresys = coresys
|
||||
|
||||
@Job(conditions=[JobCondition.RUNNING])
|
||||
async def execute(self):
|
||||
"""Execute the class method."""
|
||||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
|
||||
coresys.core.state = CoreState.RUNNING
|
||||
assert await test.execute()
|
||||
|
||||
coresys.core.state = CoreState.FREEZE
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.RUNNING]
|
||||
assert await test.execute()
|
||||
|
||||
@ -258,7 +259,7 @@ async def test_exception_conditions(coresys: CoreSys):
|
||||
await test.execute()
|
||||
|
||||
|
||||
async def test_exectution_limit_single_wait(
|
||||
async def test_execution_limit_single_wait(
|
||||
coresys: CoreSys, loop: asyncio.BaseEventLoop
|
||||
):
|
||||
"""Test the single wait job execution limit."""
|
||||
@ -355,7 +356,7 @@ async def test_execution_limit_throttle_rate_limit(
|
||||
assert test.call == 3
|
||||
|
||||
|
||||
async def test_exectution_limit_throttle(coresys: CoreSys, loop: asyncio.BaseEventLoop):
|
||||
async def test_execution_limit_throttle(coresys: CoreSys, loop: asyncio.BaseEventLoop):
|
||||
"""Test the ignore conditions decorator."""
|
||||
|
||||
class TestClass:
|
||||
@ -384,7 +385,7 @@ async def test_exectution_limit_throttle(coresys: CoreSys, loop: asyncio.BaseEve
|
||||
assert test.call == 1
|
||||
|
||||
|
||||
async def test_exectution_limit_once(coresys: CoreSys, loop: asyncio.BaseEventLoop):
|
||||
async def test_execution_limit_once(coresys: CoreSys, loop: asyncio.BaseEventLoop):
|
||||
"""Test the ignore conditions decorator."""
|
||||
|
||||
class TestClass:
|
||||
@ -436,6 +437,9 @@ async def test_supervisor_updated(coresys: CoreSys):
|
||||
):
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.SUPERVISOR_UPDATED]
|
||||
assert await test.execute()
|
||||
|
||||
|
||||
async def test_plugins_updated(coresys: CoreSys):
|
||||
"""Test the plugins updated decorator."""
|
||||
@ -459,13 +463,16 @@ async def test_plugins_updated(coresys: CoreSys):
|
||||
assert await test.execute()
|
||||
|
||||
with patch.object(
|
||||
type(coresys.plugins.audio), "need_update", new=PropertyMock(return_value=True)
|
||||
PluginAudio, "need_update", new=PropertyMock(return_value=True)
|
||||
), patch.object(
|
||||
type(coresys.plugins.audio), "update", side_effect=[AudioUpdateError, None]
|
||||
PluginAudio, "update", side_effect=[AudioUpdateError, None, AudioUpdateError]
|
||||
):
|
||||
assert not await test.execute()
|
||||
assert await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.PLUGINS_UPDATED]
|
||||
assert await test.execute()
|
||||
|
||||
|
||||
async def test_auto_update(coresys: CoreSys):
|
||||
"""Test the auto update decorator."""
|
||||
@ -489,9 +496,12 @@ async def test_auto_update(coresys: CoreSys):
|
||||
coresys.updater.auto_update = False
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.AUTO_UPDATE]
|
||||
assert await test.execute()
|
||||
|
||||
async def test_unhealthy(coresys: CoreSys, caplog: pytest.LogCaptureFixture):
|
||||
"""Test the healthy decorator when unhealthy."""
|
||||
|
||||
async def test_os_agent(coresys: CoreSys):
|
||||
"""Test the os agent decorator."""
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
@ -500,23 +510,27 @@ async def test_unhealthy(coresys: CoreSys, caplog: pytest.LogCaptureFixture):
|
||||
"""Initialize the test class."""
|
||||
self.coresys = coresys
|
||||
|
||||
@Job(conditions=[JobCondition.HEALTHY])
|
||||
@Job(conditions=[JobCondition.OS_AGENT])
|
||||
async def execute(self) -> bool:
|
||||
"""Execute the class method."""
|
||||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
coresys.resolution.unhealthy = UnhealthyReason.SETUP
|
||||
assert not await test.execute()
|
||||
assert "blocked from execution, system is not healthy - setup" in caplog.text
|
||||
with patch.object(
|
||||
HostManager, "supported_features", return_value=[HostFeature.OS_AGENT]
|
||||
):
|
||||
assert await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.HEALTHY]
|
||||
assert await test.execute()
|
||||
coresys.host.supported_features.cache_clear()
|
||||
with patch.object(HostManager, "supported_features", return_value=[]):
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.OS_AGENT]
|
||||
assert await test.execute()
|
||||
|
||||
|
||||
async def test_unhandled_exception(coresys: CoreSys, capture_exception: Mock):
|
||||
"""Test an unhandled exception from job."""
|
||||
err = OSError()
|
||||
async def test_host_network(coresys: CoreSys):
|
||||
"""Test the host network decorator."""
|
||||
|
||||
class TestClass:
|
||||
"""Test class."""
|
||||
@ -525,13 +539,16 @@ async def test_unhandled_exception(coresys: CoreSys, capture_exception: Mock):
|
||||
"""Initialize the test class."""
|
||||
self.coresys = coresys
|
||||
|
||||
@Job(conditions=[JobCondition.HEALTHY])
|
||||
async def execute(self) -> None:
|
||||
@Job(conditions=[JobCondition.HOST_NETWORK])
|
||||
async def execute(self) -> bool:
|
||||
"""Execute the class method."""
|
||||
raise err
|
||||
return True
|
||||
|
||||
test = TestClass(coresys)
|
||||
with pytest.raises(JobException):
|
||||
await test.execute()
|
||||
assert await test.execute()
|
||||
|
||||
capture_exception.assert_called_once_with(err)
|
||||
coresys.dbus.network.disconnect()
|
||||
assert not await test.execute()
|
||||
|
||||
coresys.jobs.ignore_conditions = [JobCondition.HOST_NETWORK]
|
||||
assert await test.execute()
|
||||
|
Loading…
x
Reference in New Issue
Block a user