From 83aeb572c4c14d227d7df19fce14a4ced2265e62 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 23 Jun 2025 17:17:33 +0200 Subject: [PATCH] Load container architecture in system info Make the Alpine Linux container architecture from the Alpine package manager available in the system info. This information is useful not only to create the deprecation repairs, but also as a general system information since the container architecture may differ from the host architecture, for example, when running a 64-bit host with a 32-bit container. --- .../components/homeassistant/__init__.py | 9 +-------- .../components/homeassistant/strings.json | 1 + .../components/homeassistant/system_health.py | 1 + homeassistant/helpers/system_info.py | 18 ++++++++++++++++++ tests/components/homeassistant/test_init.py | 5 +---- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/homeassistant/__init__.py b/homeassistant/components/homeassistant/__init__.py index 4360fa9c16e..96bb68bfb65 100644 --- a/homeassistant/components/homeassistant/__init__.py +++ b/homeassistant/components/homeassistant/__init__.py @@ -7,7 +7,6 @@ import logging import struct from typing import Any -import aiofiles import voluptuous as vol from homeassistant import config as conf_util, core_config @@ -101,12 +100,6 @@ def _is_32_bit() -> bool: return size * 8 == 32 -async def _get_arch() -> str: - async with aiofiles.open("/etc/apk/arch") as arch_file: - raw_arch = (await arch_file.read()).strip() - return {"x86": "i386", "x86_64": "amd64"}.get(raw_arch, raw_arch) - - async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: C901 """Set up general services related to Home Assistant.""" @@ -419,7 +412,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: bit32 = _is_32_bit() arch = info["arch"] if bit32 and installation_type == "Container": - arch = await _get_arch() + arch = info.get("container_arch", arch) ir.async_create_issue( hass, DOMAIN, diff --git a/homeassistant/components/homeassistant/strings.json b/homeassistant/components/homeassistant/strings.json index 940af999c4d..7c95680076c 100644 --- a/homeassistant/components/homeassistant/strings.json +++ b/homeassistant/components/homeassistant/strings.json @@ -124,6 +124,7 @@ "info": { "arch": "CPU architecture", "config_dir": "Configuration directory", + "container_arch": "Container architecture", "dev": "Development", "docker": "Docker", "hassio": "Supervisor", diff --git a/homeassistant/components/homeassistant/system_health.py b/homeassistant/components/homeassistant/system_health.py index 8a51b9cd418..3f98c5ae6e0 100644 --- a/homeassistant/components/homeassistant/system_health.py +++ b/homeassistant/components/homeassistant/system_health.py @@ -27,6 +27,7 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: "dev": info.get("dev"), "hassio": info.get("hassio"), "docker": info.get("docker"), + "container_arch": info.get("container_arch"), "user": info.get("user"), "virtualenv": info.get("virtualenv"), "python_version": info.get("python_version"), diff --git a/homeassistant/helpers/system_info.py b/homeassistant/helpers/system_info.py index 30b7616319d..1baec4df052 100644 --- a/homeassistant/helpers/system_info.py +++ b/homeassistant/helpers/system_info.py @@ -21,6 +21,7 @@ from .singleton import singleton _LOGGER = logging.getLogger(__name__) _DATA_MAC_VER = "system_info_mac_ver" +_DATA_CONTAINER_ARCH = "system_info_container_arch" @singleton(_DATA_MAC_VER) @@ -29,6 +30,22 @@ async def async_get_mac_ver(hass: HomeAssistant) -> str: return (await hass.async_add_executor_job(platform.mac_ver))[0] +@singleton(_DATA_CONTAINER_ARCH) +async def async_get_container_arch(hass: HomeAssistant) -> str: + """Return the container architecture.""" + + def _read_arch_file() -> str: + """Read the architecture from /etc/apk/arch.""" + with open("/etc/apk/arch", encoding="utf-8") as arch_file: + return arch_file.read().strip() + + try: + raw_arch = await hass.async_add_executor_job(_read_arch_file) + except FileNotFoundError: + return "unknown" + return {"x86": "i386", "x86_64": "amd64"}.get(raw_arch, raw_arch) + + # Cache the result of getuser() because it can call getpwuid() which # can do blocking I/O to look up the username in /etc/passwd. cached_get_user = cache(getuser) @@ -79,6 +96,7 @@ async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]: if info_object["docker"]: if info_object["user"] == "root" and is_official_image(): info_object["installation_type"] = "Home Assistant Container" + info_object["container_arch"] = await async_get_container_arch(hass) else: info_object["installation_type"] = "Unsupported Third Party Container" diff --git a/tests/components/homeassistant/test_init.py b/tests/components/homeassistant/test_init.py index 0779339cf65..2f89d9ae2df 100644 --- a/tests/components/homeassistant/test_init.py +++ b/tests/components/homeassistant/test_init.py @@ -738,6 +738,7 @@ async def test_deprecated_installation_issue_32bit( "homeassistant.components.homeassistant.async_get_system_info", return_value={ "installation_type": "Home Assistant Container", + "container_arch": arch, "arch": arch, }, ), @@ -745,10 +746,6 @@ async def test_deprecated_installation_issue_32bit( "homeassistant.components.homeassistant._is_32_bit", return_value=True, ), - patch( - "homeassistant.components.homeassistant._get_arch", - return_value=arch, - ), ): assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done()