From 48306a830d37e64b5275f48e25c315e658ee37e6 Mon Sep 17 00:00:00 2001 From: Alberto Planas Date: Mon, 5 Oct 2020 16:24:16 +0200 Subject: [PATCH] zypperpkg: ignore retcode 104 for search() (bsc#1176697) (#270) --- salt/modules/zypperpkg.py | 28 ++++++--- tests/unit/modules/test_zypperpkg.py | 89 +++++++++++++++++++++------- 2 files changed, 90 insertions(+), 27 deletions(-) diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py index c7228bf712..4af29652d9 100644 --- a/salt/modules/zypperpkg.py +++ b/salt/modules/zypperpkg.py @@ -99,6 +99,8 @@ class _Zypper: } LOCK_EXIT_CODE = 7 + NOT_FOUND_EXIT_CODE = 104 + XML_DIRECTIVES = ["-x", "--xmlout"] # ZYPPER_LOCK is not affected by --root ZYPPER_LOCK = "/var/run/zypp.pid" @@ -129,6 +131,7 @@ class _Zypper: self.__no_raise = False self.__refresh = False self.__ignore_repo_failure = False + self.__ignore_not_found = False self.__systemd_scope = False self.__root = None @@ -148,6 +151,9 @@ class _Zypper: # Ignore exit code for 106 (repo is not available) if "no_repo_failure" in kwargs: self.__ignore_repo_failure = kwargs["no_repo_failure"] + # Ignore exit code for 104 (package not found) + if "ignore_not_found" in kwargs: + self.__ignore_not_found = kwargs["ignore_not_found"] if "systemd_scope" in kwargs: self.__systemd_scope = kwargs["systemd_scope"] if "root" in kwargs: @@ -306,6 +312,10 @@ class _Zypper: if self.__root: self.__cmd.extend(["--root", self.__root]) + # Do not consider 104 as a retcode error + if self.__ignore_not_found: + kwargs["success_retcodes"] = [_Zypper.NOT_FOUND_EXIT_CODE] + self.__cmd.extend(args) kwargs["output_loglevel"] = "trace" kwargs["python_shell"] = False @@ -447,9 +457,11 @@ class Wildcard: Get available versions of the package. :return: """ - solvables = self.zypper.nolock.xml.call( - "se", "-xv", self.name - ).getElementsByTagName("solvable") + solvables = ( + self.zypper(ignore_not_found=True) + .nolock.xml.call("se", "-v", self.name) + .getElementsByTagName("solvable") + ) if not solvables: raise CommandExecutionError( "No packages found matching '{}'".format(self.name) @@ -1054,7 +1066,7 @@ def list_repo_pkgs(*args, **kwargs): root = kwargs.get("root") or None for node in ( - __zypper__(root=root) + __zypper__(root=root, ignore_not_found=True) .xml.call("se", "-s", *targets) .getElementsByTagName("solvable") ): @@ -2431,7 +2443,9 @@ def owner(*paths, **kwargs): def _get_visible_patterns(root=None): """Get all available patterns in the repo that are visible.""" patterns = {} - search_patterns = __zypper__(root=root).nolock.xml.call("se", "-t", "pattern") + search_patterns = __zypper__(root=root, ignore_not_found=True).nolock.xml.call( + "se", "-t", "pattern" + ) for element in search_patterns.getElementsByTagName("solvable"): installed = element.getAttribute("status") == "installed" patterns[element.getAttribute("name")] = { @@ -2628,7 +2642,7 @@ def search(criteria, refresh=False, **kwargs): cmd.append(criteria) solvables = ( - __zypper__(root=root) + __zypper__(root=root, ignore_not_found=True) .nolock.noraise.xml.call(*cmd) .getElementsByTagName("solvable") ) @@ -2880,7 +2894,7 @@ def _get_patches(installed_only=False, root=None): """ patches = {} for element in ( - __zypper__(root=root) + __zypper__(root=root, ignore_not_found=True) .nolock.xml.call("se", "-t", "patch") .getElementsByTagName("solvable") ): diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py index 47fca906a7..671adc2779 100644 --- a/tests/unit/modules/test_zypperpkg.py +++ b/tests/unit/modules/test_zypperpkg.py @@ -14,7 +14,7 @@ import salt.utils.files import salt.utils.pkg from salt.exceptions import CommandExecutionError from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, Mock, call, patch +from tests.support.mock import MagicMock, Mock, call, mock_open, patch from tests.support.unit import TestCase @@ -27,7 +27,10 @@ class ZyppCallMock: def __call__(self, *args, **kwargs): # If the call is for a configuration modifier, we return self - if any(i in kwargs for i in ("no_repo_failure", "systemd_scope", "root")): + if any( + i in kwargs + for i in ("no_repo_failure", "ignore_not_found", "systemd_scope", "root") + ): return self return MagicMock(return_value=self.__return_value)() @@ -1801,8 +1804,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) wcard = zypper.Wildcard(_zpr) wcard.name, wcard.version = "libzypp", "*" assert wcard._get_scope_versions(wcard._get_available_versions()) == [ @@ -1824,8 +1828,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) wcard = zypper.Wildcard(_zpr) wcard.name, wcard.version = "libzypp", "16.2.*-2*" assert wcard._get_scope_versions(wcard._get_available_versions()) == [ @@ -1846,8 +1851,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) wcard = zypper.Wildcard(_zpr) wcard.name, wcard.version = "libzypp", "16.2.5*" assert wcard._get_scope_versions(wcard._get_available_versions()) == [ @@ -1867,8 +1873,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) wcard = zypper.Wildcard(_zpr) wcard.name, wcard.version = "libzypp", "*.1" assert wcard._get_scope_versions(wcard._get_available_versions()) == [ @@ -1889,8 +1896,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) assert zypper.Wildcard(_zpr)("libzypp", "16.2.4*") == "16.2.4-19.5" assert zypper.Wildcard(_zpr)("libzypp", "16.2*") == "16.2.5-25.1" assert zypper.Wildcard(_zpr)("libzypp", "*6-*") == "17.2.6-27.9.1" @@ -1909,8 +1917,10 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) + assert zypper.Wildcard(_zpr)("libzypp", None) is None def test_wildcard_to_query_typecheck(self): @@ -1926,8 +1936,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) assert isinstance(zypper.Wildcard(_zpr)("libzypp", "*.1"), str) def test_wildcard_to_query_condition_preservation(self): @@ -1943,8 +1954,9 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) for op in zypper.Wildcard.Z_OP: assert zypper.Wildcard(_zpr)( @@ -1970,8 +1982,10 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ - _zpr = MagicMock() - _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) + __zpr = MagicMock() + __zpr.nolock.xml.call.return_value = minidom.parseString(xmldoc) + _zpr = MagicMock(return_value=__zpr) + with self.assertRaises(CommandExecutionError): for op in [">>", "==", "<<", "+"]: zypper.Wildcard(_zpr)("libzypp", "{}*.1".format(op)) @@ -2063,3 +2077,38 @@ pattern() = package-c""" with patch("salt.modules.zypperpkg.__zypper__", zypper_mock): assert zypper.services_need_restart() == expected zypper_mock(root=None).nolock.call.assert_called_with("ps", "-sss") + + def test_search(self): + """Test zypperpkg.search()""" + xml_mock = MagicMock(return_value=[]) + zypp_mock = MagicMock(return_value=xml_mock) + ZyppCallMock(return_value=xml_mock) + with patch("salt.modules.zypperpkg.__zypper__", zypp_mock): + zypper.search("emacs") + zypp_mock.assert_called_with(root=None, ignore_not_found=True) + xml_mock.nolock.noraise.xml.call.assert_called_with("search", "emacs") + + def test_search_not_found(self): + """Test zypperpkg.search()""" + ret = { + "stdout": "", + "stderr": None, + "retcode": 104, + } + run_all_mock = MagicMock(return_value=ret) + with patch.dict(zypper.__salt__, {"cmd.run_all": run_all_mock}): + self.assertRaises(CommandExecutionError, zypper.search, "vim") + run_all_mock.assert_called_with( + [ + "zypper", + "--non-interactive", + "--xmlout", + "--no-refresh", + "search", + "vim", + ], + success_retcodes=[104], + output_loglevel="trace", + python_shell=False, + env={"ZYPP_READONLY_HACK": "1"}, + ) -- 2.34.1