mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-18 22:56:31 +00:00
Finish migrating read_text to executor (#5698)
* Move read_text to executor * switch to async_capture_exception * Finish moving read_text to executor * Cover read_bytes and some write_text calls as well * Fix await issues * Fix format_message
This commit is contained in:
parent
c01d788c4c
commit
582b128ad9
@ -140,9 +140,7 @@ class Addon(AddonModel):
|
|||||||
super().__init__(coresys, slug)
|
super().__init__(coresys, slug)
|
||||||
self.instance: DockerAddon = DockerAddon(coresys, self)
|
self.instance: DockerAddon = DockerAddon(coresys, self)
|
||||||
self._state: AddonState = AddonState.UNKNOWN
|
self._state: AddonState = AddonState.UNKNOWN
|
||||||
self._manual_stop: bool = (
|
self._manual_stop: bool = False
|
||||||
self.sys_hardware.helper.last_boot != self.sys_config.last_boot
|
|
||||||
)
|
|
||||||
self._listeners: list[EventListener] = []
|
self._listeners: list[EventListener] = []
|
||||||
self._startup_event = asyncio.Event()
|
self._startup_event = asyncio.Event()
|
||||||
self._startup_task: asyncio.Task | None = None
|
self._startup_task: asyncio.Task | None = None
|
||||||
@ -216,6 +214,10 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Async initialize of object."""
|
"""Async initialize of object."""
|
||||||
|
self._manual_stop = (
|
||||||
|
await self.sys_hardware.helper.last_boot() != self.sys_config.last_boot
|
||||||
|
)
|
||||||
|
|
||||||
if self.is_detached:
|
if self.is_detached:
|
||||||
await super().refresh_path_cache()
|
await super().refresh_path_cache()
|
||||||
|
|
||||||
@ -720,7 +722,7 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
options = self.schema.validate(self.options)
|
options = self.schema.validate(self.options)
|
||||||
write_json_file(self.path_options, options)
|
await self.sys_run_in_executor(write_json_file, self.path_options, options)
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Add-on %s has invalid options: %s",
|
"Add-on %s has invalid options: %s",
|
||||||
@ -938,19 +940,20 @@ class Addon(AddonModel):
|
|||||||
)
|
)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def write_pulse(self) -> None:
|
async def write_pulse(self) -> None:
|
||||||
"""Write asound config to file and return True on success."""
|
"""Write asound config to file and return True on success."""
|
||||||
pulse_config = self.sys_plugins.audio.pulse_client(
|
pulse_config = self.sys_plugins.audio.pulse_client(
|
||||||
input_profile=self.audio_input, output_profile=self.audio_output
|
input_profile=self.audio_input, output_profile=self.audio_output
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cleanup wrong maps
|
def write_pulse_config():
|
||||||
if self.path_pulse.is_dir():
|
# Cleanup wrong maps
|
||||||
shutil.rmtree(self.path_pulse, ignore_errors=True)
|
if self.path_pulse.is_dir():
|
||||||
|
shutil.rmtree(self.path_pulse, ignore_errors=True)
|
||||||
# Write pulse config
|
|
||||||
try:
|
|
||||||
self.path_pulse.write_text(pulse_config, encoding="utf-8")
|
self.path_pulse.write_text(pulse_config, encoding="utf-8")
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.sys_run_in_executor(write_pulse_config)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EBADMSG:
|
if err.errno == errno.EBADMSG:
|
||||||
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
||||||
@ -1070,7 +1073,7 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
# Sound
|
# Sound
|
||||||
if self.with_audio:
|
if self.with_audio:
|
||||||
self.write_pulse()
|
await self.write_pulse()
|
||||||
|
|
||||||
def _check_addon_config_dir():
|
def _check_addon_config_dir():
|
||||||
if self.path_config.is_dir():
|
if self.path_config.is_dir():
|
||||||
|
@ -50,7 +50,7 @@ class CpuArch(CoreSysAttributes):
|
|||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Load data and initialize default arch."""
|
"""Load data and initialize default arch."""
|
||||||
try:
|
try:
|
||||||
arch_data = read_json_file(ARCH_JSON)
|
arch_data = await self.sys_run_in_executor(read_json_file, ARCH_JSON)
|
||||||
except ConfigurationFileError:
|
except ConfigurationFileError:
|
||||||
_LOGGER.warning("Can't read arch json file from %s", ARCH_JSON)
|
_LOGGER.warning("Can't read arch json file from %s", ARCH_JSON)
|
||||||
return
|
return
|
||||||
|
@ -223,7 +223,7 @@ class Core(CoreSysAttributes):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# HomeAssistant is already running, only Supervisor restarted
|
# HomeAssistant is already running, only Supervisor restarted
|
||||||
if self.sys_hardware.helper.last_boot == self.sys_config.last_boot:
|
if await self.sys_hardware.helper.last_boot() == self.sys_config.last_boot:
|
||||||
_LOGGER.info("Detected Supervisor restart")
|
_LOGGER.info("Detected Supervisor restart")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -362,7 +362,7 @@ class Core(CoreSysAttributes):
|
|||||||
|
|
||||||
async def _update_last_boot(self):
|
async def _update_last_boot(self):
|
||||||
"""Update last boot time."""
|
"""Update last boot time."""
|
||||||
self.sys_config.last_boot = self.sys_hardware.helper.last_boot
|
self.sys_config.last_boot = await self.sys_hardware.helper.last_boot()
|
||||||
await self.sys_config.save_data()
|
await self.sys_config.save_data()
|
||||||
|
|
||||||
async def _retrieve_whoami(self, with_ssl: bool) -> WhoamiData | None:
|
async def _retrieve_whoami(self, with_ssl: bool) -> WhoamiData | None:
|
||||||
|
@ -25,6 +25,7 @@ class HwHelper(CoreSysAttributes):
|
|||||||
def __init__(self, coresys: CoreSys):
|
def __init__(self, coresys: CoreSys):
|
||||||
"""Init hardware object."""
|
"""Init hardware object."""
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
|
self._last_boot: datetime | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def support_audio(self) -> bool:
|
def support_audio(self) -> bool:
|
||||||
@ -41,11 +42,15 @@ class HwHelper(CoreSysAttributes):
|
|||||||
"""Return True if the device have USB ports."""
|
"""Return True if the device have USB ports."""
|
||||||
return bool(self.sys_hardware.filter_devices(subsystem=UdevSubsystem.USB))
|
return bool(self.sys_hardware.filter_devices(subsystem=UdevSubsystem.USB))
|
||||||
|
|
||||||
@property
|
async def last_boot(self) -> datetime | None:
|
||||||
def last_boot(self) -> datetime | None:
|
|
||||||
"""Return last boot time."""
|
"""Return last boot time."""
|
||||||
|
if self._last_boot:
|
||||||
|
return self._last_boot
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stats: str = _PROC_STAT.read_text(encoding="utf-8")
|
stats: str = await self.sys_run_in_executor(
|
||||||
|
_PROC_STAT.read_text, encoding="utf-8"
|
||||||
|
)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
_LOGGER.error("Can't read stat data: %s", err)
|
_LOGGER.error("Can't read stat data: %s", err)
|
||||||
return None
|
return None
|
||||||
@ -56,7 +61,8 @@ class HwHelper(CoreSysAttributes):
|
|||||||
_LOGGER.error("Can't found last boot time!")
|
_LOGGER.error("Can't found last boot time!")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return datetime.fromtimestamp(int(found.group(1)), UTC)
|
self._last_boot = datetime.fromtimestamp(int(found.group(1)), UTC)
|
||||||
|
return self._last_boot
|
||||||
|
|
||||||
def hide_virtual_device(self, udev_device: pyudev.Device) -> bool:
|
def hide_virtual_device(self, udev_device: pyudev.Device) -> bool:
|
||||||
"""Small helper to hide not needed Devices."""
|
"""Small helper to hide not needed Devices."""
|
||||||
|
@ -342,7 +342,7 @@ class HomeAssistantCore(JobGroup):
|
|||||||
await self.sys_homeassistant.save_data()
|
await self.sys_homeassistant.save_data()
|
||||||
|
|
||||||
# Write audio settings
|
# Write audio settings
|
||||||
self.sys_homeassistant.write_pulse()
|
await self.sys_homeassistant.write_pulse()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.instance.run(restore_job_id=self.sys_backups.current_restore)
|
await self.instance.run(restore_job_id=self.sys_backups.current_restore)
|
||||||
|
@ -313,19 +313,20 @@ class HomeAssistant(FileConfiguration, CoreSysAttributes):
|
|||||||
BusEvent.HARDWARE_REMOVE_DEVICE, self._hardware_events
|
BusEvent.HARDWARE_REMOVE_DEVICE, self._hardware_events
|
||||||
)
|
)
|
||||||
|
|
||||||
def write_pulse(self):
|
async def write_pulse(self):
|
||||||
"""Write asound config to file and return True on success."""
|
"""Write asound config to file and return True on success."""
|
||||||
pulse_config = self.sys_plugins.audio.pulse_client(
|
pulse_config = self.sys_plugins.audio.pulse_client(
|
||||||
input_profile=self.audio_input, output_profile=self.audio_output
|
input_profile=self.audio_input, output_profile=self.audio_output
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cleanup wrong maps
|
def write_pulse_config():
|
||||||
if self.path_pulse.is_dir():
|
# Cleanup wrong maps
|
||||||
shutil.rmtree(self.path_pulse, ignore_errors=True)
|
if self.path_pulse.is_dir():
|
||||||
|
shutil.rmtree(self.path_pulse, ignore_errors=True)
|
||||||
# Write pulse config
|
|
||||||
try:
|
|
||||||
self.path_pulse.write_text(pulse_config, encoding="utf-8")
|
self.path_pulse.write_text(pulse_config, encoding="utf-8")
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.sys_run_in_executor(write_pulse_config)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EBADMSG:
|
if err.errno == errno.EBADMSG:
|
||||||
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
||||||
|
@ -72,7 +72,9 @@ class LogsControl(CoreSysAttributes):
|
|||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Load log control."""
|
"""Load log control."""
|
||||||
try:
|
try:
|
||||||
self._default_identifiers = read_json_file(SYSLOG_IDENTIFIERS_JSON)
|
self._default_identifiers = await self.sys_run_in_executor(
|
||||||
|
read_json_file, SYSLOG_IDENTIFIERS_JSON
|
||||||
|
)
|
||||||
except ConfigurationFileError:
|
except ConfigurationFileError:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Can't read syslog identifiers json file from %s",
|
"Can't read syslog identifiers json file from %s",
|
||||||
|
@ -88,7 +88,9 @@ class PluginAudio(PluginBase):
|
|||||||
# Initialize Client Template
|
# Initialize Client Template
|
||||||
try:
|
try:
|
||||||
self.client_template = jinja2.Template(
|
self.client_template = jinja2.Template(
|
||||||
PULSE_CLIENT_TMPL.read_text(encoding="utf-8")
|
await self.sys_run_in_executor(
|
||||||
|
PULSE_CLIENT_TMPL.read_text, encoding="utf-8"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EBADMSG:
|
if err.errno == errno.EBADMSG:
|
||||||
@ -100,13 +102,17 @@ class PluginAudio(PluginBase):
|
|||||||
|
|
||||||
# Setup default asound config
|
# Setup default asound config
|
||||||
asound = self.sys_config.path_audio.joinpath("asound")
|
asound = self.sys_config.path_audio.joinpath("asound")
|
||||||
if not asound.exists():
|
|
||||||
try:
|
def setup_default_asound():
|
||||||
|
if not asound.exists():
|
||||||
shutil.copy(ASOUND_TMPL, asound)
|
shutil.copy(ASOUND_TMPL, asound)
|
||||||
except OSError as err:
|
|
||||||
if err.errno == errno.EBADMSG:
|
try:
|
||||||
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
await self.sys_run_in_executor(setup_default_asound)
|
||||||
_LOGGER.error("Can't create default asound: %s", err)
|
except OSError as err:
|
||||||
|
if err.errno == errno.EBADMSG:
|
||||||
|
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
||||||
|
_LOGGER.error("Can't create default asound: %s", err)
|
||||||
|
|
||||||
@Job(
|
@Job(
|
||||||
name="plugin_audio_update",
|
name="plugin_audio_update",
|
||||||
@ -123,7 +129,7 @@ class PluginAudio(PluginBase):
|
|||||||
async def restart(self) -> None:
|
async def restart(self) -> None:
|
||||||
"""Restart Audio plugin."""
|
"""Restart Audio plugin."""
|
||||||
_LOGGER.info("Restarting Audio plugin")
|
_LOGGER.info("Restarting Audio plugin")
|
||||||
self._write_config()
|
await self._write_config()
|
||||||
try:
|
try:
|
||||||
await self.instance.restart()
|
await self.instance.restart()
|
||||||
except DockerError as err:
|
except DockerError as err:
|
||||||
@ -132,7 +138,7 @@ class PluginAudio(PluginBase):
|
|||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
"""Run Audio plugin."""
|
"""Run Audio plugin."""
|
||||||
_LOGGER.info("Starting Audio plugin")
|
_LOGGER.info("Starting Audio plugin")
|
||||||
self._write_config()
|
await self._write_config()
|
||||||
try:
|
try:
|
||||||
await self.instance.run()
|
await self.instance.run()
|
||||||
except DockerError as err:
|
except DockerError as err:
|
||||||
@ -177,10 +183,11 @@ class PluginAudio(PluginBase):
|
|||||||
default_sink=output_profile,
|
default_sink=output_profile,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _write_config(self):
|
async def _write_config(self):
|
||||||
"""Write pulse audio config."""
|
"""Write pulse audio config."""
|
||||||
try:
|
try:
|
||||||
write_json_file(
|
await self.sys_run_in_executor(
|
||||||
|
write_json_file,
|
||||||
self.pulse_audio_config,
|
self.pulse_audio_config,
|
||||||
{
|
{
|
||||||
"debug": self.sys_config.logging == LogLevel.DEBUG,
|
"debug": self.sys_config.logging == LogLevel.DEBUG,
|
||||||
|
@ -152,15 +152,16 @@ class PluginDns(PluginBase):
|
|||||||
# Initialize CoreDNS Template
|
# Initialize CoreDNS Template
|
||||||
try:
|
try:
|
||||||
self.resolv_template = jinja2.Template(
|
self.resolv_template = jinja2.Template(
|
||||||
RESOLV_TMPL.read_text(encoding="utf-8")
|
await self.sys_run_in_executor(RESOLV_TMPL.read_text, encoding="utf-8")
|
||||||
)
|
)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EBADMSG:
|
if err.errno == errno.EBADMSG:
|
||||||
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
||||||
_LOGGER.error("Can't read resolve.tmpl: %s", err)
|
_LOGGER.error("Can't read resolve.tmpl: %s", err)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.hosts_template = jinja2.Template(
|
self.hosts_template = jinja2.Template(
|
||||||
HOSTS_TMPL.read_text(encoding="utf-8")
|
await self.sys_run_in_executor(HOSTS_TMPL.read_text, encoding="utf-8")
|
||||||
)
|
)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EBADMSG:
|
if err.errno == errno.EBADMSG:
|
||||||
@ -171,7 +172,7 @@ class PluginDns(PluginBase):
|
|||||||
await super().load()
|
await super().load()
|
||||||
|
|
||||||
# Update supervisor
|
# Update supervisor
|
||||||
self._write_resolv(HOST_RESOLV)
|
await self._write_resolv(HOST_RESOLV)
|
||||||
await self.sys_supervisor.check_connectivity()
|
await self.sys_supervisor.check_connectivity()
|
||||||
|
|
||||||
async def install(self) -> None:
|
async def install(self) -> None:
|
||||||
@ -195,7 +196,7 @@ class PluginDns(PluginBase):
|
|||||||
|
|
||||||
async def restart(self) -> None:
|
async def restart(self) -> None:
|
||||||
"""Restart CoreDNS plugin."""
|
"""Restart CoreDNS plugin."""
|
||||||
self._write_config()
|
await self._write_config()
|
||||||
_LOGGER.info("Restarting CoreDNS plugin")
|
_LOGGER.info("Restarting CoreDNS plugin")
|
||||||
try:
|
try:
|
||||||
await self.instance.restart()
|
await self.instance.restart()
|
||||||
@ -204,7 +205,7 @@ class PluginDns(PluginBase):
|
|||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
"""Run CoreDNS."""
|
"""Run CoreDNS."""
|
||||||
self._write_config()
|
await self._write_config()
|
||||||
|
|
||||||
# Start Instance
|
# Start Instance
|
||||||
_LOGGER.info("Starting CoreDNS plugin")
|
_LOGGER.info("Starting CoreDNS plugin")
|
||||||
@ -273,7 +274,7 @@ class PluginDns(PluginBase):
|
|||||||
else:
|
else:
|
||||||
self._loop = False
|
self._loop = False
|
||||||
|
|
||||||
def _write_config(self) -> None:
|
async def _write_config(self) -> None:
|
||||||
"""Write CoreDNS config."""
|
"""Write CoreDNS config."""
|
||||||
debug: bool = self.sys_config.logging == LogLevel.DEBUG
|
debug: bool = self.sys_config.logging == LogLevel.DEBUG
|
||||||
dns_servers: list[str] = []
|
dns_servers: list[str] = []
|
||||||
@ -297,7 +298,8 @@ class PluginDns(PluginBase):
|
|||||||
|
|
||||||
# Write config to plugin
|
# Write config to plugin
|
||||||
try:
|
try:
|
||||||
write_json_file(
|
await self.sys_run_in_executor(
|
||||||
|
write_json_file,
|
||||||
self.coredns_config,
|
self.coredns_config,
|
||||||
{
|
{
|
||||||
"servers": dns_servers,
|
"servers": dns_servers,
|
||||||
@ -412,7 +414,7 @@ class PluginDns(PluginBase):
|
|||||||
_LOGGER.error("Repair of CoreDNS failed")
|
_LOGGER.error("Repair of CoreDNS failed")
|
||||||
await async_capture_exception(err)
|
await async_capture_exception(err)
|
||||||
|
|
||||||
def _write_resolv(self, resolv_conf: Path) -> None:
|
async def _write_resolv(self, resolv_conf: Path) -> None:
|
||||||
"""Update/Write resolv.conf file."""
|
"""Update/Write resolv.conf file."""
|
||||||
if not self.resolv_template:
|
if not self.resolv_template:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
@ -427,7 +429,7 @@ class PluginDns(PluginBase):
|
|||||||
|
|
||||||
# Write config back to resolv
|
# Write config back to resolv
|
||||||
try:
|
try:
|
||||||
resolv_conf.write_text(data)
|
await self.sys_run_in_executor(resolv_conf.write_text, data)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno == errno.EBADMSG:
|
if err.errno == errno.EBADMSG:
|
||||||
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
|
||||||
|
@ -36,6 +36,9 @@ class EvaluateAppArmor(EvaluateBase):
|
|||||||
async def evaluate(self) -> None:
|
async def evaluate(self) -> None:
|
||||||
"""Run evaluation."""
|
"""Run evaluation."""
|
||||||
try:
|
try:
|
||||||
return _APPARMOR_KERNEL.read_text(encoding="utf-8").strip().upper() != "Y"
|
apparmor = await self.sys_run_in_executor(
|
||||||
|
_APPARMOR_KERNEL.read_text, encoding="utf-8"
|
||||||
|
)
|
||||||
except OSError:
|
except OSError:
|
||||||
return True
|
return True
|
||||||
|
return apparmor.strip().upper() != "Y"
|
||||||
|
@ -34,7 +34,13 @@ class EvaluateLxc(EvaluateBase):
|
|||||||
|
|
||||||
async def evaluate(self):
|
async def evaluate(self):
|
||||||
"""Run evaluation."""
|
"""Run evaluation."""
|
||||||
with suppress(OSError):
|
|
||||||
if "container=lxc" in Path("/proc/1/environ").read_text(encoding="utf-8"):
|
def check_lxc():
|
||||||
return True
|
with suppress(OSError):
|
||||||
return Path("/dev/lxd/sock").exists()
|
if "container=lxc" in Path("/proc/1/environ").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return Path("/dev/lxd/sock").exists()
|
||||||
|
|
||||||
|
return await self.sys_run_in_executor(check_lxc)
|
||||||
|
@ -48,7 +48,10 @@ json_loads = orjson.loads # pylint: disable=no-member
|
|||||||
|
|
||||||
|
|
||||||
def write_json_file(jsonfile: Path, data: Any) -> None:
|
def write_json_file(jsonfile: Path, data: Any) -> None:
|
||||||
"""Write a JSON file."""
|
"""Write a JSON file.
|
||||||
|
|
||||||
|
Must be run in executor.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
with atomic_write(jsonfile, overwrite=True) as fp:
|
with atomic_write(jsonfile, overwrite=True) as fp:
|
||||||
fp.write(
|
fp.write(
|
||||||
@ -67,7 +70,10 @@ def write_json_file(jsonfile: Path, data: Any) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def read_json_file(jsonfile: Path) -> Any:
|
def read_json_file(jsonfile: Path) -> Any:
|
||||||
"""Read a JSON file and return a dict."""
|
"""Read a JSON file and return a dict.
|
||||||
|
|
||||||
|
Must be run in executor.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return json_loads(jsonfile.read_bytes())
|
return json_loads(jsonfile.read_bytes())
|
||||||
except (OSError, ValueError, TypeError, UnicodeDecodeError) as err:
|
except (OSError, ValueError, TypeError, UnicodeDecodeError) as err:
|
||||||
|
@ -20,6 +20,7 @@ from supervisor.docker.addon import DockerAddon
|
|||||||
from supervisor.docker.const import ContainerState
|
from supervisor.docker.const import ContainerState
|
||||||
from supervisor.docker.monitor import DockerContainerStateEvent
|
from supervisor.docker.monitor import DockerContainerStateEvent
|
||||||
from supervisor.exceptions import AddonsError, AddonsJobError, AudioUpdateError
|
from supervisor.exceptions import AddonsError, AddonsJobError, AudioUpdateError
|
||||||
|
from supervisor.hardware.helper import HwHelper
|
||||||
from supervisor.ingress import Ingress
|
from supervisor.ingress import Ingress
|
||||||
from supervisor.store.repository import Repository
|
from supervisor.store.repository import Repository
|
||||||
from supervisor.utils.dt import utcnow
|
from supervisor.utils.dt import utcnow
|
||||||
@ -250,11 +251,7 @@ async def test_watchdog_during_attach(
|
|||||||
|
|
||||||
with (
|
with (
|
||||||
patch.object(Addon, "restart") as restart,
|
patch.object(Addon, "restart") as restart,
|
||||||
patch.object(
|
patch.object(HwHelper, "last_boot", return_value=utcnow()),
|
||||||
type(coresys.hardware.helper),
|
|
||||||
"last_boot",
|
|
||||||
new=PropertyMock(return_value=utcnow()),
|
|
||||||
),
|
|
||||||
patch.object(DockerAddon, "attach"),
|
patch.object(DockerAddon, "attach"),
|
||||||
patch.object(
|
patch.object(
|
||||||
DockerAddon,
|
DockerAddon,
|
||||||
@ -262,7 +259,9 @@ async def test_watchdog_during_attach(
|
|||||||
return_value=ContainerState.STOPPED,
|
return_value=ContainerState.STOPPED,
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
coresys.config.last_boot = coresys.hardware.helper.last_boot + boot_timedelta
|
coresys.config.last_boot = (
|
||||||
|
await coresys.hardware.helper.last_boot() + boot_timedelta
|
||||||
|
)
|
||||||
addon = Addon(coresys, store.slug)
|
addon = Addon(coresys, store.slug)
|
||||||
coresys.addons.local[addon.slug] = addon
|
coresys.addons.local[addon.slug] = addon
|
||||||
addon.watchdog = True
|
addon.watchdog = True
|
||||||
@ -739,7 +738,7 @@ async def test_local_example_ingress_port_set(
|
|||||||
assert install_addon_example.ingress_port != 0
|
assert install_addon_example.ingress_port != 0
|
||||||
|
|
||||||
|
|
||||||
def test_addon_pulse_error(
|
async def test_addon_pulse_error(
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
install_addon_example: Addon,
|
install_addon_example: Addon,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
@ -750,14 +749,14 @@ def test_addon_pulse_error(
|
|||||||
"supervisor.addons.addon.Path.write_text", side_effect=(err := OSError())
|
"supervisor.addons.addon.Path.write_text", side_effect=(err := OSError())
|
||||||
):
|
):
|
||||||
err.errno = errno.EBUSY
|
err.errno = errno.EBUSY
|
||||||
install_addon_example.write_pulse()
|
await install_addon_example.write_pulse()
|
||||||
|
|
||||||
assert "can't write pulse/client.config" in caplog.text
|
assert "can't write pulse/client.config" in caplog.text
|
||||||
assert coresys.core.healthy is True
|
assert coresys.core.healthy is True
|
||||||
|
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
err.errno = errno.EBADMSG
|
err.errno = errno.EBADMSG
|
||||||
install_addon_example.write_pulse()
|
await install_addon_example.write_pulse()
|
||||||
|
|
||||||
assert "can't write pulse/client.config" in caplog.text
|
assert "can't write pulse/client.config" in caplog.text
|
||||||
assert coresys.core.healthy is False
|
assert coresys.core.healthy is False
|
||||||
|
@ -87,13 +87,13 @@ def test_hide_virtual_device(coresys: CoreSys):
|
|||||||
assert coresys.hardware.helper.hide_virtual_device(udev_device)
|
assert coresys.hardware.helper.hide_virtual_device(udev_device)
|
||||||
|
|
||||||
|
|
||||||
def test_last_boot_error(coresys: CoreSys, caplog: LogCaptureFixture):
|
async def test_last_boot_error(coresys: CoreSys, caplog: LogCaptureFixture):
|
||||||
"""Test error reading last boot."""
|
"""Test error reading last boot."""
|
||||||
with patch(
|
with patch(
|
||||||
"supervisor.hardware.helper.Path.read_text", side_effect=(err := OSError())
|
"supervisor.hardware.helper.Path.read_text", side_effect=(err := OSError())
|
||||||
):
|
):
|
||||||
err.errno = errno.EBADMSG
|
err.errno = errno.EBADMSG
|
||||||
assert coresys.hardware.helper.last_boot is None
|
assert await coresys.hardware.helper.last_boot() is None
|
||||||
|
|
||||||
assert coresys.core.healthy is True
|
assert coresys.core.healthy is True
|
||||||
assert "Can't read stat data" in caplog.text
|
assert "Can't read stat data" in caplog.text
|
||||||
|
@ -66,21 +66,21 @@ async def test_get_users_none(coresys: CoreSys, ha_ws_client: AsyncMock):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_write_pulse_error(coresys: CoreSys, caplog: pytest.LogCaptureFixture):
|
async def test_write_pulse_error(coresys: CoreSys, caplog: pytest.LogCaptureFixture):
|
||||||
"""Test errors writing pulse config."""
|
"""Test errors writing pulse config."""
|
||||||
with patch(
|
with patch(
|
||||||
"supervisor.homeassistant.module.Path.write_text",
|
"supervisor.homeassistant.module.Path.write_text",
|
||||||
side_effect=(err := OSError()),
|
side_effect=(err := OSError()),
|
||||||
):
|
):
|
||||||
err.errno = errno.EBUSY
|
err.errno = errno.EBUSY
|
||||||
coresys.homeassistant.write_pulse()
|
await coresys.homeassistant.write_pulse()
|
||||||
|
|
||||||
assert "can't write pulse/client.config" in caplog.text
|
assert "can't write pulse/client.config" in caplog.text
|
||||||
assert coresys.core.healthy is True
|
assert coresys.core.healthy is True
|
||||||
|
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
err.errno = errno.EBADMSG
|
err.errno = errno.EBADMSG
|
||||||
coresys.homeassistant.write_pulse()
|
await coresys.homeassistant.write_pulse()
|
||||||
|
|
||||||
assert "can't write pulse/client.config" in caplog.text
|
assert "can't write pulse/client.config" in caplog.text
|
||||||
assert coresys.core.healthy is False
|
assert coresys.core.healthy is False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user