File zypperpkg-ignore-retcode-104-for-search-bsc-1176697-.patch of Package venv-salt-minion

From deaee93b2f83f1524ec136afc1a5198b33d293d2 Mon Sep 17 00:00:00 2001
From: Alberto Planas <aplanas@suse.com>
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 | 87 ++++++++++++++++++++++------
 2 files changed, 89 insertions(+), 26 deletions(-)

diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py
index d8220a1fdd..4bb10f445a 100644
--- a/salt/modules/zypperpkg.py
+++ b/salt/modules/zypperpkg.py
@@ -103,6 +103,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"
@@ -134,6 +136,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
 
@@ -153,6 +156,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:
@@ -333,6 +339,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
@@ -479,9 +489,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)
@@ -1086,7 +1098,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")
     ):
@@ -2556,7 +2568,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")] = {
@@ -2753,7 +2767,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")
     )
@@ -3005,7 +3019,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 22137a2544..5e4c967520 100644
--- a/tests/unit/modules/test_zypperpkg.py
+++ b/tests/unit/modules/test_zypperpkg.py
@@ -28,7 +28,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)()
 
@@ -1662,8 +1665,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="(System Packages)"/>
         </solvable-list></search-result></stream>
                 """
-        _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()) == [
@@ -1685,8 +1689,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         </solvable-list></search-result></stream>
         """
 
-        _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()) == [
@@ -1707,8 +1712,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         </solvable-list></search-result></stream>
         """
 
-        _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()) == [
@@ -1728,8 +1734,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         </solvable-list></search-result></stream>
         """
 
-        _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()) == [
@@ -1750,8 +1757,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         <solvable status="other-version" name="libzypp" kind="package" edition="17.2.6-27.9.1" arch="x86_64" repository="foo"/>
         </solvable-list></search-result></stream>
         """
-        _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"
@@ -1770,8 +1778,10 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         <solvable status="other-version" name="libzypp" kind="package" edition="17.2.6-27.9.1" arch="x86_64" repository="foo"/>
         </solvable-list></search-result></stream>
         """
-        _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):
@@ -1787,8 +1797,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         <solvable status="other-version" name="libzypp" kind="package" edition="17.2.6-27.9.1" arch="x86_64" repository="foo"/>
         </solvable-list></search-result></stream>
         """
-        _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):
@@ -1804,8 +1815,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         <solvable status="other-version" name="libzypp" kind="package" edition="17.2.6-27.9.1" arch="x86_64" repository="foo"/>
         </solvable-list></search-result></stream>
         """
-        _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)(
@@ -1831,8 +1843,10 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
         <solvable status="other-version" name="libzypp" kind="package" edition="17.2.6-27.9.1" arch="x86_64" repository="foo"/>
         </solvable-list></search-result></stream>
         """
-        _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))
@@ -1958,3 +1972,38 @@ pattern() = package-c"""
                 self.assertFalse(zypper.__zypper__._is_rpm_lock())
                 self.assertEqual(lockf_mock.call_count, 2)
             zypper.__zypper__._reset()
+
+    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": "<?xml version='1.0'?><stream></stream>",
+            "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.39.2


openSUSE Build Service is sponsored by