From 4597475fead117ca084c0135956f47fda36ed954 Mon Sep 17 00:00:00 2001 From: jneo8 Date: Fri, 27 Oct 2023 17:49:22 +0800 Subject: [PATCH] feat: third party resource checksum Check upload resources sha256 hash. --- requirements.txt | 1 + src/check_sum.py | 284 +++++++++++++++++++++++++++++++++ src/hw_tools.py | 24 +++ src/os_platform.py | 66 ++++++++ tests/unit/test_check_sum.py | 34 ++++ tests/unit/test_hw_tools.py | 103 +++++++++++- tests/unit/test_os_platform.py | 52 ++++++ 7 files changed, 556 insertions(+), 8 deletions(-) create mode 100644 src/check_sum.py create mode 100644 src/os_platform.py create mode 100644 tests/unit/test_check_sum.py create mode 100644 tests/unit/test_os_platform.py diff --git a/requirements.txt b/requirements.txt index 6a149980..ade9a108 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ cosl +distro ops >= 2.2.0 jinja2 redfish diff --git a/src/check_sum.py b/src/check_sum.py new file mode 100644 index 00000000..8d3551a4 --- /dev/null +++ b/src/check_sum.py @@ -0,0 +1,284 @@ +# Copyright 2023 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# For further info, check https://github.com/canonical/charmcraft +"""Check sum definition, check functions and related utils.""" +import hashlib +import logging +import typing as t +from dataclasses import dataclass +from pathlib import Path + +from os_platform import UbuntuSeries, get_os_platform + +logger = logging.getLogger(__name__) + + +class ResourceCheckSumError(Exception): + """Rais if check sum does not match.""" + + +@dataclass +class ToolSupportInfo: + """Tool support information for checksum.""" + + version: str + support_series: t.List[UbuntuSeries] + sha256_checksum: str + link: t.Optional[str] = None + desc: str = "" + + +STORCLI_SUPPORT_INFOS: t.List[ToolSupportInfo] = [ + ToolSupportInfo( + version="007.2705.0000.0000", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/1232743397", + desc="MR 7.27", + sha256_checksum="45ff0d3c7fc8b77f64de7de7b3698307971546a6be00982934a19ee44f5d91bb", + ), + ToolSupportInfo( + version="007.2612.0000.0000", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/1232743291", + desc="MR 7.26", + sha256_checksum="5ab2c1b608934626817828ced85e4aeaee7dc97fbd6e3f4fed00b13a95a06e14", + ), + ToolSupportInfo( + version="007.2508.0000.0000", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/1232743203", + desc="MR 7.25", + sha256_checksum="17c3f5292de6491f1388c9305ba65836730614b6defe17039b427fced2f75e0b", + ), + ToolSupportInfo( + version="007.2408.0000.0000", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/1232743081", + desc="MR 7.24", + sha256_checksum="8ecf2d46e253e243c5d169adcd84f2701e52e3815913694f074e80af5a98cbab", + ), + ToolSupportInfo( + version="007.2310.0000.0000", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/Unified_storcli_all_os_7.2309.0000.0000.zip", + desc="MR 7.23", + sha256_checksum="94cbef2ec2ca58700a49e646a7bded3a49ddab4646a9d5d178bc4ccb2996cb73", + ), +] + +PERCCLI_SUPPORT_INFOS: t.List[ToolSupportInfo] = [ + ToolSupportInfo( + version="007.2313.0000.0000", + support_series=[UbuntuSeries.JAMMY, UbuntuSeries.FOCAL], + link="https://www.dell.com/support/home/zh-tw/drivers/driversdetails?driverid=tdghn", + desc="A14", + sha256_checksum="043f7d6235cf125072e95d748cb98f5db42965f218de30f6f72f5503a626e4e3", + ), + ToolSupportInfo( + version="007.1623.0000.0000", + support_series=[UbuntuSeries.FOCAL, UbuntuSeries.BIONIC, UbuntuSeries.XENIAL], + link="https://www.dell.com/support/home/zh-tw/drivers/driversdetails?driverid=j91yg", + desc="A11", + sha256_checksum="e46d955241c932023caf63862cd9dacb2b723b7f944340efb0e5afb6a2681e9d", + ), + ToolSupportInfo( + version="007.1420.0000.0000", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://www.dell.com/support/home/zh-tw/drivers/driversdetails?driverid=n65f1", + desc="A10", + sha256_checksum="8a405000ea592e1d2999313ade07609a7abcfa24d1b9b35bb242bb6aff75a6be", + ), + ToolSupportInfo( + version="007.1327.0000.0000", + support_series=[UbuntuSeries.FOCAL, UbuntuSeries.BIONIC, UbuntuSeries.XENIAL], + link="https://www.dell.com/support/home/zh-tw/drivers/driversdetails?driverid=d6ywp", + desc="A09", + sha256_checksum="53c8ee43808779f8263c25b3cb975d816d207659684f3c7de1df4bbd2447ead4", + ), +] + +SAS2IRCU_SUPPORT_INFOS: t.List[ToolSupportInfo] = [ + ToolSupportInfo( + version="20.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/12351735", + desc="P20, linux_x86", + sha256_checksum="37467826d0b22aad47287efe70bb34e47f475d70e9b1b64cbd63f57607701e73", + ), + ToolSupportInfo( + version="19.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/12351734", + desc="P19, linux_x86", + sha256_checksum="4baaec21865973c0a6da617e37850cc27512715e6ab22df18b1f67d068e5095c", + ), + ToolSupportInfo( + version="18.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/12351733", + desc="P18, linux_x86", + sha256_checksum="b6ed72275066e80ebe9813cd15f1d019eba9daddbd9dfd8ad426da78801f15d8", + ), + ToolSupportInfo( + version="17.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/12351732", + desc="P17, linux_x86", + sha256_checksum="07e9236b99bbe4a3ae6148f8668e1ce0331d83c2fcb0c4841d000454c6200c1f", + ), + ToolSupportInfo( + version="16.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/12351731", + desc="P16, linux_x86", + sha256_checksum="a8653117067847042bb83e7b51f02d8f2db94e91ce95842efea0dffcb655c966", + ), +] + +SAS3IRCU_SUPPORT_INFOS: t.List[ToolSupportInfo] = [ + ToolSupportInfo( + version="16.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/SAS3IRCU_P15.zip", + desc="P15, linux_x86", + sha256_checksum="f150eb37bb332668949a3eccf9636e0e03f874aecd17a39d586082c6be1386bd", + ), + ToolSupportInfo( + version="15.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/SAS3IRCU_P14.zip", + desc="P14, linux_x86", + sha256_checksum="5825b90964d1950551e5ed5100ddf1141360b0acf8dd3c6ddb4fe5874d6bbabb", + ), + ToolSupportInfo( + version="14.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/SAS3IRCU_P13.zip", + desc="P13, linux_x86", + sha256_checksum="1ce45e57efa0a2d8c5c3d61a0950ab7e950a317aff3155fc1831099e19274c32", + ), + ToolSupportInfo( + version="13.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/SAS3IRCU_P12.zip", + desc="P12, linux_x86", + sha256_checksum="cb4010a3d2bc4f9b75859a0c599d889f9fb847e4dfc88abf74082a13b9490a59", + ), + ToolSupportInfo( + version="12.00.00.00", + support_series=[ + UbuntuSeries.JAMMY, + UbuntuSeries.FOCAL, + UbuntuSeries.BIONIC, + UbuntuSeries.XENIAL, + ], + link="https://docs.broadcom.com/docs/SAS3IRCU_P11.zip", + desc="P11, linux_x86", + sha256_checksum="458d51b030468901fc8a207088070e6ce82db34b181d9190c8f849605f1b9b6d", + ), +] + + +def check_file_sum(infos: t.List[ToolSupportInfo], path: Path) -> bool: + """Check if file match the checksum.""" + os_platform = get_os_platform() + + support_checksums = [] + for info in infos: + if os_platform.series in info.support_series: + support_checksums.append(info.sha256_checksum) + + with open(path, "rb") as f: + sha256_hash = hashlib.sha256(f.read()).hexdigest() + + if sha256_hash in support_checksums: + return True + logger.warning("Check sum fail, path: %s hash: %s", path, sha256_hash) + return False diff --git a/src/hw_tools.py b/src/hw_tools.py index f54618f1..b0a6efed 100644 --- a/src/hw_tools.py +++ b/src/hw_tools.py @@ -16,6 +16,14 @@ from redfish import redfish_client from redfish.rest.v1 import InvalidCredentialsError, RetriesExhaustedError, SessionCreationError +from check_sum import ( + PERCCLI_SUPPORT_INFOS, + SAS2IRCU_SUPPORT_INFOS, + SAS3IRCU_SUPPORT_INFOS, + STORCLI_SUPPORT_INFOS, + ResourceCheckSumError, + check_file_sum, +) from config import SNAP_COMMON, TOOLS_DIR, TPR_RESOURCES, HWTool, StorageVendor, SystemVendor from hardware import SUPPORTED_STORAGES, get_bmc_address, lshw from keys import HP_KEYS @@ -144,6 +152,8 @@ def install(self, path: Path) -> None: """Install storcli.""" if not check_file_size(path): raise ResourceFileSizeZeroError(tool=self._name, path=path) + if not check_file_sum(STORCLI_SUPPORT_INFOS, path): + raise ResourceCheckSumError install_deb(self.name, path) symlink(src=self.origin_path, dst=self.symlink_bin) @@ -165,6 +175,8 @@ def install(self, path: Path) -> None: """Install perccli.""" if not check_file_size(path): raise ResourceFileSizeZeroError(tool=self._name, path=path) + if not check_file_sum(PERCCLI_SUPPORT_INFOS, path): + raise ResourceCheckSumError install_deb(self.name, path) symlink(src=self.origin_path, dst=self.symlink_bin) @@ -185,6 +197,8 @@ def install(self, path: Path) -> None: """Install sas2ircu.""" if not check_file_size(path): raise ResourceFileSizeZeroError(tool=self._name, path=path) + if not check_file_sum(SAS2IRCU_SUPPORT_INFOS, path): + raise ResourceCheckSumError make_executable(path) symlink(src=path, dst=self.symlink_bin) @@ -200,6 +214,15 @@ class SAS3IRCUStrategy(SAS2IRCUStrategy): _name = HWTool.SAS3IRCU symlink_bin = TOOLS_DIR / HWTool.SAS3IRCU.value + def install(self, path: Path) -> None: + """Install sas3ircu.""" + if not check_file_size(path): + raise ResourceFileSizeZeroError(tool=self._name, path=path) + if not check_file_sum(SAS3IRCU_SUPPORT_INFOS, path): + raise ResourceCheckSumError + make_executable(path) + symlink(src=path, dst=self.symlink_bin) + class SSACLIStrategy(APTStrategyABC): """Strategy for install ssacli.""" @@ -444,6 +467,7 @@ def install(self, resources: Resources) -> t.Tuple[bool, str]: ResourceFileSizeZeroError, OSError, apt.PackageError, + ResourceCheckSumError, ) as e: logger.warning("Strategy %s install fail: %s", strategy, e) fail_strategies.append(strategy.name) diff --git a/src/os_platform.py b/src/os_platform.py new file mode 100644 index 00000000..8608bb8f --- /dev/null +++ b/src/os_platform.py @@ -0,0 +1,66 @@ +# Copyright 2023 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# For further info, check https://github.com/canonical/charmcraft +"""Platform-related Charmcraft utilities.""" +import dataclasses +import platform +import typing as t +from enum import Enum + +import distro + + +class UbuntuSeries(str, Enum): + """Ubuntu Series.""" + + JAMMY = "22.04" + FOCAL = "20.04" + BIONIC = "18.04" + XENIAL = "16.04" + + +@dataclasses.dataclass +class OSPlatform: + """Description of an operating system platform.""" + + system: str + release: str + machine: str + + @property + def series(self) -> t.Optional[UbuntuSeries]: + """Return series base on system and release.""" + if self.system == "ubuntu": + for series in UbuntuSeries: + if series == self.release: + return series + return None + + +def get_os_platform() -> OSPlatform: + """Determine a system/release combo for an OS using /etc/os-release if available.""" + system = platform.system() + release = platform.release() + machine = platform.machine() + + if system == "Linux": + info = distro.info() + system = info.get("id", system) + # Treat Ubuntu derivatives as Ubuntu, as they should be compatible. + if system != "ubuntu" and "ubuntu" in info.get("like", "").split(): + system = "ubuntu" + release = info.get("version", release) + + return OSPlatform(system=system, release=release, machine=machine) diff --git a/tests/unit/test_check_sum.py b/tests/unit/test_check_sum.py new file mode 100644 index 00000000..4cef38e2 --- /dev/null +++ b/tests/unit/test_check_sum.py @@ -0,0 +1,34 @@ +from unittest import mock + +from check_sum import PERCCLI_SUPPORT_INFOS, check_file_sum +from os_platform import OSPlatform + + +class TestCheckFileSum: + @mock.patch( + "check_sum.get_os_platform", + return_value=OSPlatform( + system="ubuntu", + release="20.04", + machine="x86_64", + ), + ) + @mock.patch("check_sum.hashlib.sha256") + def test_check_file_sum(self, mock_sha256, mock_get_os_platform, tmp_path): + mock_sha256.return_value = mock.Mock() + mock_sha256.return_value.hexdigest.return_value = ( + "53c8ee43808779f8263c25b3cb975d816d207659684f3c7de1df4bbd2447ead4" + ) + + target = tmp_path / "perccli" + target.write_text("fake file") + + ok = check_file_sum(PERCCLI_SUPPORT_INFOS, target) + assert ok + + def test_check_file_sum_fail(self, tmp_path): + target = tmp_path / "perccli" + target.write_text("fake file") + + ok = check_file_sum(PERCCLI_SUPPORT_INFOS, target) + assert not ok diff --git a/tests/unit/test_hw_tools.py b/tests/unit/test_hw_tools.py index 3705f9e7..8f69f07c 100644 --- a/tests/unit/test_hw_tools.py +++ b/tests/unit/test_hw_tools.py @@ -11,6 +11,12 @@ from ops.model import ModelError from charm import HardwareObserverCharm +from check_sum import ( + PERCCLI_SUPPORT_INFOS, + SAS2IRCU_SUPPORT_INFOS, + SAS3IRCU_SUPPORT_INFOS, + STORCLI_SUPPORT_INFOS, +) from config import SNAP_COMMON, TOOLS_DIR, TPR_RESOURCES, HWTool, StorageVendor, SystemVendor from hw_tools import ( APTStrategyABC, @@ -18,6 +24,7 @@ InvalidCredentialsError, IPMIStrategy, PercCLIStrategy, + ResourceCheckSumError, ResourceFileSizeZeroError, RetriesExhaustedError, SAS2IRCUStrategy, @@ -318,10 +325,13 @@ def test_11_check_missing_resources_zero_size_resources(self, check_file_size): class TestStorCLIStrategy(unittest.TestCase): + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.check_file_size", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.install_deb") - def test_install(self, mock_install_deb, mock_symlink, _): + def test_install( + self, mock_install_deb, mock_symlink, mock_check_file_size, mock_check_file_sum + ): strategy = StorCLIStrategy() strategy.install(path="path-a") mock_install_deb.assert_called_with("storcli", "path-a") @@ -330,13 +340,30 @@ def test_install(self, mock_install_deb, mock_symlink, _): dst=TOOLS_DIR / "storcli", ) + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.install_deb") - def test_install_empty_resource(self, mock_install_deb, mock_symlink): + def test_install_empty_resource(self, mock_install_deb, mock_symlink, mock_check_file_sum): strategy = StorCLIStrategy() with pytest.raises(ResourceFileSizeZeroError): strategy.install(get_mock_path(0)) + mock_check_file_sum.assert_not_called() + mock_install_deb.assert_not_called() + mock_symlink.assert_not_called() + + @mock.patch("hw_tools.check_file_sum", return_value=False) + @mock.patch("hw_tools.check_file_size", return_value=True) + @mock.patch("hw_tools.symlink") + @mock.patch("hw_tools.install_deb") + def test_install_checksum_fail( + self, mock_install_deb, mock_symlink, mock_check_file_size, mock_check_file_sum + ): + mock_path = mock.Mock() + strategy = StorCLIStrategy() + with pytest.raises(ResourceCheckSumError): + strategy.install(mock_path) + mock_check_file_sum.assert_called_with(STORCLI_SUPPORT_INFOS, mock_path) mock_install_deb.assert_not_called() mock_symlink.assert_not_called() @@ -400,25 +427,45 @@ def test_check_file_size_zero(self): class TestSAS2IRCUStrategy(unittest.TestCase): + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.check_file_size", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.make_executable") - def test_install(self, mock_make_executable, mock_symlink, _): + def test_install( + self, mock_make_executable, mock_symlink, mock_check_file_size, mock_check_file_sum + ): strategy = SAS2IRCUStrategy() strategy.install(path="path-a") mock_make_executable.assert_called_with("path-a") mock_symlink.assert_called_with(src="path-a", dst=TOOLS_DIR / "sas2ircu") + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.make_executable") - def test_install_empty_resource(self, mock_make_executable, mock_symlink): + def test_install_empty_resource(self, mock_make_executable, mock_symlink, mock_check_file_sum): strategy = SAS2IRCUStrategy() with pytest.raises(ResourceFileSizeZeroError): strategy.install(get_mock_path(0)) + mock_check_file_sum.assert_not_called() mock_make_executable.assert_not_called() mock_symlink.assert_not_called() + @mock.patch("hw_tools.check_file_sum", return_value=False) + @mock.patch("hw_tools.check_file_size", return_value=True) + @mock.patch("hw_tools.symlink") + @mock.patch("hw_tools.install_deb") + def test_install_checksum_fail( + self, mock_install_deb, mock_symlink, mock_check_file_size, mock_check_file_sum + ): + mock_path = mock.Mock() + strategy = SAS2IRCUStrategy() + with pytest.raises(ResourceCheckSumError): + strategy.install(mock_path) + mock_check_file_sum.assert_called_with(SAS2IRCU_SUPPORT_INFOS, mock_path) + mock_install_deb.assert_not_called() + mock_symlink.assert_not_called() + def test_remove(self): strategy = SAS2IRCUStrategy() with mock.patch.object(strategy, "symlink_bin") as mock_symlink_bin: @@ -427,25 +474,45 @@ def test_remove(self): class TestSAS3IRCUStrategy(unittest.TestCase): + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.check_file_size", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.make_executable") - def test_install(self, mock_make_executable, mock_symlink, _): + def test_install( + self, mock_make_executable, mock_symlink, mock_check_file_size, mock_check_file_sum + ): strategy = SAS3IRCUStrategy() strategy.install(path="path-a") mock_make_executable.assert_called_with("path-a") mock_symlink.assert_called_with(src="path-a", dst=TOOLS_DIR / "sas3ircu") + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.make_executable") - def test_install_empty_resource(self, mock_make_executable, mock_symlink): + def test_install_empty_resource(self, mock_make_executable, mock_symlink, mock_check_file_sum): strategy = SAS3IRCUStrategy() with pytest.raises(ResourceFileSizeZeroError): strategy.install(get_mock_path(0)) + mock_check_file_sum.assert_not_called() mock_make_executable.assert_not_called() mock_symlink.assert_not_called() + @mock.patch("hw_tools.check_file_sum", return_value=False) + @mock.patch("hw_tools.check_file_size", return_value=True) + @mock.patch("hw_tools.symlink") + @mock.patch("hw_tools.install_deb") + def test_install_checksum_fail( + self, mock_install_deb, mock_symlink, mock_check_file_size, mock_check_file_sum + ): + mock_path = mock.Mock() + strategy = SAS3IRCUStrategy() + with pytest.raises(ResourceCheckSumError): + strategy.install(mock_path) + mock_check_file_sum.assert_called_with(SAS3IRCU_SUPPORT_INFOS, mock_path) + mock_install_deb.assert_not_called() + mock_symlink.assert_not_called() + def test_remove(self): strategy = SAS3IRCUStrategy() with mock.patch.object(strategy, "symlink_bin") as mock_symlink_bin: @@ -454,10 +521,13 @@ def test_remove(self): class TestPercCLIStrategy(unittest.TestCase): + @mock.patch("hw_tools.check_file_sum", return_value=True) @mock.patch("hw_tools.check_file_size", return_value=True) @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.install_deb") - def test_install(self, mock_install_deb, mock_symlink, _): + def test_install( + self, mock_install_deb, mock_symlink, mock_check_file_size, mock_check_file_sum + ): strategy = PercCLIStrategy() strategy.install(path="path-a") mock_install_deb.assert_called_with("perccli", "path-a") @@ -466,9 +536,10 @@ def test_install(self, mock_install_deb, mock_symlink, _): dst=TOOLS_DIR / "perccli", ) + @mock.patch("hw_tools.check_file_sum") @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.install_deb") - def test_install_empty_resource(self, mock_install_deb, mock_symlink): + def test_install_empty_resource(self, mock_install_deb, mock_symlink, mock_check_sum): mock_path = mock.Mock() mock_path_stat = mock.Mock() mock_path.stat.return_value = mock_path_stat @@ -480,6 +551,22 @@ def test_install_empty_resource(self, mock_install_deb, mock_symlink): mock_install_deb.assert_not_called() mock_symlink.assert_not_called() + mock_check_sum.assert_not_called() + + @mock.patch("hw_tools.check_file_sum", return_value=False) + @mock.patch("hw_tools.check_file_size", return_value=True) + @mock.patch("hw_tools.symlink") + @mock.patch("hw_tools.install_deb") + def test_install_checksum_fail( + self, mock_install_deb, mock_symlink, mock_check_file_size, mock_check_file_sum + ): + mock_path = mock.Mock() + strategy = PercCLIStrategy() + with pytest.raises(ResourceCheckSumError): + strategy.install(mock_path) + mock_check_file_sum.assert_called_with(PERCCLI_SUPPORT_INFOS, mock_path) + mock_install_deb.assert_not_called() + mock_symlink.assert_not_called() @mock.patch("hw_tools.symlink") @mock.patch("hw_tools.remove_deb") diff --git a/tests/unit/test_os_platform.py b/tests/unit/test_os_platform.py new file mode 100644 index 00000000..5ce388c6 --- /dev/null +++ b/tests/unit/test_os_platform.py @@ -0,0 +1,52 @@ +from unittest.mock import patch + +import pytest + +from os_platform import OSPlatform, UbuntuSeries, get_os_platform + + +@pytest.mark.parametrize("system", ["Windows", "Darwin", "Java", ""]) +@pytest.mark.parametrize("release", ["NT", "Sparkling", "Jaguar", "0.0", ""]) +@pytest.mark.parametrize( + "machine", + [ + "AMD64", + "x86_86", + "arm64", + "riscv64", + ], +) +def test_get_os_platform_non_linux(system, release, machine): + """Get platform from a patched Windows machine.""" + with patch("platform.system", return_value=system): + with patch("platform.release", return_value=release): + with patch("platform.machine", return_value=machine): + result = get_os_platform() + assert result == OSPlatform(system=system, release=release, machine=machine) + + +@patch("os_platform.distro") +def test_get_os_platform_non_linux_distro_info(mock_distro): + mock_distro.info.return_value = {"like": "ubuntu abc def", "version": "22.04"} + with patch("platform.system", return_value="Linux"): + with patch("platform.release", return_value="22.04"): + with patch("platform.machine", return_value="x86_86"): + result = get_os_platform() + assert result == OSPlatform(system="ubuntu", release="22.04", machine="x86_86") + assert result.series == UbuntuSeries.JAMMY + + +@pytest.mark.parametrize("system", ["ubuntu"]) +@pytest.mark.parametrize( + "release,series", + [("22.04", UbuntuSeries.JAMMY), ("20.04", UbuntuSeries.FOCAL), ("NR", None)], +) +@pytest.mark.parametrize("machine", ["AMD64", "x86_86", "arm64", "riscv64"]) +def test_os_platform_series(system, release, series, machine): + """Get platform from a patched Windows machine.""" + with patch("platform.system", return_value=system): + with patch("platform.release", return_value=release): + with patch("platform.machine", return_value=machine): + result = get_os_platform() + assert result == OSPlatform(system=system, release=release, machine=machine) + assert result.series == series