Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve is docker env checks #132404

Merged
merged 15 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion homeassistant/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
)
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
Expand All @@ -106,6 +106,7 @@
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
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/ffmpeg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
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"

Expand Down
8 changes: 1 addition & 7 deletions homeassistant/helpers/system_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from functools import cache
from getpass import getuser
import logging
import os
import platform
from typing import TYPE_CHECKING, Any

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
Expand All @@ -23,12 +23,6 @@
_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."""
Expand Down
11 changes: 9 additions & 2 deletions homeassistant/util/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

from packaging.requirements import InvalidRequirement, Requirement

from .system_info import is_official_image

_LOGGER = logging.getLogger(__name__)


Expand All @@ -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()
frenck marked this conversation as resolved.
Show resolved Hide resolved
candrews marked this conversation as resolved.
Show resolved Hide resolved
)


def get_installed_versions(specifiers: set[str]) -> set[str]:
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/util/system_info.py
Original file line number Diff line number Diff line change
@@ -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:
frenck marked this conversation as resolved.
Show resolved Hide resolved
"""Return True if Home Assistant is running in an official container."""
return os.path.isfile("/OFFICIAL_IMAGE")
12 changes: 1 addition & 11 deletions tests/helpers/test_system_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,7 @@
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:
Expand Down
44 changes: 44 additions & 0 deletions tests/util/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
)
15 changes: 15 additions & 0 deletions tests/util/test_system_info.py
Original file line number Diff line number Diff line change
@@ -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
Loading