diff --git a/changelog/62029.fixed b/changelog/62029.fixed new file mode 100644 index 000000000000..b468d5a14989 --- /dev/null +++ b/changelog/62029.fixed @@ -0,0 +1 @@ +Normalize package names once on using pkg.installed/removed with yum to make it possible to install packages with the name containing a part similar to a name of architecture. diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index b5bcede2df1c..bc25648a7dd9 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -1411,6 +1411,12 @@ def install( .. versionadded:: 2014.7.0 + split_arch : True + If set to False it prevents package name normalization more strict way + than ``normalize`` set to ``False`` does. + + .. versionadded:: 3006 + diff_attr: If a list of package attributes is specified, returned value will contain them, eg.:: @@ -1458,7 +1464,12 @@ def install( try: pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( - name, pkgs, sources, saltenv=saltenv, normalize=normalize, **kwargs + name, + pkgs, + sources, + saltenv=saltenv, + normalize=normalize and kwargs.get("split_arch", True), + **kwargs ) except MinionError as exc: raise CommandExecutionError(exc) @@ -1610,7 +1621,10 @@ def install( except ValueError: pass else: - if archpart in salt.utils.pkg.rpm.ARCHES: + if archpart in salt.utils.pkg.rpm.ARCHES and ( + archpart != __grains__["osarch"] + or kwargs.get("split_arch", True) + ): arch = "." + archpart pkgname = namepart @@ -2114,6 +2128,11 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 .. versionadded:: 0.16.0 + split_arch : True + If set to False it prevents package name normalization by removing arch. + + .. versionadded:: 3006 + Returns a dict containing the changes. @@ -2162,11 +2181,13 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 arch = "" pkgname = target try: - namepart, archpart = target.rsplit(".", 1) + namepart, archpart = pkgname.rsplit(".", 1) except ValueError: pass else: - if archpart in salt.utils.pkg.rpm.ARCHES: + if archpart in salt.utils.pkg.rpm.ARCHES and ( + archpart != __grains__["osarch"] or kwargs.get("split_arch", True) + ): arch = "." + archpart pkgname = namepart # Since we don't always have the arch info, epoch information has to parsed out. But diff --git a/salt/states/pkg.py b/salt/states/pkg.py index cde64603e866..8a3ca425aad7 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -1876,6 +1876,7 @@ def installed( normalize=normalize, update_holds=update_holds, ignore_epoch=ignore_epoch, + split_arch=False, **kwargs ) except CommandExecutionError as exc: @@ -2943,7 +2944,7 @@ def _uninstall( } changes = __salt__["pkg.{}".format(action)]( - name, pkgs=pkgs, version=version, **kwargs + name, pkgs=pkgs, version=version, split_arch=False, **kwargs ) new = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) failed = [] diff --git a/tests/pytests/unit/states/test_pkg.py b/tests/pytests/unit/states/test_pkg.py index 1ee3880fefbb..b852f27b008b 100644 --- a/tests/pytests/unit/states/test_pkg.py +++ b/tests/pytests/unit/states/test_pkg.py @@ -3,6 +3,8 @@ import pytest import salt.modules.beacons as beaconmod +import salt.modules.pkg_resource as pkg_resource +import salt.modules.yumpkg as yumpkg import salt.states.beacon as beaconstate import salt.states.pkg as pkg import salt.utils.state as state_utils @@ -18,7 +20,7 @@ def configure_loader_modules(): pkg: { "__env__": "base", "__salt__": {}, - "__grains__": {"os": "CentOS"}, + "__grains__": {"os": "CentOS", "os_family": "RedHat"}, "__opts__": {"test": False, "cachedir": ""}, "__instance_id__": "", "__low__": {}, @@ -26,6 +28,15 @@ def configure_loader_modules(): }, beaconstate: {"__salt__": {}, "__opts__": {}}, beaconmod: {"__salt__": {}, "__opts__": {}}, + pkg_resource: { + "__salt__": {}, + "__grains__": {"os": "CentOS", "os_family": "RedHat"}, + }, + yumpkg: { + "__salt__": {}, + "__grains__": {"osarch": "x86_64", "osmajorrelease": 7}, + "__opts__": {}, + }, } @@ -727,3 +738,278 @@ def pkg_unhold(name, pkgs=None, *_args, **__kwargs): hold_mock.assert_not_called() unhold_mock.assert_any_call(name="held-test", pkgs=["baz"]) unhold_mock.assert_any_call(name="held-test", pkgs=["bar"]) + + +def test_installed_with_single_normalize(): + """ + Test pkg.installed with preventing multiple package name normalisation + """ + + list_no_weird_installed = { + "pkga": "1.0.1", + "pkgb": "1.0.2", + "pkgc": "1.0.3", + } + list_no_weird_installed_ver_list = { + "pkga": ["1.0.1"], + "pkgb": ["1.0.2"], + "pkgc": ["1.0.3"], + } + list_with_weird_installed = { + "pkga": "1.0.1", + "pkgb": "1.0.2", + "pkgc": "1.0.3", + "weird-name-1.2.3-1234.5.6.test7tst.x86_64": "20220214-2.1", + } + list_with_weird_installed_ver_list = { + "pkga": ["1.0.1"], + "pkgb": ["1.0.2"], + "pkgc": ["1.0.3"], + "weird-name-1.2.3-1234.5.6.test7tst.x86_64": ["20220214-2.1"], + } + list_pkgs = MagicMock( + side_effect=[ + # For the package with version specified + list_no_weird_installed_ver_list, + {}, + list_no_weird_installed, + list_no_weird_installed_ver_list, + list_with_weird_installed, + list_with_weird_installed_ver_list, + # For the package with no version specified + list_no_weird_installed_ver_list, + {}, + list_no_weird_installed, + list_no_weird_installed_ver_list, + list_with_weird_installed, + list_with_weird_installed_ver_list, + ] + ) + + salt_dict = { + "pkg.install": yumpkg.install, + "pkg.list_pkgs": list_pkgs, + "pkg.normalize_name": yumpkg.normalize_name, + "pkg_resource.version_clean": pkg_resource.version_clean, + "pkg_resource.parse_targets": pkg_resource.parse_targets, + } + + with patch("salt.modules.yumpkg.list_pkgs", list_pkgs), patch( + "salt.modules.yumpkg.version_cmp", MagicMock(return_value=0) + ), patch( + "salt.modules.yumpkg._call_yum", MagicMock(return_value={"retcode": 0}) + ) as call_yum_mock, patch.dict( + pkg.__salt__, salt_dict + ), patch.dict( + pkg_resource.__salt__, salt_dict + ), patch.dict( + yumpkg.__salt__, salt_dict + ), patch.dict( + yumpkg.__grains__, {"os": "CentOS", "osarch": "x86_64", "osmajorrelease": 7} + ), patch.object( + yumpkg, "list_holds", MagicMock() + ): + + expected = { + "weird-name-1.2.3-1234.5.6.test7tst.x86_64": { + "old": "", + "new": "20220214-2.1", + } + } + ret = pkg.installed( + "test_install", + pkgs=[{"weird-name-1.2.3-1234.5.6.test7tst.x86_64.noarch": "20220214-2.1"}], + ) + call_yum_mock.assert_called_once() + assert ( + "weird-name-1.2.3-1234.5.6.test7tst.x86_64-20220214-2.1" + in call_yum_mock.mock_calls[0].args[0] + ) + assert ret["result"] + assert ret["changes"] == expected + + call_yum_mock.reset_mock() + + ret = pkg.installed( + "test_install", + pkgs=["weird-name-1.2.3-1234.5.6.test7tst.x86_64.noarch"], + ) + call_yum_mock.assert_called_once() + assert ( + "weird-name-1.2.3-1234.5.6.test7tst.x86_64" + in call_yum_mock.mock_calls[0].args[0] + ) + assert ret["result"] + assert ret["changes"] == expected + + +def test_removed_with_single_normalize(): + """ + Test pkg.removed with preventing multiple package name normalisation + """ + + list_no_weird_installed = { + "pkga": "1.0.1", + "pkgb": "1.0.2", + "pkgc": "1.0.3", + } + list_no_weird_installed_ver_list = { + "pkga": ["1.0.1"], + "pkgb": ["1.0.2"], + "pkgc": ["1.0.3"], + } + list_with_weird_installed = { + "pkga": "1.0.1", + "pkgb": "1.0.2", + "pkgc": "1.0.3", + "weird-name-1.2.3-1234.5.6.test7tst.x86_64": "20220214-2.1", + } + list_with_weird_installed_ver_list = { + "pkga": ["1.0.1"], + "pkgb": ["1.0.2"], + "pkgc": ["1.0.3"], + "weird-name-1.2.3-1234.5.6.test7tst.x86_64": ["20220214-2.1"], + } + list_pkgs = MagicMock( + side_effect=[ + # For the package with version specified + list_with_weird_installed_ver_list, + list_with_weird_installed, + list_no_weird_installed, + list_no_weird_installed_ver_list, + # For the package with no version specified + list_with_weird_installed_ver_list, + list_with_weird_installed, + list_no_weird_installed, + list_no_weird_installed_ver_list, + ] + ) + + salt_dict = { + "pkg.remove": yumpkg.remove, + "pkg.list_pkgs": list_pkgs, + "pkg.normalize_name": yumpkg.normalize_name, + "pkg_resource.parse_targets": pkg_resource.parse_targets, + "pkg_resource.version_clean": pkg_resource.version_clean, + } + + with patch("salt.modules.yumpkg.list_pkgs", list_pkgs), patch( + "salt.modules.yumpkg.version_cmp", MagicMock(return_value=0) + ), patch( + "salt.modules.yumpkg._call_yum", MagicMock(return_value={"retcode": 0}) + ) as call_yum_mock, patch.dict( + pkg.__salt__, salt_dict + ), patch.dict( + pkg_resource.__salt__, salt_dict + ), patch.dict( + yumpkg.__salt__, salt_dict + ): + + expected = { + "weird-name-1.2.3-1234.5.6.test7tst.x86_64": { + "old": "20220214-2.1", + "new": "", + } + } + ret = pkg.removed( + "test_remove", + pkgs=[{"weird-name-1.2.3-1234.5.6.test7tst.x86_64.noarch": "20220214-2.1"}], + ) + call_yum_mock.assert_called_once() + assert ( + "weird-name-1.2.3-1234.5.6.test7tst.x86_64-20220214-2.1" + in call_yum_mock.mock_calls[0].args[0] + ) + assert ret["result"] + assert ret["changes"] == expected + + call_yum_mock.reset_mock() + + ret = pkg.removed( + "test_remove", + pkgs=["weird-name-1.2.3-1234.5.6.test7tst.x86_64.noarch"], + ) + call_yum_mock.assert_called_once() + assert ( + "weird-name-1.2.3-1234.5.6.test7tst.x86_64" + in call_yum_mock.mock_calls[0].args[0] + ) + assert ret["result"] + assert ret["changes"] == expected + + +def test_installed_with_single_normalize_32bit(): + """ + Test pkg.installed of 32bit package with preventing multiple package name normalisation + """ + + list_no_weird_installed = { + "pkga": "1.0.1", + "pkgb": "1.0.2", + "pkgc": "1.0.3", + } + list_no_weird_installed_ver_list = { + "pkga": ["1.0.1"], + "pkgb": ["1.0.2"], + "pkgc": ["1.0.3"], + } + list_with_weird_installed = { + "pkga": "1.0.1", + "pkgb": "1.0.2", + "pkgc": "1.0.3", + "xz-devel.i686": "1.2.3", + } + list_with_weird_installed_ver_list = { + "pkga": ["1.0.1"], + "pkgb": ["1.0.2"], + "pkgc": ["1.0.3"], + "xz-devel.i686": ["1.2.3"], + } + list_pkgs = MagicMock( + side_effect=[ + list_no_weird_installed_ver_list, + {}, + list_no_weird_installed, + list_no_weird_installed_ver_list, + list_with_weird_installed, + list_with_weird_installed, + list_with_weird_installed_ver_list, + ] + ) + + salt_dict = { + "pkg.install": yumpkg.install, + "pkg.list_pkgs": list_pkgs, + "pkg.normalize_name": yumpkg.normalize_name, + "pkg_resource.version_clean": pkg_resource.version_clean, + "pkg_resource.parse_targets": pkg_resource.parse_targets, + } + + with patch("salt.modules.yumpkg.list_pkgs", list_pkgs), patch( + "salt.modules.yumpkg.version_cmp", MagicMock(return_value=0) + ), patch( + "salt.modules.yumpkg._call_yum", MagicMock(return_value={"retcode": 0}) + ) as call_yum_mock, patch.dict( + pkg.__salt__, salt_dict + ), patch.dict( + pkg_resource.__salt__, salt_dict + ), patch.dict( + yumpkg.__salt__, salt_dict + ), patch.dict( + yumpkg.__grains__, {"os": "CentOS", "osarch": "x86_64", "osmajorrelease": 7} + ): + + expected = { + "xz-devel.i686": { + "old": "", + "new": "1.2.3", + } + } + ret = pkg.installed( + "test_install", + pkgs=["xz-devel.i686"], + ) + call_yum_mock.assert_called_once() + assert "xz-devel.i686" in call_yum_mock.mock_calls[0].args[0] + assert ret["result"] + assert ret["changes"] == expected