From 0ef71d1dd1d9aad7282055fff4dc1057ebc6c4f7 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 4 Mar 2026 10:59:14 +0100 Subject: [PATCH] Drop unsupported architectures and machines, create issue for affected apps (#6607) * Drop unsupported architectures and machines from Supervisor Since #5620 Supervisor no longer updates the version information on unsupported architectures and machines. This means users can no longer update to newer version of Supervisor since that PR got released. Furthermore since #6347 we also no longer build for these architectures. With this, any code related to these architectures becomes dead code and should be removed. This commit removes all refrences to the deprecated architectures and machines from Supervisor. This affects the following architectures: - armhf - armv7 - i386 And the following machines: - odroid-xu - qemuarm - qemux86 - raspberrypi - raspberrypi2 - raspberrypi3 - raspberrypi4 - tinker * Create issue if an app using a deprecated architecture is installed This adds a check to the resolution system to detect if an app is installed that uses a deprecated architecture. If so, it will show a warning to the user and recommend them to uninstall the app. * Formally deprecate machine add-on configs as well Not only deprecate add-on configs for unsupported architectures, but also for unsupported machines. * For installed add-ons architecture must always exist Fail hard in case of missing architecture, as this is a required field for installed add-ons. This will prevent the Supervisor from running with an unsupported configuration and causing further issues down the line. --- supervisor/addons/addon.py | 15 +- supervisor/addons/model.py | 37 +++- supervisor/addons/validate.py | 30 +++- supervisor/arch.py | 3 - supervisor/const.py | 18 +- supervisor/data/arch.json | 34 ++-- supervisor/docker/interface.py | 3 - .../checks/deprecated_arch_addon.py | 61 +++++++ supervisor/resolution/const.py | 1 + .../evaluations/system_architecture.py | 2 +- .../resolution/fixups/addon_execute_remove.py | 2 +- tests/addons/test_config.py | 75 ++++++-- tests/api/test_store.py | 12 +- tests/docker/test_interface.py | 19 +- tests/fixtures/addon-config-add-image.json | 2 +- tests/fixtures/addon-config-remove-image.json | 2 +- tests/fixtures/addons/core/samba/build.yaml | 3 - tests/fixtures/addons/core/samba/config.yaml | 3 - .../fixtures/addons/local/example/build.yaml | 3 - .../fixtures/addons/local/example/config.yaml | 3 - .../addons/local/example_image/build.yaml | 3 - .../addons/local/example_image/config.yaml | 3 - tests/fixtures/addons/local/ssh/build.yaml | 3 - tests/fixtures/addons/local/ssh/config.yaml | 3 - tests/fixtures/basic-build-config.json | 1 - tests/fixtures/version_stable.json | 5 - .../check/test_check_deprecated_arch_addon.py | 164 ++++++++++++++++++ .../fixup/test_addon_execute_remove.py | 28 +++ tests/store/test_store_manager.py | 8 +- tests/test_arch.py | 127 ++------------ tests/test_updater.py | 6 +- 31 files changed, 460 insertions(+), 219 deletions(-) create mode 100644 supervisor/resolution/checks/deprecated_arch_addon.py create mode 100644 tests/resolution/check/test_check_deprecated_arch_addon.py diff --git a/supervisor/addons/addon.py b/supervisor/addons/addon.py index ab4f61646..1a56911fe 100644 --- a/supervisor/addons/addon.py +++ b/supervisor/addons/addon.py @@ -89,7 +89,7 @@ from ..hardware.data import Device from ..homeassistant.const import WSEvent from ..jobs.const import JobConcurrency, JobThrottle from ..jobs.decorator import Job -from ..resolution.const import ContextType, IssueType, UnhealthyReason +from ..resolution.const import ContextType, IssueType, SuggestionType, UnhealthyReason from ..resolution.data import Issue from ..store.addon import AddonStore from ..utils import check_port @@ -239,6 +239,19 @@ class Addon(AddonModel): await self._check_ingress_port() + if (self.has_deprecated_arch and not self.has_supported_arch) or ( + self.has_deprecated_machine and not self.has_supported_machine + ): + self.sys_resolution.create_issue( + IssueType.DEPRECATED_ARCH_ADDON, + ContextType.ADDON, + reference=self.slug, + suggestions=[SuggestionType.EXECUTE_REMOVE], + ) + with suppress(DockerError): + await self.instance.attach(version=self.version) + return + default_image = self._image(self.data) try: await self.instance.attach(version=self.version) diff --git a/supervisor/addons/model.py b/supervisor/addons/model.py index 4ac0a1030..0419d95c8 100644 --- a/supervisor/addons/model.py +++ b/supervisor/addons/model.py @@ -12,6 +12,7 @@ from typing import Any from awesomeversion import AwesomeVersion, AwesomeVersionException from ..const import ( + ARCH_DEPRECATED, ATTR_ADVANCED, ATTR_APPARMOR, ATTR_ARCH, @@ -78,6 +79,7 @@ from ..const import ( ATTR_VIDEO, ATTR_WATCHDOG, ATTR_WEBUI, + MACHINE_DEPRECATED, SECURITY_DEFAULT, SECURITY_DISABLE, SECURITY_PROFILE, @@ -94,6 +96,7 @@ from ..exceptions import ( AddonNotSupportedError, AddonNotSupportedHomeAssistantVersionError, AddonNotSupportedMachineTypeError, + HassioArchNotFound, ) from ..jobs.const import JOB_GROUP_ADDON from ..jobs.job_group import JobGroup @@ -542,6 +545,35 @@ class AddonModel(JobGroup, ABC): """Return list of supported arch.""" return self.data[ATTR_ARCH] + @property + def has_deprecated_arch(self) -> bool: + """Return True if add-on includes deprecated architectures.""" + return any(arch in ARCH_DEPRECATED for arch in self.supported_arch) + + @property + def has_supported_arch(self) -> bool: + """Return True if add-on supports any architecture on this system.""" + return self.sys_arch.is_supported(self.supported_arch) + + @property + def has_deprecated_machine(self) -> bool: + """Return True if add-on includes deprecated machine entries.""" + return any( + machine.lstrip("!") in MACHINE_DEPRECATED + for machine in self.supported_machine + ) + + @property + def has_supported_machine(self) -> bool: + """Return True if add-on supports this machine.""" + if not (machine_types := self.supported_machine): + return True + + return ( + f"!{self.sys_machine}" not in machine_types + and self.sys_machine in machine_types + ) + @property def supported_machine(self) -> list[str]: """Return list of supported machine.""" @@ -718,7 +750,10 @@ class AddonModel(JobGroup, ABC): """Generate image name from data.""" # Repository with Dockerhub images if ATTR_IMAGE in config: - arch = self.sys_arch.match(config[ATTR_ARCH]) + try: + arch = self.sys_arch.match(config[ATTR_ARCH]) + except HassioArchNotFound: + arch = self.sys_arch.default return config[ATTR_IMAGE].format(arch=arch) # local build diff --git a/supervisor/addons/validate.py b/supervisor/addons/validate.py index e51754c31..c56df63f4 100644 --- a/supervisor/addons/validate.py +++ b/supervisor/addons/validate.py @@ -9,7 +9,8 @@ import uuid import voluptuous as vol from ..const import ( - ARCH_ALL, + ARCH_ALL_COMPAT, + ARCH_DEPRECATED, ATTR_ACCESS_TOKEN, ATTR_ADVANCED, ATTR_APPARMOR, @@ -97,6 +98,7 @@ from ..const import ( ATTR_VIDEO, ATTR_WATCHDOG, ATTR_WEBUI, + MACHINE_DEPRECATED, ROLE_ALL, ROLE_DEFAULT, AddonBoot, @@ -156,6 +158,8 @@ SCHEMA_ELEMENT = vol.Schema( RE_MACHINE = re.compile( r"^!?(?:" r"|intel-nuc" + r"|khadas-vim3" + r"|generic-aarch64" r"|generic-x86-64" r"|odroid-c2" r"|odroid-c4" @@ -207,6 +211,26 @@ def _warn_addon_config(config: dict[str, Any]): name, ) + if deprecated_arches := [ + arch for arch in config.get(ATTR_ARCH, []) if arch in ARCH_DEPRECATED + ]: + _LOGGER.warning( + "Add-on config 'arch' uses deprecated values %s. Please report this to the maintainer of %s", + deprecated_arches, + name, + ) + + if deprecated_machines := [ + machine + for machine in config.get(ATTR_MACHINE, []) + if machine.lstrip("!") in MACHINE_DEPRECATED + ]: + _LOGGER.warning( + "Add-on config 'machine' uses deprecated values %s. Please report this to the maintainer of %s", + deprecated_machines, + name, + ) + if ATTR_CODENOTARY in config: _LOGGER.warning( "Add-on '%s' uses deprecated 'codenotary' field in config. This field is no longer used and will be ignored. Please report this to the maintainer.", @@ -349,7 +373,7 @@ _SCHEMA_ADDON_CONFIG = vol.Schema( vol.Required(ATTR_VERSION): version_tag, vol.Required(ATTR_SLUG): vol.Match(RE_SLUG_FIELD), vol.Required(ATTR_DESCRIPTON): str, - vol.Required(ATTR_ARCH): [vol.In(ARCH_ALL)], + vol.Required(ATTR_ARCH): [vol.In(ARCH_ALL_COMPAT)], vol.Optional(ATTR_MACHINE): vol.All([vol.Match(RE_MACHINE)], vol.Unique()), vol.Optional(ATTR_URL): vol.Url(), vol.Optional(ATTR_STARTUP, default=AddonStartup.APPLICATION): vol.Coerce( @@ -462,7 +486,7 @@ SCHEMA_BUILD_CONFIG = vol.Schema( { vol.Optional(ATTR_BUILD_FROM, default=dict): vol.Any( vol.Match(RE_DOCKER_IMAGE_BUILD), - vol.Schema({vol.In(ARCH_ALL): vol.Match(RE_DOCKER_IMAGE_BUILD)}), + vol.Schema({vol.In(ARCH_ALL_COMPAT): vol.Match(RE_DOCKER_IMAGE_BUILD)}), ), vol.Optional(ATTR_SQUASH, default=False): vol.Boolean(), vol.Optional(ATTR_ARGS, default=dict): vol.Schema({str: str}), diff --git a/supervisor/arch.py b/supervisor/arch.py index 882d06ba9..d48ef145b 100644 --- a/supervisor/arch.py +++ b/supervisor/arch.py @@ -14,11 +14,8 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) ARCH_JSON: Path = Path(__file__).parent.joinpath("data/arch.json") MAP_CPU: dict[str, CpuArch] = { - "armv7": CpuArch.ARMV7, - "armv6": CpuArch.ARMHF, "armv8": CpuArch.AARCH64, "aarch64": CpuArch.AARCH64, - "i686": CpuArch.I386, "x86_64": CpuArch.AMD64, } diff --git a/supervisor/const.py b/supervisor/const.py index 7bd8a3c5b..ac9e5ef09 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -388,7 +388,20 @@ ARCH_AARCH64 = "aarch64" ARCH_AMD64 = "amd64" ARCH_I386 = "i386" -ARCH_ALL = [ARCH_ARMHF, ARCH_ARMV7, ARCH_AARCH64, ARCH_AMD64, ARCH_I386] +ARCH_ALL = [ARCH_AARCH64, ARCH_AMD64] +ARCH_DEPRECATED = [ARCH_ARMHF, ARCH_ARMV7, ARCH_I386] +ARCH_ALL_COMPAT = ARCH_ALL + ARCH_DEPRECATED + +MACHINE_DEPRECATED = [ + "odroid-xu", + "qemuarm", + "qemux86", + "raspberrypi", + "raspberrypi2", + "raspberrypi3", + "raspberrypi4", + "tinker", +] REPOSITORY_CORE = "core" REPOSITORY_LOCAL = "local" @@ -530,10 +543,7 @@ class BusEvent(StrEnum): class CpuArch(StrEnum): """Supported CPU architectures.""" - ARMV7 = "armv7" - ARMHF = "armhf" AARCH64 = "aarch64" - I386 = "i386" AMD64 = "amd64" diff --git a/supervisor/data/arch.json b/supervisor/data/arch.json index 0093cc510..8565aaf36 100644 --- a/supervisor/data/arch.json +++ b/supervisor/data/arch.json @@ -1,25 +1,17 @@ { - "raspberrypi": ["armhf"], - "raspberrypi2": ["armv7", "armhf"], - "raspberrypi3": ["armv7", "armhf"], - "raspberrypi3-64": ["aarch64", "armv7", "armhf"], - "raspberrypi4": ["armv7", "armhf"], - "raspberrypi4-64": ["aarch64", "armv7", "armhf"], - "raspberrypi5-64": ["aarch64", "armv7", "armhf"], - "yellow": ["aarch64", "armv7", "armhf"], - "green": ["aarch64", "armv7", "armhf"], - "tinker": ["armv7", "armhf"], - "odroid-c2": ["aarch64", "armv7", "armhf"], - "odroid-c4": ["aarch64", "armv7", "armhf"], - "odroid-m1": ["aarch64", "armv7", "armhf"], - "odroid-n2": ["aarch64", "armv7", "armhf"], - "odroid-xu": ["armv7", "armhf"], - "khadas-vim3": ["aarch64", "armv7", "armhf"], + "raspberrypi3-64": ["aarch64"], + "raspberrypi4-64": ["aarch64"], + "raspberrypi5-64": ["aarch64"], + "yellow": ["aarch64"], + "green": ["aarch64"], + "odroid-c2": ["aarch64"], + "odroid-c4": ["aarch64"], + "odroid-m1": ["aarch64"], + "odroid-n2": ["aarch64"], + "khadas-vim3": ["aarch64"], "generic-aarch64": ["aarch64"], - "qemux86": ["i386"], - "qemux86-64": ["amd64", "i386"], - "qemuarm": ["armhf"], + "qemux86-64": ["amd64"], "qemuarm-64": ["aarch64"], - "intel-nuc": ["amd64", "i386"], - "generic-x86-64": ["amd64", "i386"] + "intel-nuc": ["amd64"], + "generic-x86-64": ["amd64"] } diff --git a/supervisor/docker/interface.py b/supervisor/docker/interface.py index ce5e4d664..623895a22 100644 --- a/supervisor/docker/interface.py +++ b/supervisor/docker/interface.py @@ -50,10 +50,7 @@ from .stats import DockerStats _LOGGER: logging.Logger = logging.getLogger(__name__) MAP_ARCH: dict[CpuArch, str] = { - CpuArch.ARMV7: "linux/arm/v7", - CpuArch.ARMHF: "linux/arm/v6", CpuArch.AARCH64: "linux/arm64", - CpuArch.I386: "linux/386", CpuArch.AMD64: "linux/amd64", } diff --git a/supervisor/resolution/checks/deprecated_arch_addon.py b/supervisor/resolution/checks/deprecated_arch_addon.py new file mode 100644 index 000000000..d0f4c3010 --- /dev/null +++ b/supervisor/resolution/checks/deprecated_arch_addon.py @@ -0,0 +1,61 @@ +"""Helpers to check for add-ons using deprecated compatibility entries.""" + +from ...const import AddonStage, CoreState +from ...coresys import CoreSys +from ..const import ContextType, IssueType, SuggestionType +from .base import CheckBase + + +def setup(coresys: CoreSys) -> CheckBase: + """Check setup function.""" + return CheckDeprecatedArchAddon(coresys) + + +class CheckDeprecatedArchAddon(CheckBase): + """CheckDeprecatedArchAddon class for check.""" + + async def run_check(self) -> None: + """Run check if not affected by issue.""" + for addon in self.sys_addons.installed: + if addon.stage == AddonStage.DEPRECATED: + continue + + if (addon.has_deprecated_arch and not addon.has_supported_arch) or ( + addon.has_deprecated_machine and not addon.has_supported_machine + ): + self.sys_resolution.create_issue( + IssueType.DEPRECATED_ARCH_ADDON, + ContextType.ADDON, + reference=addon.slug, + suggestions=[SuggestionType.EXECUTE_REMOVE], + ) + + async def approve_check(self, reference: str | None = None) -> bool: + """Approve check if it is affected by issue.""" + if not reference: + return False + + addon = self.sys_addons.get_local_only(reference) + return ( + addon is not None + and addon.stage != AddonStage.DEPRECATED + and ( + (addon.has_deprecated_arch and not addon.has_supported_arch) + or (addon.has_deprecated_machine and not addon.has_supported_machine) + ) + ) + + @property + def issue(self) -> IssueType: + """Return a IssueType enum.""" + return IssueType.DEPRECATED_ARCH_ADDON + + @property + def context(self) -> ContextType: + """Return a ContextType enum.""" + return ContextType.ADDON + + @property + def states(self) -> list[CoreState]: + """Return a list of valid states when this check can run.""" + return [CoreState.SETUP, CoreState.RUNNING] diff --git a/supervisor/resolution/const.py b/supervisor/resolution/const.py index 34be58c2d..6142743ba 100644 --- a/supervisor/resolution/const.py +++ b/supervisor/resolution/const.py @@ -81,6 +81,7 @@ class IssueType(StrEnum): CORRUPT_REPOSITORY = "corrupt_repository" CORRUPT_FILESYSTEM = "corrupt_filesystem" DEPRECATED_ADDON = "deprecated_addon" + DEPRECATED_ARCH_ADDON = "deprecated_arch_addon" DETACHED_ADDON_MISSING = "detached_addon_missing" DETACHED_ADDON_REMOVED = "detached_addon_removed" DEVICE_ACCESS_MISSING = "device_access_missing" diff --git a/supervisor/resolution/evaluations/system_architecture.py b/supervisor/resolution/evaluations/system_architecture.py index 7a034e536..644fb8507 100644 --- a/supervisor/resolution/evaluations/system_architecture.py +++ b/supervisor/resolution/evaluations/system_architecture.py @@ -31,7 +31,7 @@ class EvaluateSystemArchitecture(EvaluateBase): async def evaluate(self): """Run evaluation.""" - return self.sys_host.info.sys_arch.supervisor in { + return self.sys_supervisor.arch in { "i386", "armhf", "armv7", diff --git a/supervisor/resolution/fixups/addon_execute_remove.py b/supervisor/resolution/fixups/addon_execute_remove.py index b3398fe93..ae77806c3 100644 --- a/supervisor/resolution/fixups/addon_execute_remove.py +++ b/supervisor/resolution/fixups/addon_execute_remove.py @@ -48,7 +48,7 @@ class FixupAddonExecuteRemove(FixupBase): @property def issues(self) -> list[IssueType]: """Return a IssueType enum list.""" - return [IssueType.DETACHED_ADDON_REMOVED] + return [IssueType.DETACHED_ADDON_REMOVED, IssueType.DEPRECATED_ARCH_ADDON] @property def auto(self) -> bool: diff --git a/tests/addons/test_config.py b/tests/addons/test_config.py index 2fa4645c6..d7ac00a77 100644 --- a/tests/addons/test_config.py +++ b/tests/addons/test_config.py @@ -187,6 +187,42 @@ def test_valid_basic_build(): vd.SCHEMA_BUILD_CONFIG(config) +def test_valid_legacy_arch_values_for_migration(): + """Validate legacy arch values are accepted for migration compatibility.""" + config = load_json_fixture("basic-addon-config.json") + config["arch"] = ["armv7", "amd64"] + + assert vd.SCHEMA_ADDON_CONFIG(config) + + +def test_valid_legacy_build_from_keys_for_migration(): + """Validate legacy build_from keys are accepted for migration compatibility.""" + config = load_json_fixture("basic-build-config.json") + config["build_from"]["i386"] = "mycustom/legacy-base:latest" + + assert vd.SCHEMA_BUILD_CONFIG(config) + + +def test_warn_legacy_arch_values(caplog: pytest.LogCaptureFixture): + """Warn when deprecated architecture values are present.""" + config = load_json_fixture("basic-addon-config.json") + config["arch"] = ["armv7", "amd64"] + + vd.SCHEMA_ADDON_CONFIG(config) + + assert "Add-on config 'arch' uses deprecated values" in caplog.text + + +def test_warn_legacy_machine_values(caplog: pytest.LogCaptureFixture): + """Warn when deprecated machine values are present.""" + config = load_json_fixture("basic-addon-config.json") + config["machine"] = ["qemux86"] + + vd.SCHEMA_ADDON_CONFIG(config) + + assert "Add-on config 'machine' uses deprecated values" in caplog.text + + async def test_valid_manifest_build(): """Validate build config with manifest build from.""" config = load_json_fixture("build-config-manifest.json") @@ -200,56 +236,66 @@ def test_valid_machine(): config["machine"] = [ "intel-nuc", + "khadas-vim3", + "generic-aarch64", "odroid-c2", "odroid-n2", "odroid-xu", - "qemuarm-64", "qemuarm", - "qemux86-64", + "qemuarm-64", "qemux86", + "qemux86-64", "raspberrypi", "raspberrypi2", - "raspberrypi3-64", "raspberrypi3", - "raspberrypi4-64", + "raspberrypi3-64", "raspberrypi4", + "raspberrypi4-64", "raspberrypi5-64", "tinker", + "yellow", + "green", + "generic-x86-64", ] assert vd.SCHEMA_ADDON_CONFIG(config) config["machine"] = [ "!intel-nuc", + "!khadas-vim3", + "!generic-aarch64", "!odroid-c2", "!odroid-n2", "!odroid-xu", - "!qemuarm-64", "!qemuarm", - "!qemux86-64", + "!qemuarm-64", "!qemux86", + "!qemux86-64", "!raspberrypi", "!raspberrypi2", - "!raspberrypi3-64", "!raspberrypi3", - "!raspberrypi4-64", + "!raspberrypi3-64", "!raspberrypi4", + "!raspberrypi4-64", "!raspberrypi5-64", "!tinker", + "!yellow", + "!green", + "!generic-x86-64", ] assert vd.SCHEMA_ADDON_CONFIG(config) config["machine"] = [ "odroid-n2", - "!odroid-xu", + "odroid-xu", + "qemuarm", "qemuarm-64", - "!qemuarm", - "qemux86-64", "qemux86", + "qemux86-64", "raspberrypi", - "raspberrypi4-64", "raspberrypi4", + "raspberrypi4-64", "raspberrypi5-64", "!tinker", ] @@ -263,10 +309,9 @@ def test_invalid_machine(): config["machine"] = [ "intel-nuc", - "raspberrypi3", "raspberrypi4-64", - "raspberrypi4", - "tinkerxy", + "raspberrypi7-64", + "generic-armv7", ] with pytest.raises(vol.Invalid): diff --git a/tests/api/test_store.py b/tests/api/test_store.py index beadaf1fd..bf084cbaa 100644 --- a/tests/api/test_store.py +++ b/tests/api/test_store.py @@ -587,12 +587,12 @@ async def test_api_store_addons_addon_availability_success( @pytest.mark.parametrize( ("supported_architectures", "api_action", "api_method", "installed"), [ - (["i386"], "availability", "get", False), - (["i386", "aarch64"], "availability", "get", False), - (["i386"], "install", "post", False), - (["i386", "aarch64"], "install", "post", False), - (["i386"], "update", "post", True), - (["i386", "aarch64"], "update", "post", True), + (["aarch64"], "availability", "get", False), + (["aarch64", "fooarch"], "availability", "get", False), + (["aarch64"], "install", "post", False), + (["aarch64", "fooarch"], "install", "post", False), + (["aarch64"], "update", "post", True), + (["aarch64", "fooarch"], "update", "post", True), ], ) async def test_api_store_addons_addon_availability_arch_not_supported( diff --git a/tests/docker/test_interface.py b/tests/docker/test_interface.py index a0353ba91..b355abea4 100644 --- a/tests/docker/test_interface.py +++ b/tests/docker/test_interface.py @@ -34,10 +34,7 @@ from tests.common import AsyncIterator, load_json_fixture @pytest.mark.parametrize( "cpu_arch, platform", [ - (CpuArch.ARMV7, "linux/arm/v7"), - (CpuArch.ARMHF, "linux/arm/v6"), (CpuArch.AARCH64, "linux/arm64"), - (CpuArch.I386, "linux/386"), (CpuArch.AMD64, "linux/amd64"), ], ) @@ -63,14 +60,14 @@ async def test_docker_image_default_platform( coresys.docker.images.inspect.return_value = {"Id": "test:1.2.3"} with ( patch.object( - type(coresys.supervisor), "arch", PropertyMock(return_value="i386") + type(coresys.supervisor), "arch", PropertyMock(return_value="amd64") ), ): await test_docker_interface.install(AwesomeVersion("1.2.3"), "test") coresys.docker.images.pull.assert_called_once_with( "test", tag="1.2.3", - platform="linux/386", + platform="linux/amd64", auth=None, stream=True, timeout=None, @@ -349,14 +346,14 @@ async def test_install_fires_progress_events( with ( patch.object( - type(coresys.supervisor), "arch", PropertyMock(return_value="i386") + type(coresys.supervisor), "arch", PropertyMock(return_value="amd64") ), ): await test_docker_interface.install(AwesomeVersion("1.2.3"), "test") coresys.docker.images.pull.assert_called_once_with( "test", tag="1.2.3", - platform="linux/386", + platform="linux/amd64", auth=None, stream=True, timeout=None, @@ -567,7 +564,7 @@ async def test_install_progress_handles_download_restart( with ( patch.object( - type(coresys.supervisor), "arch", PropertyMock(return_value="i386") + type(coresys.supervisor), "arch", PropertyMock(return_value="amd64") ), ): # Schedule job so we can listen for the end. Then we can assert against the WS mock @@ -805,7 +802,7 @@ async def test_install_progress_containerd_snapshot( async def mock_install(self) -> None: """Mock install.""" await super().install( - AwesomeVersion("1.2.3"), image="test", arch=CpuArch.I386 + AwesomeVersion("1.2.3"), image="test", arch=CpuArch.AMD64 ) # Fixture emulates log as received when using containerd snapshotter @@ -814,12 +811,12 @@ async def test_install_progress_containerd_snapshot( coresys.docker.images.pull.return_value = AsyncIterator(logs) test_docker_interface = TestDockerInterface(coresys) - with patch.object(Supervisor, "arch", PropertyMock(return_value="i386")): + with patch.object(Supervisor, "arch", PropertyMock(return_value="amd64")): await test_docker_interface.mock_install() coresys.docker.images.pull.assert_called_once_with( "test", tag="1.2.3", - platform="linux/386", + platform="linux/amd64", auth=None, stream=True, timeout=None, diff --git a/tests/fixtures/addon-config-add-image.json b/tests/fixtures/addon-config-add-image.json index a731433ec..79dc06be4 100644 --- a/tests/fixtures/addon-config-add-image.json +++ b/tests/fixtures/addon-config-add-image.json @@ -4,7 +4,7 @@ "slug": "ssh", "description": "Allow logging in remotely to Home Assistant using SSH", "url": "https://github.com/home-assistant/hassio-addons/tree/master/ssh", - "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], + "arch": ["aarch64", "amd64"], "init": false, "advanced": true, "host_dbus": true, diff --git a/tests/fixtures/addon-config-remove-image.json b/tests/fixtures/addon-config-remove-image.json index e05111ea8..99d06aaa4 100644 --- a/tests/fixtures/addon-config-remove-image.json +++ b/tests/fixtures/addon-config-remove-image.json @@ -4,7 +4,7 @@ "slug": "ssh", "description": "Allow logging in remotely to Home Assistant using SSH", "url": "https://github.com/home-assistant/hassio-addons/tree/master/ssh", - "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], + "arch": ["aarch64", "amd64"], "init": false, "advanced": true, "host_dbus": true, diff --git a/tests/fixtures/addons/core/samba/build.yaml b/tests/fixtures/addons/core/samba/build.yaml index 84dc2cbcb..db9fac1b2 100644 --- a/tests/fixtures/addons/core/samba/build.yaml +++ b/tests/fixtures/addons/core/samba/build.yaml @@ -1,6 +1,3 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.13" amd64: "ghcr.io/home-assistant/amd64-base:3.13" - armhf: "ghcr.io/home-assistant/armhf-base:3.13" - armv7: "ghcr.io/home-assistant/armv7-base:3.13" - i386: "ghcr.io/home-assistant/i386-base:3.13" diff --git a/tests/fixtures/addons/core/samba/config.yaml b/tests/fixtures/addons/core/samba/config.yaml index 51de0465a..d99e935f4 100644 --- a/tests/fixtures/addons/core/samba/config.yaml +++ b/tests/fixtures/addons/core/samba/config.yaml @@ -4,11 +4,8 @@ slug: samba description: Expose Home Assistant folders with SMB/CIFS url: "https://github.com/home-assistant/hassio-addons/tree/master/samba" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 startup: services init: false hassio_api: true diff --git a/tests/fixtures/addons/local/example/build.yaml b/tests/fixtures/addons/local/example/build.yaml index cdca3163d..8964c2eed 100644 --- a/tests/fixtures/addons/local/example/build.yaml +++ b/tests/fixtures/addons/local/example/build.yaml @@ -2,9 +2,6 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.15" amd64: "ghcr.io/home-assistant/amd64-base:3.15" - armhf: "ghcr.io/home-assistant/armhf-base:3.15" - armv7: "ghcr.io/home-assistant/armv7-base:3.15" - i386: "ghcr.io/home-assistant/i386-base:3.15" labels: org.opencontainers.image.title: "Home Assistant Add-on: Example add-on" org.opencontainers.image.description: "Example add-on to use as a blueprint for new add-ons." diff --git a/tests/fixtures/addons/local/example/config.yaml b/tests/fixtures/addons/local/example/config.yaml index ab94374ae..3f710b281 100644 --- a/tests/fixtures/addons/local/example/config.yaml +++ b/tests/fixtures/addons/local/example/config.yaml @@ -5,11 +5,8 @@ slug: example description: Example add-on url: "https://github.com/home-assistant/addons-example/tree/main/example" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 init: false map: - share:rw diff --git a/tests/fixtures/addons/local/example_image/build.yaml b/tests/fixtures/addons/local/example_image/build.yaml index cdca3163d..8964c2eed 100644 --- a/tests/fixtures/addons/local/example_image/build.yaml +++ b/tests/fixtures/addons/local/example_image/build.yaml @@ -2,9 +2,6 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.15" amd64: "ghcr.io/home-assistant/amd64-base:3.15" - armhf: "ghcr.io/home-assistant/armhf-base:3.15" - armv7: "ghcr.io/home-assistant/armv7-base:3.15" - i386: "ghcr.io/home-assistant/i386-base:3.15" labels: org.opencontainers.image.title: "Home Assistant Add-on: Example add-on" org.opencontainers.image.description: "Example add-on to use as a blueprint for new add-ons." diff --git a/tests/fixtures/addons/local/example_image/config.yaml b/tests/fixtures/addons/local/example_image/config.yaml index 0cee3435f..02878e4cd 100644 --- a/tests/fixtures/addons/local/example_image/config.yaml +++ b/tests/fixtures/addons/local/example_image/config.yaml @@ -5,11 +5,8 @@ slug: example_image description: Example add-on url: "https://github.com/home-assistant/addons-example/tree/main/example" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 init: false map: - share:rw diff --git a/tests/fixtures/addons/local/ssh/build.yaml b/tests/fixtures/addons/local/ssh/build.yaml index 973b3e320..b83c45868 100644 --- a/tests/fixtures/addons/local/ssh/build.yaml +++ b/tests/fixtures/addons/local/ssh/build.yaml @@ -1,9 +1,6 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.14" amd64: "ghcr.io/home-assistant/amd64-base:3.14" - armhf: "ghcr.io/home-assistant/armhf-base:3.14" - armv7: "ghcr.io/home-assistant/armv7-base:3.14" - i386: "ghcr.io/home-assistant/i386-base:3.14" labels: org.opencontainers.image.title: "Home Assistant Add-on: Test add-on" org.opencontainers.image.description: "Test add-on PyTest." diff --git a/tests/fixtures/addons/local/ssh/config.yaml b/tests/fixtures/addons/local/ssh/config.yaml index 1d001ebd2..cec2040ec 100644 --- a/tests/fixtures/addons/local/ssh/config.yaml +++ b/tests/fixtures/addons/local/ssh/config.yaml @@ -4,11 +4,8 @@ slug: ssh description: Allow logging in remotely to Home Assistant using SSH url: "https://github.com/home-assistant/hassio-addons/tree/master/ssh" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 init: false advanced: true host_dbus: true diff --git a/tests/fixtures/basic-build-config.json b/tests/fixtures/basic-build-config.json index 392664f9b..b5fc8bedc 100644 --- a/tests/fixtures/basic-build-config.json +++ b/tests/fixtures/basic-build-config.json @@ -1,6 +1,5 @@ { "build_from": { - "armhf": "mycustom/base-image:latest", "aarch64": "mycustom/base-image", "amd64": "ghcr.io/home-assistant/amd64-base-ubuntu:18.04" }, diff --git a/tests/fixtures/version_stable.json b/tests/fixtures/version_stable.json index 460f21261..9eb27d2c8 100644 --- a/tests/fixtures/version_stable.json +++ b/tests/fixtures/version_stable.json @@ -28,21 +28,16 @@ }, "hassos": { "ova": "13.2", - "rpi2": "13.2", - "rpi3": "13.2", "rpi3-64": "13.2", - "rpi4": "13.2", "rpi4-64": "13.2", "rpi5-64": "13.2", "yellow": "13.2", "green": "13.2", - "tinker": "13.2", "odroid-c2": "13.2", "odroid-c4": "13.2", "odroid-m1": "13.2", "odroid-m1s": "13.2", "odroid-n2": "13.2", - "odroid-xu4": "13.2", "generic-x86-64": "13.2", "generic-aarch64": "13.2", "khadas-vim3": "13.2" diff --git a/tests/resolution/check/test_check_deprecated_arch_addon.py b/tests/resolution/check/test_check_deprecated_arch_addon.py new file mode 100644 index 000000000..e59f61368 --- /dev/null +++ b/tests/resolution/check/test_check_deprecated_arch_addon.py @@ -0,0 +1,164 @@ +"""Test check for add-ons with deprecated architectures.""" + +from unittest.mock import patch + +from supervisor.addons.addon import Addon +from supervisor.const import AddonStage, CoreState +from supervisor.coresys import CoreSys +from supervisor.resolution.checks.deprecated_arch_addon import CheckDeprecatedArchAddon +from supervisor.resolution.const import ContextType, IssueType, SuggestionType + + +async def test_base(coresys: CoreSys): + """Test check basics.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + assert deprecated_arch_addon.slug == "deprecated_arch_addon" + assert deprecated_arch_addon.enabled + + +async def test_check(coresys: CoreSys, install_addon_ssh: Addon): + """Test check for installed add-ons with deprecated architectures.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + await deprecated_arch_addon() + assert len(coresys.resolution.issues) == 0 + + install_addon_ssh.data["arch"] = ["armv7"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 1 + assert coresys.resolution.issues[0].type is IssueType.DEPRECATED_ARCH_ADDON + assert coresys.resolution.issues[0].context is ContextType.ADDON + assert coresys.resolution.issues[0].reference == install_addon_ssh.slug + assert len(coresys.resolution.suggestions) == 1 + assert coresys.resolution.suggestions[0].type is SuggestionType.EXECUTE_REMOVE + + +async def test_check_ignores_mixed_supported_arch( + coresys: CoreSys, install_addon_ssh: Addon +): + """Test check does not create issue when a supported arch is still present.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["arch"] = ["armv7", "amd64"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 0 + + +async def test_check_deprecated_machine(coresys: CoreSys, install_addon_ssh: Addon): + """Test check for installed add-ons using deprecated machine entries.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["machine"] = ["raspberrypi3"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 1 + assert coresys.resolution.issues[0].type is IssueType.DEPRECATED_ARCH_ADDON + assert coresys.resolution.suggestions[0].type is SuggestionType.EXECUTE_REMOVE + + +async def test_check_ignores_mixed_supported_machine( + coresys: CoreSys, install_addon_ssh: Addon +): + """Test check does not create issue when current machine is still supported.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["machine"] = ["raspberrypi3", install_addon_ssh.sys_machine] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 0 + + +async def test_check_ignores_stage_deprecated( + coresys: CoreSys, install_addon_ssh: Addon +): + """Test check does not create arch repair issue for already deprecated add-ons.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["stage"] = AddonStage.DEPRECATED + install_addon_ssh.data["arch"] = ["armv7"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 0 + + +async def test_approve(coresys: CoreSys, install_addon_ssh: Addon): + """Test approve existing deprecated arch addon issues.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + install_addon_ssh.data["arch"] = ["armv7"] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is True + ) + + install_addon_ssh.data["arch"] = ["armv7", "amd64"] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + install_addon_ssh.data["arch"] = ["amd64"] + install_addon_ssh.data["machine"] = ["raspberrypi3"] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is True + ) + + install_addon_ssh.data["machine"] = ["raspberrypi3", install_addon_ssh.sys_machine] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + install_addon_ssh.data["stage"] = AddonStage.DEPRECATED + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + +async def test_did_run(coresys: CoreSys): + """Test that the check ran as expected.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + should_run = deprecated_arch_addon.states + should_not_run = [state for state in CoreState if state not in should_run] + assert should_run == [CoreState.SETUP, CoreState.RUNNING] + assert len(should_not_run) != 0 + + with patch.object( + CheckDeprecatedArchAddon, "run_check", return_value=None + ) as check: + for state in should_run: + await coresys.core.set_state(state) + await deprecated_arch_addon() + check.assert_called_once() + check.reset_mock() + + for state in should_not_run: + await coresys.core.set_state(state) + await deprecated_arch_addon() + check.assert_not_called() + check.reset_mock() diff --git a/tests/resolution/fixup/test_addon_execute_remove.py b/tests/resolution/fixup/test_addon_execute_remove.py index 56a6fc2fe..869c2a1fa 100644 --- a/tests/resolution/fixup/test_addon_execute_remove.py +++ b/tests/resolution/fixup/test_addon_execute_remove.py @@ -37,3 +37,31 @@ async def test_fixup(coresys: CoreSys, install_addon_ssh: Addon): assert len(coresys.resolution.suggestions) == 0 assert len(coresys.resolution.issues) == 0 + + +async def test_fixup_deprecated_arch_addon(coresys: CoreSys, install_addon_ssh: Addon): + """Test fixup for deprecated arch add-on issue.""" + addon_execute_remove = FixupAddonExecuteRemove(coresys) + + coresys.resolution.add_suggestion( + Suggestion( + SuggestionType.EXECUTE_REMOVE, + ContextType.ADDON, + reference=install_addon_ssh.slug, + ) + ) + coresys.resolution.add_issue( + Issue( + IssueType.DEPRECATED_ARCH_ADDON, + ContextType.ADDON, + reference=install_addon_ssh.slug, + ) + ) + + with patch.object(Addon, "uninstall") as uninstall: + await addon_execute_remove() + + assert uninstall.called + + assert len(coresys.resolution.suggestions) == 0 + assert len(coresys.resolution.issues) == 0 diff --git a/tests/store/test_store_manager.py b/tests/store/test_store_manager.py index 4a328a928..736591862 100644 --- a/tests/store/test_store_manager.py +++ b/tests/store/test_store_manager.py @@ -127,8 +127,8 @@ async def test_reload_fails_if_out_of_date(coresys: CoreSys): "config,log", [ ( - {"arch": ["i386"]}, - "Add-on local_ssh not supported on this platform, supported architectures: i386", + {"arch": ["aarch64"]}, + "Add-on local_ssh not supported on this platform, supported architectures: aarch64", ), ( {"machine": ["odroid-n2"]}, @@ -186,8 +186,8 @@ async def test_update_unavailable_addon( "config,log", [ ( - {"arch": ["i386"]}, - "Add-on local_ssh not supported on this platform, supported architectures: i386", + {"arch": ["aarch64"]}, + "Add-on local_ssh not supported on this platform, supported architectures: aarch64", ), ( {"machine": ["odroid-n2"]}, diff --git a/tests/test_arch.py b/tests/test_arch.py index 6e482a38a..2c0dc33b0 100644 --- a/tests/test_arch.py +++ b/tests/test_arch.py @@ -16,7 +16,7 @@ def mock_detect_cpu_fixture(): async def test_machine_not_exits(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" + """Test fallback when machine is missing.""" sys_machine.return_value = None sys_supervisor.arch = "amd64" await coresys.arch.load() @@ -26,7 +26,7 @@ async def test_machine_not_exits(coresys, sys_machine, sys_supervisor): async def test_machine_not_exits_in_db(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" + """Test fallback when machine is unknown.""" sys_machine.return_value = "jedi-master-knight" sys_supervisor.arch = "amd64" await coresys.arch.load() @@ -36,7 +36,7 @@ async def test_machine_not_exits_in_db(coresys, sys_machine, sys_supervisor): async def test_supervisor_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" + """Test supervisor architecture property.""" sys_machine.return_value = None sys_supervisor.arch = "amd64" assert coresys.arch.supervisor == "amd64" @@ -46,36 +46,6 @@ async def test_supervisor_arch(coresys, sys_machine, sys_supervisor): assert coresys.arch.supervisor == "amd64" -async def test_raspberrypi_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" - sys_machine.return_value = "raspberrypi" - sys_supervisor.arch = "armhf" - await coresys.arch.load() - - assert coresys.arch.default == "armhf" - assert coresys.arch.supported == [CpuArch.ARMHF] - - -async def test_raspberrypi2_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi2.""" - sys_machine.return_value = "raspberrypi2" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_raspberrypi3_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi3.""" - sys_machine.return_value = "raspberrypi3" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] - - async def test_raspberrypi3_64_arch(coresys, sys_machine, sys_supervisor): """Test arch for raspberrypi3_64.""" sys_machine.return_value = "raspberrypi3-64" @@ -83,17 +53,7 @@ async def test_raspberrypi3_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_raspberrypi4_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi4.""" - sys_machine.return_value = "raspberrypi4" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_raspberrypi4_64_arch(coresys, sys_machine, sys_supervisor): @@ -103,7 +63,7 @@ async def test_raspberrypi4_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_raspberrypi5_64_arch(coresys, sys_machine, sys_supervisor): @@ -113,7 +73,7 @@ async def test_raspberrypi5_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_yellow_arch(coresys, sys_machine, sys_supervisor): @@ -123,11 +83,11 @@ async def test_yellow_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] assert coresys.arch.is_supported(["aarch64"]) is True - assert coresys.arch.is_supported(["armv7"]) is True - assert coresys.arch.is_supported(["armhf"]) is True - assert coresys.arch.is_supported(["x86_64", "i386"]) is False + assert coresys.arch.is_supported(["fooarch"]) is False + assert coresys.arch.is_supported(["bararch"]) is False + assert coresys.arch.is_supported(["x86_64"]) is False async def test_green_arch(coresys, sys_machine, sys_supervisor): @@ -137,17 +97,7 @@ async def test_green_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_tinker_arch(coresys, sys_machine, sys_supervisor): - """Test arch for tinker.""" - sys_machine.return_value = "tinker" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_c2_arch(coresys, sys_machine, sys_supervisor): @@ -157,7 +107,7 @@ async def test_odroid_c2_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_c4_arch(coresys, sys_machine, sys_supervisor): @@ -167,7 +117,7 @@ async def test_odroid_c4_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_m1_arch(coresys, sys_machine, sys_supervisor): @@ -177,7 +127,7 @@ async def test_odroid_m1_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_n2_arch(coresys, sys_machine, sys_supervisor): @@ -187,17 +137,7 @@ async def test_odroid_n2_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_odroid_xu_arch(coresys, sys_machine, sys_supervisor): - """Test arch for odroid-xu.""" - sys_machine.return_value = "odroid-xu" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_intel_nuc_arch(coresys, sys_machine, sys_supervisor): @@ -207,17 +147,7 @@ async def test_intel_nuc_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "amd64" - assert coresys.arch.supported == [CpuArch.AMD64, CpuArch.I386] - - -async def test_qemux86_arch(coresys, sys_machine, sys_supervisor): - """Test arch for qemux86.""" - sys_machine.return_value = "qemux86" - sys_supervisor.arch = "i386" - await coresys.arch.load() - - assert coresys.arch.default == "i386" - assert coresys.arch.supported == [CpuArch.I386] + assert coresys.arch.supported == [CpuArch.AMD64] async def test_qemux86_64_arch(coresys, sys_machine, sys_supervisor): @@ -227,17 +157,7 @@ async def test_qemux86_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "amd64" - assert coresys.arch.supported == [CpuArch.AMD64, CpuArch.I386] - - -async def test_qemuarm_arch(coresys, sys_machine, sys_supervisor): - """Test arch for qemuarm.""" - sys_machine.return_value = "qemuarm" - sys_supervisor.arch = "armhf" - await coresys.arch.load() - - assert coresys.arch.default == "armhf" - assert coresys.arch.supported == [CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AMD64] async def test_qemuarm_64_arch(coresys, sys_machine, sys_supervisor): @@ -248,16 +168,3 @@ async def test_qemuarm_64_arch(coresys, sys_machine, sys_supervisor): assert coresys.arch.default == "aarch64" assert coresys.arch.supported == [CpuArch.AARCH64] - - -async def test_qemuarm_arch_native_armv7( - coresys, sys_machine, mock_detect_cpu, sys_supervisor -): - """Test arch for qemuarm.""" - sys_machine.return_value = "qemuarm" - sys_supervisor.arch = "armhf" - mock_detect_cpu.return_value = "armv7l" - await coresys.arch.load() - - assert coresys.arch.default == "armhf" - assert coresys.arch.supported == [CpuArch.ARMHF, CpuArch.ARMV7] diff --git a/tests/test_updater.py b/tests/test_updater.py index 6fcd5e5d2..37d7dbb9e 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -84,7 +84,7 @@ async def test_os_update_path( supervisor_internet: AsyncMock, ): """Test OS upgrade path across major versions.""" - coresys.os._board = "rpi4" # pylint: disable=protected-access + coresys.os._board = "rpi4-64" # pylint: disable=protected-access coresys.os._version = AwesomeVersion(version) # pylint: disable=protected-access await coresys.updater.fetch_data() @@ -147,7 +147,7 @@ async def test_load_calls_reload_when_os_board_without_version( ) -> None: """Test load calls reload when OS board exists but no version_hassos_unrestricted.""" # Set up OS board but no version data - coresys.os._board = "rpi4" # pylint: disable=protected-access + coresys.os._board = "rpi4-64" # pylint: disable=protected-access coresys.security.force = True # Mock reload to verify it gets called @@ -162,7 +162,7 @@ async def test_load_skips_reload_when_os_board_with_version( ) -> None: """Test load skips reload when OS board exists and version_hassos_unrestricted is set.""" # Set up OS board and version data - coresys.os._board = "rpi4" # pylint: disable=protected-access + coresys.os._board = "rpi4-64" # pylint: disable=protected-access coresys.security.force = True # Pre-populate version_hassos_unrestricted by setting it directly on the data dict