We have some news to share for the request index beta feature. We’ve added more options to sort your requests, counters to the individual filters and documentation for the search functionality. Checkout the blog post for more details.

File zypper-pkgrepo-alreadyconfigured-585.patch of Package salt

From 6b6ba4bdbd4b4c52a46bf3d0bcdbaca6b47534d1 Mon Sep 17 00:00:00 2001
From: Georg <georg@lysergic.dev>
Date: Wed, 28 Jun 2023 16:39:30 +0200
Subject: [PATCH] Zypper pkgrepo alreadyconfigured (#585)

* Fix zypper repository reconfiguration

See https://github.com/saltstack/salt/issues/63402 for issue details.

Signed-off-by: Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>

* Functional pkgrepo tests for SUSE

Signed-off-by: Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>

* Change pkgrepo state to use f-strings

Follow new styling rules.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>

---------

Signed-off-by: Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>
Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
---
 changelog/63402.fixed.md                      |   1 +
 salt/states/pkgrepo.py                        |  27 ++-
 .../functional/states/pkgrepo/test_suse.py    | 219 ++++++++++++++++++
 3 files changed, 235 insertions(+), 12 deletions(-)
 create mode 100644 changelog/63402.fixed.md
 create mode 100644 tests/pytests/functional/states/pkgrepo/test_suse.py

diff --git a/changelog/63402.fixed.md b/changelog/63402.fixed.md
new file mode 100644
index 0000000000..c38715738a
--- /dev/null
+++ b/changelog/63402.fixed.md
@@ -0,0 +1 @@
+Repaired zypper repositories being reconfigured without changes
diff --git a/salt/states/pkgrepo.py b/salt/states/pkgrepo.py
index c2d23f95bb..f041644287 100644
--- a/salt/states/pkgrepo.py
+++ b/salt/states/pkgrepo.py
@@ -464,7 +464,7 @@ def managed(name, ppa=None, copr=None, aptkey=True, **kwargs):
         pre = __salt__["pkg.get_repo"](repo=repo, **kwargs)
     except CommandExecutionError as exc:
         ret["result"] = False
-        ret["comment"] = "Failed to examine repo '{}': {}".format(name, exc)
+        ret["comment"] = f"Failed to examine repo '{name}': {exc}"
         return ret
 
     # This is because of how apt-sources works. This pushes distro logic
@@ -500,7 +500,10 @@ def managed(name, ppa=None, copr=None, aptkey=True, **kwargs):
                     else:
                         break
                 else:
-                    break
+                    if kwarg in ("comps", "key_url"):
+                        break
+                    else:
+                        continue
             elif kwarg in ("comps", "key_url"):
                 if sorted(sanitizedkwargs[kwarg]) != sorted(pre[kwarg]):
                     break
@@ -546,7 +549,7 @@ def managed(name, ppa=None, copr=None, aptkey=True, **kwargs):
                         break
         else:
             ret["result"] = True
-            ret["comment"] = "Package repo '{}' already configured".format(name)
+            ret["comment"] = f"Package repo '{name}' already configured"
             return ret
 
     if __opts__["test"]:
@@ -581,7 +584,7 @@ def managed(name, ppa=None, copr=None, aptkey=True, **kwargs):
         # This is another way to pass information back from the mod_repo
         # function.
         ret["result"] = False
-        ret["comment"] = "Failed to configure repo '{}': {}".format(name, exc)
+        ret["comment"] = f"Failed to configure repo '{name}': {exc}"
         return ret
 
     try:
@@ -597,10 +600,10 @@ def managed(name, ppa=None, copr=None, aptkey=True, **kwargs):
             ret["changes"] = {"repo": repo}
 
         ret["result"] = True
-        ret["comment"] = "Configured package repo '{}'".format(name)
+        ret["comment"] = f"Configured package repo '{name}'"
     except Exception as exc:  # pylint: disable=broad-except
         ret["result"] = False
-        ret["comment"] = "Failed to confirm config of repo '{}': {}".format(name, exc)
+        ret["comment"] = f"Failed to confirm config of repo '{name}': {exc}"
 
     # Clear cache of available packages, if present, since changes to the
     # repositories may change the packages that are available.
@@ -700,11 +703,11 @@ def absent(name, **kwargs):
         repo = __salt__["pkg.get_repo"](stripname, **kwargs)
     except CommandExecutionError as exc:
         ret["result"] = False
-        ret["comment"] = "Failed to configure repo '{}': {}".format(name, exc)
+        ret["comment"] = f"Failed to configure repo '{name}': {exc}"
         return ret
 
     if not repo:
-        ret["comment"] = "Package repo {} is absent".format(name)
+        ret["comment"] = f"Package repo {name} is absent"
         ret["result"] = True
         return ret
 
@@ -727,7 +730,7 @@ def absent(name, **kwargs):
     repos = __salt__["pkg.list_repos"]()
     if stripname not in repos:
         ret["changes"]["repo"] = name
-        ret["comment"] = "Removed repo {}".format(name)
+        ret["comment"] = f"Removed repo {name}"
 
         if not remove_key:
             ret["result"] = True
@@ -736,14 +739,14 @@ def absent(name, **kwargs):
                 removed_keyid = __salt__["pkg.del_repo_key"](stripname, **kwargs)
             except (CommandExecutionError, SaltInvocationError) as exc:
                 ret["result"] = False
-                ret["comment"] += ", but failed to remove key: {}".format(exc)
+                ret["comment"] += f", but failed to remove key: {exc}"
             else:
                 ret["result"] = True
                 ret["changes"]["keyid"] = removed_keyid
-                ret["comment"] += ", and keyid {}".format(removed_keyid)
+                ret["comment"] += f", and keyid {removed_keyid}"
     else:
         ret["result"] = False
-        ret["comment"] = "Failed to remove repo {}".format(name)
+        ret["comment"] = f"Failed to remove repo {name}"
 
     return ret
 
diff --git a/tests/pytests/functional/states/pkgrepo/test_suse.py b/tests/pytests/functional/states/pkgrepo/test_suse.py
new file mode 100644
index 0000000000..19ba928ce6
--- /dev/null
+++ b/tests/pytests/functional/states/pkgrepo/test_suse.py
@@ -0,0 +1,219 @@
+import pytest
+
+pytestmark = [
+    pytest.mark.destructive_test,
+    pytest.mark.skip_if_not_root,
+]
+
+
+@pytest.fixture
+def pkgrepo(states, grains):
+    if grains["os_family"] != "Suse":
+        raise pytest.skip.Exception(
+            "Test is only applicable to SUSE based operating systems",
+            _use_item_location=True,
+        )
+    return states.pkgrepo
+
+
+@pytest.fixture
+def suse_state_tree(grains, pkgrepo, state_tree):
+    managed_sls_contents = """
+    salttest:
+      pkgrepo.managed:
+        - enabled: 1
+        - gpgcheck: 1
+        - comments:
+          - '# Salt Test'
+        - refresh: 1
+    {% if grains['osmajorrelease'] == 15 %}
+        - baseurl: https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15-SP4/standard/
+        - humanname: openSUSE Backports for SLE 15 SP4
+        - gpgkey: https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15-SP4/standard/repodata/repomd.xml.key
+    {% elif grains['osfullname'] == 'openSUSE Tumbleweed' %}
+        - baseurl: http://download.opensuse.org/tumbleweed/repo/oss/
+        - humanname: openSUSE Tumbleweed OSS
+        - gpgkey: https://download.opensuse.org/tumbleweed/repo/oss/repodata/repomd.xml.key
+    {% endif %}
+    """
+
+    absent_sls_contents = """
+    salttest:
+      pkgrepo:
+        - absent
+    """
+
+    modified_sls_contents = """
+    salttest:
+      pkgrepo.managed:
+        - enabled: 1
+        - gpgcheck: 1
+        - comments:
+          - '# Salt Test (modified)'
+        - refresh: 1
+    {% if grains['osmajorrelease'] == 15 %}
+        - baseurl: https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15-SP4/standard/
+        - humanname: Salt modified Backports
+        - gpgkey: https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15-SP4/standard/repodata/repomd.xml.key
+    {% elif grains['osfullname'] == 'openSUSE Tumbleweed' %}
+        - baseurl: http://download.opensuse.org/tumbleweed/repo/oss/
+        - humanname: Salt modified OSS
+        - gpgkey: https://download.opensuse.org/tumbleweed/repo/oss/repodata/repomd.xml.key
+    {% endif %}
+    """
+
+    managed_state_file = pytest.helpers.temp_file(
+        "pkgrepo/managed.sls", managed_sls_contents, state_tree
+    )
+    absent_state_file = pytest.helpers.temp_file(
+        "pkgrepo/absent.sls", absent_sls_contents, state_tree
+    )
+    modified_state_file = pytest.helpers.temp_file(
+        "pkgrepo/modified.sls", modified_sls_contents, state_tree
+    )
+
+    try:
+        with managed_state_file, absent_state_file, modified_state_file:
+            yield
+    finally:
+        pass
+
+
+@pytest.mark.requires_salt_states("pkgrepo.managed", "pkgrepo.absent")
+def test_pkgrepo_managed_absent(grains, modules, subtests, suse_state_tree):
+    """
+    Test adding and removing a repository
+    """
+    add_repo_test_passed = False
+
+    def _run(name, test=False):
+        return modules.state.sls(
+            mods=name,
+            test=test,
+        )
+
+    with subtests.test("Add repository"):
+        ret = _run("pkgrepo.managed")
+        assert ret.failed is False
+        for state in ret:
+            assert state.result is True
+        add_repo_test_passed = True
+
+    if add_repo_test_passed is False:
+        pytest.skip("Adding the repository failed, skipping removal tests.")
+
+    with subtests.test("Remove repository, test"):
+        ret = _run("pkgrepo.absent", test=True)
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {}
+            assert state.comment.startswith("Package repo 'salttest' will be removed.")
+            assert state.result is None
+
+    with subtests.test("Remove repository"):
+        ret = _run("pkgrepo.absent")
+        assert ret.failed is False
+        for state in ret:
+            assert state.result is True
+
+    with subtests.test("Remove repository again, test"):
+        ret = _run("pkgrepo.absent", test=True)
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {}
+            assert state.comment == "Package repo salttest is absent"
+            assert state.result is True
+
+    with subtests.test("Remove repository again"):
+        ret = _run("pkgrepo.absent")
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {}
+            assert state.comment == "Package repo salttest is absent"
+            assert state.result is True
+
+
+@pytest.mark.requires_salt_states("pkgrepo.managed")
+def test_pkgrepo_managed_modify(grains, modules, subtests, suse_state_tree):
+    """
+    Test adding and modifying a repository
+    """
+    add_repo_test_passed = False
+
+    def _run(name, test=False):
+        return modules.state.sls(
+            mods=name,
+            test=test,
+        )
+
+    with subtests.test("Add repository, test"):
+        ret = _run("pkgrepo.managed", test=True)
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {"repo": "salttest"}
+            assert state.comment.startswith(
+                "Package repo 'salttest' would be configured."
+            )
+            assert state.result is None
+
+    with subtests.test("Add repository"):
+        ret = _run("pkgrepo.managed")
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {"repo": "salttest"}
+            assert state.comment == "Configured package repo 'salttest'"
+            assert state.result is True
+        add_repo_test_passed = True
+
+    if add_repo_test_passed is False:
+        pytest.skip("Adding the repository failed, skipping modification tests.")
+
+    with subtests.test("Add repository again, test"):
+        ret = _run("pkgrepo.managed", test=True)
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {}
+            assert state.comment == "Package repo 'salttest' already configured"
+            assert state.result is True
+
+    with subtests.test("Add repository again"):
+        ret = _run("pkgrepo.managed")
+        assert ret.failed is False
+        for state in ret:
+            assert state.result is True
+            assert state.changes == {}
+            assert state.comment == "Package repo 'salttest' already configured"
+
+    with subtests.test("Modify repository, test"):
+        ret = _run("pkgrepo.modified", test=True)
+        assert ret.failed is False
+        for state in ret:
+            assert state.changes == {
+                "comments": {"new": ["# Salt Test (modified)"], "old": None},
+                "refresh": {"new": 1, "old": None},
+                "gpgkey": {
+                    "new": "https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15-SP4/standard/repodata/repomd.xml.key",
+                    "old": None,
+                },
+                "name": {
+                    "new": "Salt modified Backports",
+                    "old": "openSUSE Backports for SLE 15 SP4",
+                },
+            }
+            assert state.comment.startswith(
+                "Package repo 'salttest' would be configured."
+            )
+            assert state.result is None
+
+    with subtests.test("Modify repository"):
+        ret = _run("pkgrepo.modified")
+        assert ret.failed is False
+        for state in ret:
+            assert state.result is True
+            assert state.changes == {
+                "name": {
+                    "new": "Salt modified Backports",
+                    "old": "openSUSE Backports for SLE 15 SP4",
+                }
+            }
+            assert state.comment == "Configured package repo 'salttest'"
-- 
2.41.0


openSUSE Build Service is sponsored by