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
# Need install/update
with TemporaryDirectory(dir=self.sys_config.path_tmp) as tmp_folder:
profile_file = Path(tmp_folder, "apparmor.txt")
tmp_folder: TemporaryDirectory | None = None
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)
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)
finally:
if tmp_folder:
await self.sys_run_in_executor(tmp_folder.cleanup)
async def uninstall_apparmor(self) -> None:
"""Remove AppArmor profile for Add-on."""

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Callable
import errno
from io import IOBase
import logging
from pathlib import Path
import re
@ -518,29 +519,28 @@ class APIBackups(CoreSysAttributes):
except vol.Invalid as ex:
raise APIError(humanize_error(filename, ex)) from None
with TemporaryDirectory(dir=tmp_path.as_posix()) as temp_dir:
tar_file = Path(temp_dir, "backup.tar")
temp_dir: TemporaryDirectory | None = None
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()
contents = await reader.next()
try:
with tar_file.open("wb") as backup:
while True:
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
tar_file = await self.sys_run_in_executor(open_backup_file)
while chunk := await contents.read_chunk(size=2**16):
await self.sys_run_in_executor(backup_file_stream.write, chunk)
backup = await asyncio.shield(
self.sys_backups.import_backup(
@ -550,6 +550,21 @@ class APIBackups(CoreSysAttributes):
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:
return {ATTR_SLUG: backup.slug}

View File

@ -158,25 +158,35 @@ class Supervisor(CoreSysAttributes):
) from err
# Load
with TemporaryDirectory(dir=self.sys_config.path_tmp) as tmp_dir:
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
temp_dir: TemporaryDirectory | None = None
try:
await self.sys_host.apparmor.load_profile(
"hassio-supervisor", profile_file
)
except HostAppArmorError as err:
raise SupervisorAppArmorError(
"Can't update AppArmor profile!", _LOGGER.error
) from err
def write_profile() -> Path:
nonlocal temp_dir
temp_dir = TemporaryDirectory(dir=self.sys_config.path_tmp)
profile_file = Path(temp_dir.name, "apparmor.txt")
profile_file.write_text(data, encoding="utf-8")
return profile_file
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:
"""Update Supervisor version."""

View File

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