Add container arch to system info (#147372)

This commit is contained in:
Stefan Agner
2025-06-24 09:52:21 +02:00
committed by GitHub
parent 121239bcf7
commit e5d19baf3e
14 changed files with 99 additions and 118 deletions

View File

@ -12,7 +12,6 @@ import re
import struct
from typing import Any, NamedTuple
import aiofiles
from aiohasupervisor import SupervisorError
import voluptuous as vol
@ -239,12 +238,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()
return {"x86": "i386"}.get(raw_arch, raw_arch)
class APIEndpointSettings(NamedTuple):
"""Settings for API endpoint."""
@ -566,8 +559,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh()
hass.data[ADDONS_COORDINATOR] = coordinator
arch = await _get_arch()
def deprecated_setup_issue() -> None:
os_info = get_os_info(hass)
info = get_info(hass)
@ -575,6 +566,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return
is_haos = info.get("hassos") is not None
board = os_info.get("board")
arch = info.get("arch", "unknown")
unsupported_board = board in {"tinker", "odroid-xu4", "rpi2"}
unsupported_os_on_board = board in {"rpi3", "rpi4"}
if is_haos and (unsupported_board or unsupported_os_on_board):

View File

@ -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
@ -18,6 +17,7 @@ from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_LATITUDE,
ATTR_LONGITUDE,
EVENT_HOMEASSISTANT_STARTED,
RESTART_EXIT_CODE,
SERVICE_RELOAD,
SERVICE_SAVE_PERSISTENT_STATES,
@ -26,6 +26,7 @@ from homeassistant.const import (
SERVICE_TURN_ON,
)
from homeassistant.core import (
Event,
HomeAssistant,
ServiceCall,
ServiceResponse,
@ -101,12 +102,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."""
@ -411,45 +406,50 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
hass.data[DATA_EXPOSED_ENTITIES] = exposed_entities
async_set_stop_handler(hass, _async_stop)
info = await async_get_system_info(hass)
async def _async_check_deprecation(event: Event) -> None:
"""Check and create deprecation issues after startup."""
info = await async_get_system_info(hass)
installation_type = info["installation_type"][15:]
if installation_type in {"Core", "Container"}:
deprecated_method = installation_type == "Core"
bit32 = _is_32_bit()
arch = info["arch"]
if bit32 and installation_type == "Container":
arch = await _get_arch()
ir.async_create_issue(
hass,
DOMAIN,
"deprecated_container",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_container",
translation_placeholders={"arch": arch},
)
deprecated_architecture = bit32 and installation_type != "Container"
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": installation_type,
"arch": arch,
},
)
installation_type = info["installation_type"][15:]
if installation_type in {"Core", "Container"}:
deprecated_method = installation_type == "Core"
bit32 = _is_32_bit()
arch = info["arch"]
if bit32 and installation_type == "Container":
arch = info.get("container_arch", arch)
ir.async_create_issue(
hass,
DOMAIN,
"deprecated_container",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_container",
translation_placeholders={"arch": arch},
)
deprecated_architecture = bit32 and installation_type != "Container"
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": installation_type,
"arch": arch,
},
)
# Delay deprecation check to make sure installation method is determined correctly
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_check_deprecation)
return True

View File

@ -124,6 +124,7 @@
"info": {
"arch": "CPU architecture",
"config_dir": "Configuration directory",
"container_arch": "Container architecture",
"dev": "Development",
"docker": "Docker",
"hassio": "Supervisor",

View File

@ -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"),

View File

@ -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"

View File

@ -3,7 +3,6 @@
aiodhcpwatcher==1.2.0
aiodiscover==2.7.0
aiodns==3.5.0
aiofiles==24.1.0
aiohasupervisor==0.3.1
aiohttp-asyncmdnsresolver==0.1.1
aiohttp-fast-zlib==0.3.0
@ -201,6 +200,14 @@ tenacity!=8.4.0
# TypeError: 'Timeout' object does not support the context manager protocol
async-timeout==4.0.3
# aiofiles keeps getting downgraded by custom components
# causing newer methods to not be available and breaking
# some integrations at startup
# https://github.com/home-assistant/core/issues/127529
# https://github.com/home-assistant/core/issues/122508
# https://github.com/home-assistant/core/issues/118004
aiofiles>=24.1.0
# multidict < 6.4.0 has memory leaks
# https://github.com/aio-libs/multidict/issues/1134
# https://github.com/aio-libs/multidict/issues/1131

View File

@ -24,7 +24,6 @@ classifiers = [
requires-python = ">=3.13.2"
dependencies = [
"aiodns==3.5.0",
"aiofiles==24.1.0",
# Integrations may depend on hassio integration without listing it to
# change behavior based on presence of supervisor. Deprecated with #127228
# Lib can be removed with 2025.11

1
requirements.txt generated
View File

@ -4,7 +4,6 @@
# Home Assistant Core
aiodns==3.5.0
aiofiles==24.1.0
aiohasupervisor==0.3.1
aiohttp==3.12.13
aiohttp_cors==0.8.1

View File

@ -226,6 +226,14 @@ tenacity!=8.4.0
# TypeError: 'Timeout' object does not support the context manager protocol
async-timeout==4.0.3
# aiofiles keeps getting downgraded by custom components
# causing newer methods to not be available and breaking
# some integrations at startup
# https://github.com/home-assistant/core/issues/127529
# https://github.com/home-assistant/core/issues/122508
# https://github.com/home-assistant/core/issues/118004
aiofiles>=24.1.0
# multidict < 6.4.0 has memory leaks
# https://github.com/aio-libs/multidict/issues/1134
# https://github.com/aio-libs/multidict/issues/1131

View File

@ -9,6 +9,7 @@
dev | False
hassio | False
docker | False
container_arch | None
user | hass
virtualenv | False
python_version | 3.13.1

View File

@ -1931,6 +1931,7 @@ async def test_download_support_package(
"virtualenv": False,
"python_version": "3.13.1",
"docker": False,
"container_arch": None,
"arch": "x86_64",
"timezone": "US/Pacific",
"os_name": "Linux",

View File

@ -260,16 +260,3 @@ def all_setup_requests(
},
},
)
@pytest.fixture
def arch() -> str:
"""Arch found in apk file."""
return "amd64"
@pytest.fixture(autouse=True)
def mock_arch_file(arch: str) -> Generator[None]:
"""Mock arch file."""
with patch("homeassistant.components.hassio._get_arch", return_value=arch):
yield

View File

@ -1156,10 +1156,6 @@ def test_deprecated_constants(
("rpi2", "deprecated_os_armv7"),
],
)
@pytest.mark.parametrize(
"arch",
["armv7"],
)
async def test_deprecated_installation_issue_os_armv7(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
@ -1170,13 +1166,6 @@ async def test_deprecated_installation_issue_os_armv7(
"""Test deprecated installation issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "armv7",
},
),
patch(
"homeassistant.components.hassio._is_32_bit",
return_value=True,
@ -1185,7 +1174,8 @@ async def test_deprecated_installation_issue_os_armv7(
"homeassistant.components.hassio.get_os_info", return_value={"board": board}
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": True}
"homeassistant.components.hassio.get_info",
return_value={"hassos": True, "arch": "armv7"},
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
@ -1238,13 +1228,6 @@ async def test_deprecated_installation_issue_32bit_os(
"""Test deprecated architecture issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": arch,
},
),
patch(
"homeassistant.components.hassio._is_32_bit",
return_value=True,
@ -1254,7 +1237,8 @@ async def test_deprecated_installation_issue_32bit_os(
return_value={"board": "rpi3-64"},
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": True}
"homeassistant.components.hassio.get_info",
return_value={"hassos": True, "arch": arch},
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
@ -1305,13 +1289,6 @@ async def test_deprecated_installation_issue_32bit_supervised(
"""Test deprecated architecture issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant Supervised",
"arch": arch,
},
),
patch(
"homeassistant.components.hassio._is_32_bit",
return_value=True,
@ -1321,7 +1298,8 @@ async def test_deprecated_installation_issue_32bit_supervised(
return_value={"board": "rpi3-64"},
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": None}
"homeassistant.components.hassio.get_info",
return_value={"hassos": None, "arch": arch},
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
@ -1376,13 +1354,6 @@ async def test_deprecated_installation_issue_64bit_supervised(
"""Test deprecated architecture issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant Supervised",
"arch": arch,
},
),
patch(
"homeassistant.components.hassio._is_32_bit",
return_value=False,
@ -1392,7 +1363,8 @@ async def test_deprecated_installation_issue_64bit_supervised(
return_value={"board": "generic-x86-64"},
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": None}
"homeassistant.components.hassio.get_info",
return_value={"hassos": None, "arch": arch},
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
@ -1445,13 +1417,6 @@ async def test_deprecated_installation_issue_supported_board(
"""Test no deprecated installation issue for a supported board."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "aarch64",
},
),
patch(
"homeassistant.components.hassio._is_32_bit",
return_value=False,
@ -1460,7 +1425,8 @@ async def test_deprecated_installation_issue_supported_board(
"homeassistant.components.hassio.get_os_info", return_value={"board": board}
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": True}
"homeassistant.components.hassio.get_info",
return_value={"hassos": True, "arch": "aarch64"},
),
):
assert await async_setup_component(hass, "homeassistant", {})

View File

@ -24,6 +24,7 @@ from homeassistant.const import (
ENTITY_MATCH_ALL,
ENTITY_MATCH_NONE,
EVENT_CORE_CONFIG_UPDATE,
EVENT_HOMEASSISTANT_STARTED,
SERVICE_SAVE_PERSISTENT_STATES,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
@ -668,6 +669,7 @@ async def test_deprecated_installation_issue_32bit_core(
),
):
assert await async_setup_component(hass, DOMAIN, {})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
@ -707,6 +709,7 @@ async def test_deprecated_installation_issue_64bit_core(
),
):
assert await async_setup_component(hass, DOMAIN, {})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
@ -738,6 +741,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,12 +749,9 @@ 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, {})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1