Improve is docker env checks (#132404)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Sander Hoentjen <sander@hoentjen.eu>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
Craig Andrews 2025-01-02 11:21:49 -05:00 committed by GitHub
parent 5439613bff
commit 9e8df72c0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 85 additions and 22 deletions

View File

@ -89,7 +89,7 @@ from .helpers import (
)
from .helpers.dispatcher import async_dispatcher_send_internal
from .helpers.storage import get_internal_store_manager
from .helpers.system_info import async_get_system_info, is_official_image
from .helpers.system_info import async_get_system_info
from .helpers.typing import ConfigType
from .setup import (
# _setup_started is marked as protected to make it clear
@ -106,6 +106,7 @@ from .util.async_ import create_eager_task
from .util.hass_dict import HassKey
from .util.logging import async_activate_log_queue_handler
from .util.package import async_get_user_site, is_docker_env, is_virtual_env
from .util.system_info import is_official_image
with contextlib.suppress(ImportError):
# Ensure anyio backend is imported to avoid it being imported in the event loop

View File

@ -23,10 +23,10 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.system_info import is_official_image
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from homeassistant.util.signal_type import SignalType
from homeassistant.util.system_info import is_official_image
DOMAIN = "ffmpeg"

View File

@ -5,7 +5,6 @@ from __future__ import annotations
from functools import cache
from getpass import getuser
import logging
import os
import platform
from typing import TYPE_CHECKING, Any
@ -13,6 +12,7 @@ from homeassistant.const import __version__ as current_version
from homeassistant.core import HomeAssistant
from homeassistant.loader import bind_hass
from homeassistant.util.package import is_docker_env, is_virtual_env
from homeassistant.util.system_info import is_official_image
from .hassio import is_hassio
from .importlib import async_import_module
@ -23,12 +23,6 @@ _LOGGER = logging.getLogger(__name__)
_DATA_MAC_VER = "system_info_mac_ver"
@cache
def is_official_image() -> bool:
"""Return True if Home Assistant is running in an official container."""
return os.path.isfile("/OFFICIAL_IMAGE")
@singleton(_DATA_MAC_VER)
async def async_get_mac_ver(hass: HomeAssistant) -> str:
"""Return the macOS version."""

View File

@ -15,6 +15,8 @@ from urllib.parse import urlparse
from packaging.requirements import InvalidRequirement, Requirement
from .system_info import is_official_image
_LOGGER = logging.getLogger(__name__)
@ -28,8 +30,13 @@ def is_virtual_env() -> bool:
@cache
def is_docker_env() -> bool:
"""Return True if we run in a docker env."""
return Path("/.dockerenv").exists()
"""Return True if we run in a container env."""
return (
Path("/.dockerenv").exists()
or Path("/run/.containerenv").exists()
or "KUBERNETES_SERVICE_HOST" in os.environ
or is_official_image()
)
def get_installed_versions(specifiers: set[str]) -> set[str]:

View File

@ -0,0 +1,12 @@
"""Util to gather system info."""
from __future__ import annotations
from functools import cache
import os
@cache
def is_official_image() -> bool:
"""Return True if Home Assistant is running in an official container."""
return os.path.isfile("/OFFICIAL_IMAGE")

View File

@ -9,17 +9,7 @@ import pytest
from homeassistant.components import hassio
from homeassistant.const import __version__ as current_version
from homeassistant.core import HomeAssistant
from homeassistant.helpers.system_info import async_get_system_info, is_official_image
async def test_is_official_image() -> None:
"""Test is_official_image."""
is_official_image.cache_clear()
with patch("homeassistant.helpers.system_info.os.path.isfile", return_value=True):
assert is_official_image() is True
is_official_image.cache_clear()
with patch("homeassistant.helpers.system_info.os.path.isfile", return_value=False):
assert is_official_image() is False
from homeassistant.helpers.system_info import async_get_system_info
async def test_get_system_info(hass: HomeAssistant) -> None:

View File

@ -410,3 +410,47 @@ def test_check_package_previous_failed_install() -> None:
with patch("homeassistant.util.package.version", return_value=None):
assert not package.is_installed(installed_package)
assert not package.is_installed(f"{installed_package}=={installed_version}")
@pytest.mark.parametrize("dockerenv", [True, False], ids=["dockerenv", "not_dockerenv"])
@pytest.mark.parametrize(
"containerenv", [True, False], ids=["containerenv", "not_containerenv"]
)
@pytest.mark.parametrize(
"kubernetes_service_host", [True, False], ids=["kubernetes", "not_kubernetes"]
)
@pytest.mark.parametrize(
"is_official_image", [True, False], ids=["official_image", "not_official_image"]
)
async def test_is_docker_env(
dockerenv: bool,
containerenv: bool,
kubernetes_service_host: bool,
is_official_image: bool,
) -> None:
"""Test is_docker_env."""
def new_path_mock(path: str):
mock = Mock()
if path == "/.dockerenv":
mock.exists.return_value = dockerenv
elif path == "/run/.containerenv":
mock.exists.return_value = containerenv
return mock
env = {}
if kubernetes_service_host:
env["KUBERNETES_SERVICE_HOST"] = "True"
package.is_docker_env.cache_clear()
with (
patch("homeassistant.util.package.Path", side_effect=new_path_mock),
patch(
"homeassistant.util.package.is_official_image",
return_value=is_official_image,
),
patch.dict(os.environ, env),
):
assert package.is_docker_env() is any(
[dockerenv, containerenv, kubernetes_service_host, is_official_image]
)

View File

@ -0,0 +1,15 @@
"""Tests for the system info helper."""
from unittest.mock import patch
from homeassistant.util.system_info import is_official_image
async def test_is_official_image() -> None:
"""Test is_official_image."""
is_official_image.cache_clear()
with patch("homeassistant.util.system_info.os.path.isfile", return_value=True):
assert is_official_image() is True
is_official_image.cache_clear()
with patch("homeassistant.util.system_info.os.path.isfile", return_value=False):
assert is_official_image() is False