Temporary directory to executor (#5673)

* Move temporary directory usage to executor

* Use temp_folder.name in Path constructor
This commit is contained in:
Mike Degatano 2025-02-27 11:58:55 -05:00 committed by GitHub
parent c5d4ebcd48
commit 151d4bdd73
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 81 additions and 42 deletions

View File

@ -977,11 +977,21 @@ class Addon(AddonModel):
return return
# Need install/update # Need install/update
with TemporaryDirectory(dir=self.sys_config.path_tmp) as tmp_folder: tmp_folder: TemporaryDirectory | None = None
profile_file = Path(tmp_folder, "apparmor.txt")
def install_update_profile() -> Path:
nonlocal tmp_folder
tmp_folder = TemporaryDirectory(dir=self.sys_config.path_tmp)
profile_file = Path(tmp_folder.name, "apparmor.txt")
adjust_profile(self.slug, self.path_apparmor, profile_file) adjust_profile(self.slug, self.path_apparmor, profile_file)
return profile_file
try:
profile_file = await self.sys_run_in_executor(install_update_profile)
await self.sys_host.apparmor.load_profile(self.slug, profile_file) await self.sys_host.apparmor.load_profile(self.slug, profile_file)
finally:
if tmp_folder:
await self.sys_run_in_executor(tmp_folder.cleanup)
async def uninstall_apparmor(self) -> None: async def uninstall_apparmor(self) -> None:
"""Remove AppArmor profile for Add-on.""" """Remove AppArmor profile for Add-on."""

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable from collections.abc import Callable
import errno import errno
from io import IOBase
import logging import logging
from pathlib import Path from pathlib import Path
import re import re
@ -518,29 +519,28 @@ class APIBackups(CoreSysAttributes):
except vol.Invalid as ex: except vol.Invalid as ex:
raise APIError(humanize_error(filename, ex)) from None raise APIError(humanize_error(filename, ex)) from None
with TemporaryDirectory(dir=tmp_path.as_posix()) as temp_dir: temp_dir: TemporaryDirectory | None = None
tar_file = Path(temp_dir, "backup.tar") backup_file_stream: IOBase | None = None
def open_backup_file() -> Path:
nonlocal temp_dir, backup_file_stream
temp_dir = TemporaryDirectory(dir=tmp_path.as_posix())
tar_file = Path(temp_dir.name, "backup.tar")
backup_file_stream = tar_file.open("wb")
return tar_file
def close_backup_file() -> None:
if backup_file_stream:
backup_file_stream.close()
if temp_dir:
temp_dir.cleanup()
try:
reader = await request.multipart() reader = await request.multipart()
contents = await reader.next() contents = await reader.next()
try: tar_file = await self.sys_run_in_executor(open_backup_file)
with tar_file.open("wb") as backup: while chunk := await contents.read_chunk(size=2**16):
while True: await self.sys_run_in_executor(backup_file_stream.write, chunk)
chunk = await contents.read_chunk()
if not chunk:
break
backup.write(chunk)
except OSError as err:
if err.errno == errno.EBADMSG and location in {
LOCATION_CLOUD_BACKUP,
None,
}:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
_LOGGER.error("Can't write new backup file: %s", err)
return False
except asyncio.CancelledError:
return False
backup = await asyncio.shield( backup = await asyncio.shield(
self.sys_backups.import_backup( self.sys_backups.import_backup(
@ -550,6 +550,21 @@ class APIBackups(CoreSysAttributes):
additional_locations=locations, additional_locations=locations,
) )
) )
except OSError as err:
if err.errno == errno.EBADMSG and location in {
LOCATION_CLOUD_BACKUP,
None,
}:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
_LOGGER.error("Can't write new backup file: %s", err)
return False
except asyncio.CancelledError:
return False
finally:
if temp_dir or backup:
await self.sys_run_in_executor(close_backup_file)
if backup: if backup:
return {ATTR_SLUG: backup.slug} return {ATTR_SLUG: backup.slug}

View File

@ -158,25 +158,35 @@ class Supervisor(CoreSysAttributes):
) from err ) from err
# Load # Load
with TemporaryDirectory(dir=self.sys_config.path_tmp) as tmp_dir: temp_dir: TemporaryDirectory | None = None
profile_file = Path(tmp_dir, "apparmor.txt")
try:
profile_file.write_text(data, encoding="utf-8")
except OSError as err:
if err.errno == errno.EBADMSG:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
raise SupervisorAppArmorError(
f"Can't write temporary profile: {err!s}", _LOGGER.error
) from err
try: def write_profile() -> Path:
await self.sys_host.apparmor.load_profile( nonlocal temp_dir
"hassio-supervisor", profile_file temp_dir = TemporaryDirectory(dir=self.sys_config.path_tmp)
) profile_file = Path(temp_dir.name, "apparmor.txt")
except HostAppArmorError as err: profile_file.write_text(data, encoding="utf-8")
raise SupervisorAppArmorError( return profile_file
"Can't update AppArmor profile!", _LOGGER.error
) from err try:
profile_file = await self.sys_run_in_executor(write_profile)
await self.sys_host.apparmor.load_profile("hassio-supervisor", profile_file)
except OSError as err:
if err.errno == errno.EBADMSG:
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
raise SupervisorAppArmorError(
f"Can't write temporary profile: {err!s}", _LOGGER.error
) from err
except HostAppArmorError as err:
raise SupervisorAppArmorError(
"Can't update AppArmor profile!", _LOGGER.error
) from err
finally:
if temp_dir:
await self.sys_run_in_executor(temp_dir.cleanup)
async def update(self, version: AwesomeVersion | None = None) -> None: async def update(self, version: AwesomeVersion | None = None) -> None:
"""Update Supervisor version.""" """Update Supervisor version."""

View File

@ -90,6 +90,7 @@ def remove_folder(
Is needed to avoid issue with: Is needed to avoid issue with:
- CAP_DAC_OVERRIDE - CAP_DAC_OVERRIDE
- CAP_DAC_READ_SEARCH - CAP_DAC_READ_SEARCH
Must be run in executor.
""" """
find_args = [] find_args = []
if content_only: if content_only:
@ -114,7 +115,10 @@ def remove_folder_with_excludes(
excludes: list[str], excludes: list[str],
tmp_dir: Path | None = None, tmp_dir: Path | None = None,
) -> None: ) -> None:
"""Remove folder with excludes.""" """Remove folder with excludes.
Must be run in executor.
"""
with TemporaryDirectory(dir=tmp_dir) as temp_path: with TemporaryDirectory(dir=tmp_dir) as temp_path:
temp_path = Path(temp_path) temp_path = Path(temp_path)
moved_files: list[Path] = [] moved_files: list[Path] = []