SHA256
1
0
forked from pool/salt

Accepting request 1103320 from systemsmanagement:saltstack

OBS-URL: https://build.opensuse.org/request/show/1103320
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/salt?expand=0&rev=138
This commit is contained in:
Dominique Leuenberger 2023-08-11 13:55:10 +00:00 committed by Git OBS Bridge
commit e5d36cd92f
14 changed files with 1834 additions and 2 deletions

View File

@ -0,0 +1,71 @@
From 40a57afc65e71835127a437248ed655404cff0e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Tue, 27 Jun 2023 11:24:39 +0100
Subject: [PATCH] 3006.0: Prevent _pygit2.GitError: error loading
known_hosts when $HOME is not set (bsc#1210994) (#588)
* Prevent _pygit2.GitError: error loading known_hosts when $HOME is not set
* Add unit test to cover case of unset home
---
salt/utils/gitfs.py | 5 +++++
tests/unit/utils/test_gitfs.py | 14 ++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py
index cc9895d8ab..38e84f38aa 100644
--- a/salt/utils/gitfs.py
+++ b/salt/utils/gitfs.py
@@ -34,6 +34,7 @@ import salt.utils.stringutils
import salt.utils.url
import salt.utils.user
import salt.utils.versions
+import salt.syspaths
from salt.config import DEFAULT_MASTER_OPTS as _DEFAULT_MASTER_OPTS
from salt.exceptions import FileserverConfigError, GitLockError, get_error_message
from salt.utils.event import tagify
@@ -1867,6 +1868,10 @@ class Pygit2(GitProvider):
# pruning only available in pygit2 >= 0.26.2
pass
try:
+ # Make sure $HOME env variable is set to prevent
+ # _pygit2.GitError: error loading known_hosts in some libgit2 versions.
+ if "HOME" not in os.environ:
+ os.environ["HOME"] = salt.syspaths.HOME_DIR
fetch_results = origin.fetch(**fetch_kwargs)
except GitError as exc: # pylint: disable=broad-except
exc_str = get_error_message(exc).lower()
diff --git a/tests/unit/utils/test_gitfs.py b/tests/unit/utils/test_gitfs.py
index b99da3ef91..7c400b69af 100644
--- a/tests/unit/utils/test_gitfs.py
+++ b/tests/unit/utils/test_gitfs.py
@@ -14,6 +14,7 @@ import salt.utils.gitfs
import salt.utils.platform
import tests.support.paths
from salt.exceptions import FileserverConfigError
+from tests.support.helpers import patched_environ
from tests.support.mixins import AdaptedConfigurationTestCaseMixin
from tests.support.mock import MagicMock, patch
from tests.support.unit import TestCase
@@ -335,3 +336,16 @@ class TestPygit2(TestCase):
self.assertIn(provider.cachedir, provider.checkout())
provider.branch = "does_not_exist"
self.assertIsNone(provider.checkout())
+
+ def test_checkout_with_home_env_unset(self):
+ remote = os.path.join(tests.support.paths.TMP, "pygit2-repo")
+ cache = os.path.join(tests.support.paths.TMP, "pygit2-repo-cache")
+ self._prepare_remote_repository(remote)
+ provider = self._prepare_cache_repository(remote, cache)
+ provider.remotecallbacks = None
+ provider.credentials = None
+ with patched_environ(__cleanup__=["HOME"]):
+ self.assertTrue("HOME" not in os.environ)
+ provider.init_remote()
+ provider.fetch()
+ self.assertTrue("HOME" in os.environ)
--
2.41.0

View File

@ -1 +1 @@
2c8ae68c3fb161fd84005ed1b58abf7865ba646f
26c7f283aef34794887afd5a4199be1018c5c592

View File

@ -0,0 +1,47 @@
From 8e9f2587aea52c1d0a5c07d5f9bb77a23ae4d4a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Tue, 23 May 2023 10:40:02 +0100
Subject: [PATCH] Avoid conflicts with dependencies versions
(bsc#1211612) (#581)
This commit fixes the Salt requirements file that are used to
generate the "requires.txt" file that is included in Salt egginfo
in order to be consistent with the installed packages
of Salt dependencies.
This prevents issues when resolving and validating Salt dependencies
with "pkg_resources" Python module.
---
requirements/base.txt | 2 +-
requirements/zeromq.txt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/requirements/base.txt b/requirements/base.txt
index c19d8804a2..437aa01d31 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -6,7 +6,7 @@ MarkupSafe
requests>=1.0.0
distro>=1.0.1
psutil>=5.0.0
-packaging>=21.3
+packaging>=17.1
looseversion
# We need contextvars for salt-ssh
contextvars
diff --git a/requirements/zeromq.txt b/requirements/zeromq.txt
index 1e9a815c1b..23d1ef25dc 100644
--- a/requirements/zeromq.txt
+++ b/requirements/zeromq.txt
@@ -1,5 +1,5 @@
-r base.txt
-r crypto.txt
-pyzmq>=20.0.0
+pyzmq>=17.1.2
pyzmq==25.0.2 ; sys_platform == "win32"
--
2.39.2

View File

@ -0,0 +1,39 @@
From f02e97df14e4927efbb5ddd3a2bbc5a650330b9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Fri, 26 May 2023 16:50:51 +0100
Subject: [PATCH] Define __virtualname__ for transactional_update module
(#582)
This prevent problems with LazyLoader when importing this module,
which was wrongly exposing functions for this module under "state.*"
---
salt/modules/transactional_update.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/salt/modules/transactional_update.py b/salt/modules/transactional_update.py
index 6493966782..658ebccc6b 100644
--- a/salt/modules/transactional_update.py
+++ b/salt/modules/transactional_update.py
@@ -285,6 +285,8 @@ from salt.modules.state import _check_queue, _prior_running_states, _wait, runni
__func_alias__ = {"apply_": "apply"}
+__virtualname__ = "transactional_update"
+
log = logging.getLogger(__name__)
@@ -300,7 +302,7 @@ def __virtual__():
_prior_running_states, globals()
)
running = salt.utils.functools.namespaced_function(running, globals())
- return True
+ return __virtualname__
else:
return (False, "Module transactional_update requires a transactional system")
--
2.39.2

View File

@ -0,0 +1,253 @@
From c25c8081ded775f3574b0bc999d809ce14701ba5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Thu, 3 Aug 2023 10:07:28 +0100
Subject: [PATCH] Fix regression: multiple values for keyword argument
'saltenv' (bsc#1212844) (#590)
* fix passing wrong keyword arguments to cp.cache_file in pkg.installed with sources
* Drop `**kwargs` usage and be explicit about the supported keyword arguments.
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
* Add regression test for https://github.com/saltstack/salt/issues/64118
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
* Add changelog file
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
---------
Signed-off-by: Pedro Algarvio <palgarvio@vmware.com>
Co-authored-by: Massimiliano Torromeo <massimiliano.torromeo@gmail.com>
Co-authored-by: Pedro Algarvio <palgarvio@vmware.com>
---
changelog/64118.fixed.md | 1 +
salt/modules/win_pkg.py | 25 +++++++-----
salt/states/pkg.py | 4 +-
tests/pytests/unit/modules/test_win_pkg.py | 2 +-
tests/pytests/unit/states/test_pkg.py | 46 +++++++++++++++++++---
5 files changed, 62 insertions(+), 16 deletions(-)
create mode 100644 changelog/64118.fixed.md
diff --git a/changelog/64118.fixed.md b/changelog/64118.fixed.md
new file mode 100644
index 0000000000..e7251827e9
--- /dev/null
+++ b/changelog/64118.fixed.md
@@ -0,0 +1 @@
+Stop passing `**kwargs` and be explicit about the keyword arguments to pass, namely, to `cp.cache_file` call in `salt.states.pkg`
diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py
index 3aa7c7919a..e80dd19322 100644
--- a/salt/modules/win_pkg.py
+++ b/salt/modules/win_pkg.py
@@ -1298,7 +1298,7 @@ def _repo_process_pkg_sls(filename, short_path_name, ret, successful_verbose):
successful_verbose[short_path_name] = []
-def _get_source_sum(source_hash, file_path, saltenv, **kwargs):
+def _get_source_sum(source_hash, file_path, saltenv, verify_ssl=True):
"""
Extract the hash sum, whether it is in a remote hash file, or just a string.
"""
@@ -1315,7 +1315,7 @@ def _get_source_sum(source_hash, file_path, saltenv, **kwargs):
# The source_hash is a file on a server
try:
cached_hash_file = __salt__["cp.cache_file"](
- source_hash, saltenv, verify_ssl=kwargs.get("verify_ssl", True)
+ source_hash, saltenv=saltenv, verify_ssl=verify_ssl
)
except MinionError as exc:
log.exception("Failed to cache %s", source_hash, exc_info=exc)
@@ -1671,7 +1671,7 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
try:
cached_file = __salt__["cp.cache_file"](
cache_file,
- saltenv,
+ saltenv=saltenv,
verify_ssl=kwargs.get("verify_ssl", True),
)
except MinionError as exc:
@@ -1686,7 +1686,7 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
try:
cached_file = __salt__["cp.cache_file"](
cache_file,
- saltenv,
+ saltenv=saltenv,
verify_ssl=kwargs.get("verify_ssl", True),
)
except MinionError as exc:
@@ -1706,7 +1706,9 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
# It's not cached. Cache it, mate.
try:
cached_pkg = __salt__["cp.cache_file"](
- installer, saltenv, verify_ssl=kwargs.get("verify_ssl", True)
+ installer,
+ saltenv=saltenv,
+ verify_ssl=kwargs.get("verify_ssl", True),
)
except MinionError as exc:
msg = "Failed to cache {}".format(installer)
@@ -1730,7 +1732,7 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
try:
cached_pkg = __salt__["cp.cache_file"](
installer,
- saltenv,
+ saltenv=saltenv,
verify_ssl=kwargs.get("verify_ssl", True),
)
except MinionError as exc:
@@ -1754,7 +1756,12 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
# Compare the hash sums
source_hash = pkginfo[version_num].get("source_hash", False)
if source_hash:
- source_sum = _get_source_sum(source_hash, cached_pkg, saltenv, **kwargs)
+ source_sum = _get_source_sum(
+ source_hash,
+ cached_pkg,
+ saltenv=saltenv,
+ verify_ssl=kwargs.get("verify_ssl", True),
+ )
log.debug(
"pkg.install: Source %s hash: %s",
source_sum["hash_type"],
@@ -2126,7 +2133,7 @@ def remove(name=None, pkgs=None, **kwargs):
try:
cached_pkg = __salt__["cp.cache_file"](
uninstaller,
- saltenv,
+ saltenv=saltenv,
verify_ssl=kwargs.get("verify_ssl", True),
)
except MinionError as exc:
@@ -2150,7 +2157,7 @@ def remove(name=None, pkgs=None, **kwargs):
try:
cached_pkg = __salt__["cp.cache_file"](
uninstaller,
- saltenv,
+ saltenv=saltenv,
verify_ssl=kwargs.get("verify_ssl", True),
)
except MinionError as exc:
diff --git a/salt/states/pkg.py b/salt/states/pkg.py
index 12fbc87a1a..a605b23107 100644
--- a/salt/states/pkg.py
+++ b/salt/states/pkg.py
@@ -760,7 +760,9 @@ def _find_install_targets(
err = "Unable to cache {0}: {1}"
try:
cached_path = __salt__["cp.cache_file"](
- version_string, saltenv=kwargs["saltenv"], **kwargs
+ version_string,
+ saltenv=kwargs["saltenv"],
+ verify_ssl=kwargs.get("verify_ssl", True),
)
except CommandExecutionError as exc:
problems.append(err.format(version_string, exc))
diff --git a/tests/pytests/unit/modules/test_win_pkg.py b/tests/pytests/unit/modules/test_win_pkg.py
index 76234fb77e..6d435f00a5 100644
--- a/tests/pytests/unit/modules/test_win_pkg.py
+++ b/tests/pytests/unit/modules/test_win_pkg.py
@@ -262,7 +262,7 @@ def test_pkg_install_verify_ssl_false():
result = win_pkg.install(name="nsis", version="3.02", verify_ssl=False)
mock_cp.assert_called_once_with(
"http://download.sourceforge.net/project/nsis/NSIS%203/3.02/nsis-3.02-setup.exe",
- "base",
+ saltenv="base",
verify_ssl=False,
)
assert expected == result
diff --git a/tests/pytests/unit/states/test_pkg.py b/tests/pytests/unit/states/test_pkg.py
index b852f27b00..f58be11011 100644
--- a/tests/pytests/unit/states/test_pkg.py
+++ b/tests/pytests/unit/states/test_pkg.py
@@ -3,6 +3,7 @@ import logging
import pytest
import salt.modules.beacons as beaconmod
+import salt.modules.cp as cp
import salt.modules.pkg_resource as pkg_resource
import salt.modules.yumpkg as yumpkg
import salt.states.beacon as beaconstate
@@ -15,19 +16,28 @@ log = logging.getLogger(__name__)
@pytest.fixture
-def configure_loader_modules():
+def configure_loader_modules(minion_opts):
return {
+ cp: {
+ "__opts__": minion_opts,
+ },
pkg: {
"__env__": "base",
"__salt__": {},
"__grains__": {"os": "CentOS", "os_family": "RedHat"},
- "__opts__": {"test": False, "cachedir": ""},
+ "__opts__": minion_opts,
"__instance_id__": "",
"__low__": {},
"__utils__": {"state.gen_tag": state_utils.gen_tag},
},
- beaconstate: {"__salt__": {}, "__opts__": {}},
- beaconmod: {"__salt__": {}, "__opts__": {}},
+ beaconstate: {
+ "__salt__": {},
+ "__opts__": minion_opts,
+ },
+ beaconmod: {
+ "__salt__": {},
+ "__opts__": minion_opts,
+ },
pkg_resource: {
"__salt__": {},
"__grains__": {"os": "CentOS", "os_family": "RedHat"},
@@ -35,7 +45,7 @@ def configure_loader_modules():
yumpkg: {
"__salt__": {},
"__grains__": {"osarch": "x86_64", "osmajorrelease": 7},
- "__opts__": {},
+ "__opts__": minion_opts,
},
}
@@ -563,6 +573,32 @@ def test_installed_with_changes_test_true(list_pkgs):
assert ret["changes"] == expected
+def test_installed_with_sources(list_pkgs, tmp_path):
+ """
+ Test pkg.installed with passing `sources`
+ """
+
+ list_pkgs = MagicMock(return_value=list_pkgs)
+ pkg_source = tmp_path / "pkga-package-0.3.0.deb"
+
+ with patch.dict(
+ pkg.__salt__,
+ {
+ "cp.cache_file": cp.cache_file,
+ "pkg.list_pkgs": list_pkgs,
+ "pkg_resource.pack_sources": pkg_resource.pack_sources,
+ "lowpkg.bin_pkg_info": MagicMock(),
+ },
+ ), patch("salt.fileclient.get_file_client", return_value=MagicMock()):
+ try:
+ ret = pkg.installed("install-pkgd", sources=[{"pkga": str(pkg_source)}])
+ assert ret["result"] is False
+ except TypeError as exc:
+ if "got multiple values for keyword argument 'saltenv'" in str(exc):
+ pytest.fail(f"TypeError should have not been raised: {exc}")
+ raise exc from None
+
+
@pytest.mark.parametrize("action", ["removed", "purged"])
def test_removed_purged_with_changes_test_true(list_pkgs, action):
"""
--
2.41.0

View File

@ -0,0 +1,118 @@
From 38de9af6bd243d35464713e0ee790255d3b40a7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Fri, 23 Jun 2023 13:02:51 +0100
Subject: [PATCH] Fix some issues detected in "salt-support" CLI, module
and tests (bsc#1211591) (#580)
* saltsupport: avoid debug traceback due missing import
* Use yaml and json wrappers provides by Salt utils
* Remove unnecessary call to deprecated setup_logfile_logger
* Move unittest saltsupport tests to proper place
* Fix test assertion error due wrong capturing of message
---
salt/cli/support/__init__.py | 4 ++--
salt/cli/support/collector.py | 6 ++----
tests/{pytests => }/unit/cli/test_support.py | 0
tests/unit/modules/test_saltsupport.py | 6 +++---
4 files changed, 7 insertions(+), 9 deletions(-)
rename tests/{pytests => }/unit/cli/test_support.py (100%)
diff --git a/salt/cli/support/__init__.py b/salt/cli/support/__init__.py
index 59c2609e07..0a7da72e93 100644
--- a/salt/cli/support/__init__.py
+++ b/salt/cli/support/__init__.py
@@ -6,7 +6,7 @@ import os
import jinja2
import salt.exceptions
-import yaml
+import salt.utils.yaml
log = logging.getLogger(__name__)
@@ -48,7 +48,7 @@ def get_profile(profile, caller, runner):
try:
rendered_template = _render_profile(profile_path, caller, runner)
log.debug("\n{d}\n{t}\n{d}\n".format(d="-" * 80, t=rendered_template))
- data.update(yaml.load(rendered_template))
+ data.update(salt.utils.yaml.load(rendered_template))
except Exception as ex:
log.debug(ex, exc_info=True)
raise salt.exceptions.SaltException(
diff --git a/salt/cli/support/collector.py b/salt/cli/support/collector.py
index 1879cc5220..0ba987580c 100644
--- a/salt/cli/support/collector.py
+++ b/salt/cli/support/collector.py
@@ -1,6 +1,5 @@
import builtins as exceptions
import copy
-import json
import logging
import os
import sys
@@ -16,10 +15,10 @@ import salt.cli.support.intfunc
import salt.cli.support.localrunner
import salt.defaults.exitcodes
import salt.exceptions
-import salt.ext.six as six
import salt.output.table_out
import salt.runner
import salt.utils.files
+import salt.utils.json
import salt.utils.parsers
import salt.utils.platform
import salt.utils.process
@@ -169,7 +168,7 @@ class SupportDataCollector:
content = None
if content is None:
- data = json.loads(json.dumps(data))
+ data = salt.utils.json.loads(salt.utils.json.dumps(data))
if isinstance(data, dict) and data.get("return"):
data = data.get("return")
content = yaml.safe_dump(data, default_flow_style=False, indent=4)
@@ -506,7 +505,6 @@ class SaltSupport(salt.utils.parsers.SaltSupportOptionParser):
self.out.error(ex)
else:
if self.config["log_level"] not in ("quiet",):
- self.setup_logfile_logger()
salt.utils.verify.verify_log(self.config)
salt.cli.support.log = log # Pass update logger so trace is available
diff --git a/tests/pytests/unit/cli/test_support.py b/tests/unit/cli/test_support.py
similarity index 100%
rename from tests/pytests/unit/cli/test_support.py
rename to tests/unit/cli/test_support.py
diff --git a/tests/unit/modules/test_saltsupport.py b/tests/unit/modules/test_saltsupport.py
index 4ef04246b9..2afdd69b3e 100644
--- a/tests/unit/modules/test_saltsupport.py
+++ b/tests/unit/modules/test_saltsupport.py
@@ -251,8 +251,8 @@ professor: Farnsworth
with pytest.raises(salt.exceptions.SaltInvocationError) as err:
support.sync("group-name")
assert (
- ' Support archive "/mnt/storage/three-support-222-222.bz2" was not found'
- in str(err)
+ 'Support archive "/mnt/storage/three-support-222-222.bz2" was not found'
+ in str(err.value)
)
@patch("tempfile.mkstemp", MagicMock(return_value=(0, "dummy")))
@@ -274,7 +274,7 @@ professor: Farnsworth
with pytest.raises(salt.exceptions.SaltInvocationError) as err:
support.sync("group-name", name="lost.bz2")
- assert ' Support archive "lost.bz2" was not found' in str(err)
+ assert 'Support archive "lost.bz2" was not found' in str(err.value)
@patch("tempfile.mkstemp", MagicMock(return_value=(0, "dummy")))
@patch("os.path.exists", MagicMock(return_value=False))
--
2.41.0

View File

@ -0,0 +1,154 @@
From 502354be32fcff9b0607f6e435ca8825a4c2cd56 Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <vzhestkov@suse.com>
Date: Thu, 3 Aug 2023 11:07:03 +0200
Subject: [PATCH] Fix the regression of user.present state when group is
unset (#589)
* Fix user.present state when group is unset
* Fix user unit test
---------
Co-authored-by: Megan Wilhite <mwilhite@vmware.com>
---
changelog/64211.fixed.md | 1 +
salt/states/user.py | 2 +-
tests/pytests/functional/states/test_user.py | 74 +++++++++++++++++++-
tests/pytests/unit/states/test_user.py | 2 +
4 files changed, 76 insertions(+), 3 deletions(-)
create mode 100644 changelog/64211.fixed.md
diff --git a/changelog/64211.fixed.md b/changelog/64211.fixed.md
new file mode 100644
index 0000000000..26b39acf02
--- /dev/null
+++ b/changelog/64211.fixed.md
@@ -0,0 +1 @@
+Fix user.present state when groups is unset to ensure the groups are unchanged, as documented.
diff --git a/salt/states/user.py b/salt/states/user.py
index ed2d5a05f4..929afb2cd1 100644
--- a/salt/states/user.py
+++ b/salt/states/user.py
@@ -100,7 +100,7 @@ def _changes(
change = {}
wanted_groups = sorted(set((groups or []) + (optional_groups or [])))
- if not remove_groups:
+ if not remove_groups or groups is None and not optional_groups:
wanted_groups = sorted(set(wanted_groups + lusr["groups"]))
if uid and lusr["uid"] != uid:
change["uid"] = uid
diff --git a/tests/pytests/functional/states/test_user.py b/tests/pytests/functional/states/test_user.py
index 09d34da168..96b1ec55c8 100644
--- a/tests/pytests/functional/states/test_user.py
+++ b/tests/pytests/functional/states/test_user.py
@@ -117,7 +117,6 @@ def test_user_present_when_home_dir_does_not_18843(states, existing_account):
ret = states.user.present(
name=existing_account.username,
home=existing_account.info.home,
- remove_groups=False,
)
assert ret.result is True
assert pathlib.Path(existing_account.info.home).is_dir()
@@ -228,7 +227,6 @@ def test_user_present_unicode(states, username, subtests):
roomnumber="①②③",
workphone="١٢٣٤",
homephone="६७८",
- remove_groups=False,
)
assert ret.result is True
@@ -429,3 +427,75 @@ def test_user_present_change_optional_groups(
user_info = modules.user.info(username)
assert user_info
assert user_info["groups"] == [group_1.name]
+
+
+@pytest.mark.skip_unless_on_linux(reason="underlying functionality only runs on Linux")
+def test_user_present_no_groups(modules, states, username):
+ """
+ test user.present when groups arg is not
+ included by the group is created in another
+ state. Re-run the states to ensure there are
+ not changes and it is idempotent.
+ """
+ groups = ["testgroup1", "testgroup2"]
+ try:
+ ret = states.group.present(name=username, gid=61121)
+ assert ret.result is True
+
+ ret = states.user.present(
+ name=username,
+ uid=61121,
+ gid=61121,
+ )
+ assert ret.result is True
+ assert ret.changes["groups"] == [username]
+ assert ret.changes["name"] == username
+
+ ret = states.group.present(
+ name=groups[0],
+ members=[username],
+ )
+ assert ret.changes["members"] == [username]
+
+ ret = states.group.present(
+ name=groups[1],
+ members=[username],
+ )
+ assert ret.changes["members"] == [username]
+
+ user_info = modules.user.info(username)
+ assert user_info
+ assert user_info["groups"] == [username, groups[0], groups[1]]
+
+ # run again, expecting no changes
+ ret = states.group.present(name=username)
+ assert ret.result is True
+ assert ret.changes == {}
+
+ ret = states.user.present(
+ name=username,
+ )
+ assert ret.result is True
+ assert ret.changes == {}
+
+ ret = states.group.present(
+ name=groups[0],
+ members=[username],
+ )
+ assert ret.result is True
+ assert ret.changes == {}
+
+ ret = states.group.present(
+ name=groups[1],
+ members=[username],
+ )
+ assert ret.result is True
+ assert ret.changes == {}
+
+ user_info = modules.user.info(username)
+ assert user_info
+ assert user_info["groups"] == [username, groups[0], groups[1]]
+ finally:
+ for group in groups:
+ ret = states.group.absent(name=group)
+ assert ret.result is True
diff --git a/tests/pytests/unit/states/test_user.py b/tests/pytests/unit/states/test_user.py
index 94e69d70ed..d50d16e3be 100644
--- a/tests/pytests/unit/states/test_user.py
+++ b/tests/pytests/unit/states/test_user.py
@@ -189,6 +189,8 @@ def test_present_uid_gid_change():
"user.chgid": Mock(),
"file.group_to_gid": mock_group_to_gid,
"file.gid_to_group": mock_gid_to_group,
+ "group.info": MagicMock(return_value=after),
+ "user.chgroups": MagicMock(return_value=True),
}
with patch.dict(user.__grains__, {"kernel": "Linux"}), patch.dict(
user.__salt__, dunder_salt
--
2.41.0

View File

@ -0,0 +1,181 @@
From 027cbef223616f5ab6c73e60bcaa9f9e81a6ce67 Mon Sep 17 00:00:00 2001
From: Daniel Mach <daniel.mach@suse.com>
Date: Wed, 28 Jun 2023 16:39:42 +0200
Subject: [PATCH] Fix utf8 handling in 'pass' renderer and make it more
robust (#579)
* Migrate string formatting in 'pass' renderer to a f-string
* Fix utf8 handling in 'pass' renderer and make it more robust
---
changelog/64300.fixed.md | 1 +
salt/renderers/pass.py | 12 +--
tests/pytests/unit/renderers/test_pass.py | 99 +++++++++++++++++++++++
3 files changed, 103 insertions(+), 9 deletions(-)
create mode 100644 changelog/64300.fixed.md
diff --git a/changelog/64300.fixed.md b/changelog/64300.fixed.md
new file mode 100644
index 0000000000..4418db1d04
--- /dev/null
+++ b/changelog/64300.fixed.md
@@ -0,0 +1 @@
+Fix utf8 handling in 'pass' renderer
diff --git a/salt/renderers/pass.py b/salt/renderers/pass.py
index ba0f152c23..ae75bba443 100644
--- a/salt/renderers/pass.py
+++ b/salt/renderers/pass.py
@@ -145,23 +145,17 @@ def _fetch_secret(pass_path):
env["GNUPGHOME"] = pass_gnupghome
try:
- proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env)
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env, encoding="utf-8")
pass_data, pass_error = proc.communicate()
pass_returncode = proc.returncode
- except OSError as e:
+ except (OSError, UnicodeDecodeError) as e:
pass_data, pass_error = "", str(e)
pass_returncode = 1
# The version of pass used during development sent output to
# stdout instead of stderr even though its returncode was non zero.
if pass_returncode or not pass_data:
- try:
- pass_error = pass_error.decode("utf-8")
- except (AttributeError, ValueError):
- pass
- msg = "Could not fetch secret '{}' from the password store: {}".format(
- pass_path, pass_error
- )
+ msg = f"Could not fetch secret '{pass_path}' from the password store: {pass_error}"
if pass_strict_fetch:
raise SaltRenderError(msg)
else:
diff --git a/tests/pytests/unit/renderers/test_pass.py b/tests/pytests/unit/renderers/test_pass.py
index 1e2ebb7ea8..f7c79e1fe1 100644
--- a/tests/pytests/unit/renderers/test_pass.py
+++ b/tests/pytests/unit/renderers/test_pass.py
@@ -1,8 +1,12 @@
import importlib
+import os
+import shutil
+import tempfile
import pytest
import salt.exceptions
+import salt.utils.files
from tests.support.mock import MagicMock, patch
# "pass" is a reserved keyword, we need to import it differently
@@ -19,6 +23,47 @@ def configure_loader_modules(master_opts):
}
+@pytest.fixture()
+def pass_executable(request):
+ tmp_dir = tempfile.mkdtemp(prefix="salt_pass_")
+ pass_path = os.path.join(tmp_dir, "pass")
+ with salt.utils.files.fopen(pass_path, "w") as f:
+ f.write("#!/bin/sh\n")
+ # return path path wrapped into unicode characters
+ # pass args ($1, $2) are ("show", <pass_path>)
+ f.write('echo "α>>> $2 <<<β"\n')
+ os.chmod(pass_path, 0o755)
+ yield pass_path
+ shutil.rmtree(tmp_dir)
+
+
+@pytest.fixture()
+def pass_executable_error(request):
+ tmp_dir = tempfile.mkdtemp(prefix="salt_pass_")
+ pass_path = os.path.join(tmp_dir, "pass")
+ with salt.utils.files.fopen(pass_path, "w") as f:
+ f.write("#!/bin/sh\n")
+ # return error message with unicode characters
+ f.write('echo "ERROR: αβγ" >&2\n')
+ f.write("exit 1\n")
+ os.chmod(pass_path, 0o755)
+ yield pass_path
+ shutil.rmtree(tmp_dir)
+
+
+@pytest.fixture()
+def pass_executable_invalid_utf8(request):
+ tmp_dir = tempfile.mkdtemp(prefix="salt_pass_")
+ pass_path = os.path.join(tmp_dir, "pass")
+ with salt.utils.files.fopen(pass_path, "wb") as f:
+ f.write(b"#!/bin/sh\n")
+ # return invalid utf-8 sequence
+ f.write(b'echo "\x80\x81"\n')
+ os.chmod(pass_path, 0o755)
+ yield pass_path
+ shutil.rmtree(tmp_dir)
+
+
# The default behavior is that if fetching a secret from pass fails,
# the value is passed through. Even the trailing newlines are preserved.
def test_passthrough():
@@ -161,3 +206,57 @@ def test_env():
call_args, call_kwargs = popen_mock.call_args_list[0]
assert call_kwargs["env"]["GNUPGHOME"] == config["pass_gnupghome"]
assert call_kwargs["env"]["PASSWORD_STORE_DIR"] == config["pass_dir"]
+
+
+@pytest.mark.skip_on_windows(reason="Not supported on Windows")
+def test_utf8(pass_executable):
+ config = {
+ "pass_variable_prefix": "pass:",
+ "pass_strict_fetch": True,
+ }
+ mocks = {
+ "_get_pass_exec": MagicMock(return_value=pass_executable),
+ }
+
+ pass_path = "pass:secret"
+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks):
+ result = pass_.render(pass_path)
+ assert result == "α>>> secret <<<β"
+
+
+@pytest.mark.skip_on_windows(reason="Not supported on Windows")
+def test_utf8_error(pass_executable_error):
+ config = {
+ "pass_variable_prefix": "pass:",
+ "pass_strict_fetch": True,
+ }
+ mocks = {
+ "_get_pass_exec": MagicMock(return_value=pass_executable_error),
+ }
+
+ pass_path = "pass:secret"
+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks):
+ with pytest.raises(
+ salt.exceptions.SaltRenderError,
+ match=r"Could not fetch secret 'secret' from the password store: ERROR: αβγ",
+ ):
+ result = pass_.render(pass_path)
+
+
+@pytest.mark.skip_on_windows(reason="Not supported on Windows")
+def test_invalid_utf8(pass_executable_invalid_utf8):
+ config = {
+ "pass_variable_prefix": "pass:",
+ "pass_strict_fetch": True,
+ }
+ mocks = {
+ "_get_pass_exec": MagicMock(return_value=pass_executable_invalid_utf8),
+ }
+
+ pass_path = "pass:secret"
+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks):
+ with pytest.raises(
+ salt.exceptions.SaltRenderError,
+ match=r"Could not fetch secret 'secret' from the password store: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte",
+ ):
+ result = pass_.render(pass_path)
--
2.41.0

View File

@ -0,0 +1,37 @@
From 53a5a62191b81c6838c3041cf95ffeb12fbab5b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Mon, 19 Jun 2023 15:35:41 +0100
Subject: [PATCH] Make master_tops compatible with Salt 3000 and older
minions (bsc#1212516) (bsc#1212517) (#587)
---
salt/master.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/salt/master.py b/salt/master.py
index da1eb8cef5..fc243ef674 100644
--- a/salt/master.py
+++ b/salt/master.py
@@ -1213,6 +1213,7 @@ class AESFuncs(TransportMethods):
"_dir_list",
"_symlink_list",
"_file_envs",
+ "_ext_nodes", # To keep compatibility with old Salt minion versions
)
def __init__(self, opts, context=None):
@@ -1412,6 +1413,9 @@ class AESFuncs(TransportMethods):
return {}
return self.masterapi._master_tops(load, skip_verify=True)
+ # Needed so older minions can request master_tops
+ _ext_nodes = _master_tops
+
def _master_opts(self, load):
"""
Return the master options to the minion
--
2.41.0

View File

@ -0,0 +1,480 @@
From c1408333364ac25ff5d316afa9674f7687217b0c Mon Sep 17 00:00:00 2001
From: Dominik Gedon <dgedon@suse.de>
Date: Thu, 3 Aug 2023 11:08:21 +0200
Subject: [PATCH] Mark Salt 3006 as released (#586)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Mark Salt 3006 as released
Without this, commands like
```
salt '*' salt_version.equal 'Sulfur'
```
will not work properly and return False although Salt 3006 is used.
Signed-off-by: Dominik Gedon <dominik.gedon@suse.com>
* Fix detection of Salt codename by salt_version module
* Fix mess with version detection bad version definition
* Add some new and fix unit tests
* Fix SaltStackVersion string for new versions format
* Do not crash when passing numbers to 'salt_version.get_release_number'
* Fix salt_version execution module documentation
---------
Signed-off-by: Dominik Gedon <dominik.gedon@suse.com>
Co-authored-by: Pablo Suárez Hernández <psuarezhernandez@suse.com>
---
salt/modules/salt_version.py | 8 +-
salt/version.py | 218 +++++++++---------
.../pytests/unit/modules/test_salt_version.py | 55 ++++-
tests/pytests/unit/test_version.py | 10 +-
4 files changed, 176 insertions(+), 115 deletions(-)
diff --git a/salt/modules/salt_version.py b/salt/modules/salt_version.py
index 1b5421fee4..99dae5f61a 100644
--- a/salt/modules/salt_version.py
+++ b/salt/modules/salt_version.py
@@ -20,7 +20,7 @@ A simple example might be something like the following:
.. code-block:: jinja
{# a boolean check #}
- {% set option_deprecated = salt['salt_version.less_than']("3001") %}
+ {% set option_deprecated = salt['salt_version.less_than']("Sodium") %}
{% if option_deprecated %}
<use old syntax>
@@ -35,6 +35,7 @@ import logging
import salt.utils.versions
import salt.version
+from salt.exceptions import CommandExecutionError
log = logging.getLogger(__name__)
@@ -51,7 +52,7 @@ def __virtual__():
def get_release_number(name):
"""
Returns the release number of a given release code name in a
- ``MAJOR.PATCH`` format.
+ ``MAJOR.PATCH`` format (for Salt versions < 3000) or ``MAJOR`` for newer Salt versions.
If the release name has not been given an assigned release number, the
function returns a string. If the release cannot be found, it returns
@@ -66,6 +67,9 @@ def get_release_number(name):
salt '*' salt_version.get_release_number 'Oxygen'
"""
+ if not isinstance(name, str):
+ raise CommandExecutionError("'name' argument must be a string")
+
name = name.lower()
version_map = salt.version.SaltStackVersion.LNAMES
version = version_map.get(name)
diff --git a/salt/version.py b/salt/version.py
index 67719bd020..44372830b2 100644
--- a/salt/version.py
+++ b/salt/version.py
@@ -77,109 +77,109 @@ class SaltVersionsInfo(type):
ALUMINIUM = SaltVersion("Aluminium" , info=3003, released=True)
SILICON = SaltVersion("Silicon" , info=3004, released=True)
PHOSPHORUS = SaltVersion("Phosphorus" , info=3005, released=True)
- SULFUR = SaltVersion("Sulfur" , info=(3006, 0))
- CHLORINE = SaltVersion("Chlorine" , info=(3007, 0))
- ARGON = SaltVersion("Argon" , info=(3008, 0))
- POTASSIUM = SaltVersion("Potassium" , info=(3009, 0))
- CALCIUM = SaltVersion("Calcium" , info=(3010, 0))
- SCANDIUM = SaltVersion("Scandium" , info=(3011, 0))
- TITANIUM = SaltVersion("Titanium" , info=(3012, 0))
- VANADIUM = SaltVersion("Vanadium" , info=(3013, 0))
- CHROMIUM = SaltVersion("Chromium" , info=(3014, 0))
- MANGANESE = SaltVersion("Manganese" , info=(3015, 0))
- IRON = SaltVersion("Iron" , info=(3016, 0))
- COBALT = SaltVersion("Cobalt" , info=(3017, 0))
- NICKEL = SaltVersion("Nickel" , info=(3018, 0))
- COPPER = SaltVersion("Copper" , info=(3019, 0))
- ZINC = SaltVersion("Zinc" , info=(3020, 0))
- GALLIUM = SaltVersion("Gallium" , info=(3021, 0))
- GERMANIUM = SaltVersion("Germanium" , info=(3022, 0))
- ARSENIC = SaltVersion("Arsenic" , info=(3023, 0))
- SELENIUM = SaltVersion("Selenium" , info=(3024, 0))
- BROMINE = SaltVersion("Bromine" , info=(3025, 0))
- KRYPTON = SaltVersion("Krypton" , info=(3026, 0))
- RUBIDIUM = SaltVersion("Rubidium" , info=(3027, 0))
- STRONTIUM = SaltVersion("Strontium" , info=(3028, 0))
- YTTRIUM = SaltVersion("Yttrium" , info=(3029, 0))
- ZIRCONIUM = SaltVersion("Zirconium" , info=(3030, 0))
- NIOBIUM = SaltVersion("Niobium" , info=(3031, 0))
- MOLYBDENUM = SaltVersion("Molybdenum" , info=(3032, 0))
- TECHNETIUM = SaltVersion("Technetium" , info=(3033, 0))
- RUTHENIUM = SaltVersion("Ruthenium" , info=(3034, 0))
- RHODIUM = SaltVersion("Rhodium" , info=(3035, 0))
- PALLADIUM = SaltVersion("Palladium" , info=(3036, 0))
- SILVER = SaltVersion("Silver" , info=(3037, 0))
- CADMIUM = SaltVersion("Cadmium" , info=(3038, 0))
- INDIUM = SaltVersion("Indium" , info=(3039, 0))
- TIN = SaltVersion("Tin" , info=(3040, 0))
- ANTIMONY = SaltVersion("Antimony" , info=(3041, 0))
- TELLURIUM = SaltVersion("Tellurium" , info=(3042, 0))
- IODINE = SaltVersion("Iodine" , info=(3043, 0))
- XENON = SaltVersion("Xenon" , info=(3044, 0))
- CESIUM = SaltVersion("Cesium" , info=(3045, 0))
- BARIUM = SaltVersion("Barium" , info=(3046, 0))
- LANTHANUM = SaltVersion("Lanthanum" , info=(3047, 0))
- CERIUM = SaltVersion("Cerium" , info=(3048, 0))
- PRASEODYMIUM = SaltVersion("Praseodymium" , info=(3049, 0))
- NEODYMIUM = SaltVersion("Neodymium" , info=(3050, 0))
- PROMETHIUM = SaltVersion("Promethium" , info=(3051, 0))
- SAMARIUM = SaltVersion("Samarium" , info=(3052, 0))
- EUROPIUM = SaltVersion("Europium" , info=(3053, 0))
- GADOLINIUM = SaltVersion("Gadolinium" , info=(3054, 0))
- TERBIUM = SaltVersion("Terbium" , info=(3055, 0))
- DYSPROSIUM = SaltVersion("Dysprosium" , info=(3056, 0))
- HOLMIUM = SaltVersion("Holmium" , info=(3057, 0))
- ERBIUM = SaltVersion("Erbium" , info=(3058, 0))
- THULIUM = SaltVersion("Thulium" , info=(3059, 0))
- YTTERBIUM = SaltVersion("Ytterbium" , info=(3060, 0))
- LUTETIUM = SaltVersion("Lutetium" , info=(3061, 0))
- HAFNIUM = SaltVersion("Hafnium" , info=(3062, 0))
- TANTALUM = SaltVersion("Tantalum" , info=(3063, 0))
- TUNGSTEN = SaltVersion("Tungsten" , info=(3064, 0))
- RHENIUM = SaltVersion("Rhenium" , info=(3065, 0))
- OSMIUM = SaltVersion("Osmium" , info=(3066, 0))
- IRIDIUM = SaltVersion("Iridium" , info=(3067, 0))
- PLATINUM = SaltVersion("Platinum" , info=(3068, 0))
- GOLD = SaltVersion("Gold" , info=(3069, 0))
- MERCURY = SaltVersion("Mercury" , info=(3070, 0))
- THALLIUM = SaltVersion("Thallium" , info=(3071, 0))
- LEAD = SaltVersion("Lead" , info=(3072, 0))
- BISMUTH = SaltVersion("Bismuth" , info=(3073, 0))
- POLONIUM = SaltVersion("Polonium" , info=(3074, 0))
- ASTATINE = SaltVersion("Astatine" , info=(3075, 0))
- RADON = SaltVersion("Radon" , info=(3076, 0))
- FRANCIUM = SaltVersion("Francium" , info=(3077, 0))
- RADIUM = SaltVersion("Radium" , info=(3078, 0))
- ACTINIUM = SaltVersion("Actinium" , info=(3079, 0))
- THORIUM = SaltVersion("Thorium" , info=(3080, 0))
- PROTACTINIUM = SaltVersion("Protactinium" , info=(3081, 0))
- URANIUM = SaltVersion("Uranium" , info=(3082, 0))
- NEPTUNIUM = SaltVersion("Neptunium" , info=(3083, 0))
- PLUTONIUM = SaltVersion("Plutonium" , info=(3084, 0))
- AMERICIUM = SaltVersion("Americium" , info=(3085, 0))
- CURIUM = SaltVersion("Curium" , info=(3086, 0))
- BERKELIUM = SaltVersion("Berkelium" , info=(3087, 0))
- CALIFORNIUM = SaltVersion("Californium" , info=(3088, 0))
- EINSTEINIUM = SaltVersion("Einsteinium" , info=(3089, 0))
- FERMIUM = SaltVersion("Fermium" , info=(3090, 0))
- MENDELEVIUM = SaltVersion("Mendelevium" , info=(3091, 0))
- NOBELIUM = SaltVersion("Nobelium" , info=(3092, 0))
- LAWRENCIUM = SaltVersion("Lawrencium" , info=(3093, 0))
- RUTHERFORDIUM = SaltVersion("Rutherfordium", info=(3094, 0))
- DUBNIUM = SaltVersion("Dubnium" , info=(3095, 0))
- SEABORGIUM = SaltVersion("Seaborgium" , info=(3096, 0))
- BOHRIUM = SaltVersion("Bohrium" , info=(3097, 0))
- HASSIUM = SaltVersion("Hassium" , info=(3098, 0))
- MEITNERIUM = SaltVersion("Meitnerium" , info=(3099, 0))
- DARMSTADTIUM = SaltVersion("Darmstadtium" , info=(3100, 0))
- ROENTGENIUM = SaltVersion("Roentgenium" , info=(3101, 0))
- COPERNICIUM = SaltVersion("Copernicium" , info=(3102, 0))
- NIHONIUM = SaltVersion("Nihonium" , info=(3103, 0))
- FLEROVIUM = SaltVersion("Flerovium" , info=(3104, 0))
- MOSCOVIUM = SaltVersion("Moscovium" , info=(3105, 0))
- LIVERMORIUM = SaltVersion("Livermorium" , info=(3106, 0))
- TENNESSINE = SaltVersion("Tennessine" , info=(3107, 0))
- OGANESSON = SaltVersion("Oganesson" , info=(3108, 0))
+ SULFUR = SaltVersion("Sulfur" , info=3006, released=True)
+ CHLORINE = SaltVersion("Chlorine" , info=3007)
+ ARGON = SaltVersion("Argon" , info=3008)
+ POTASSIUM = SaltVersion("Potassium" , info=3009)
+ CALCIUM = SaltVersion("Calcium" , info=3010)
+ SCANDIUM = SaltVersion("Scandium" , info=3011)
+ TITANIUM = SaltVersion("Titanium" , info=3012)
+ VANADIUM = SaltVersion("Vanadium" , info=3013)
+ CHROMIUM = SaltVersion("Chromium" , info=3014)
+ MANGANESE = SaltVersion("Manganese" , info=3015)
+ IRON = SaltVersion("Iron" , info=3016)
+ COBALT = SaltVersion("Cobalt" , info=3017)
+ NICKEL = SaltVersion("Nickel" , info=3018)
+ COPPER = SaltVersion("Copper" , info=3019)
+ ZINC = SaltVersion("Zinc" , info=3020)
+ GALLIUM = SaltVersion("Gallium" , info=3021)
+ GERMANIUM = SaltVersion("Germanium" , info=3022)
+ ARSENIC = SaltVersion("Arsenic" , info=3023)
+ SELENIUM = SaltVersion("Selenium" , info=3024)
+ BROMINE = SaltVersion("Bromine" , info=3025)
+ KRYPTON = SaltVersion("Krypton" , info=3026)
+ RUBIDIUM = SaltVersion("Rubidium" , info=3027)
+ STRONTIUM = SaltVersion("Strontium" , info=3028)
+ YTTRIUM = SaltVersion("Yttrium" , info=3029)
+ ZIRCONIUM = SaltVersion("Zirconium" , info=3030)
+ NIOBIUM = SaltVersion("Niobium" , info=3031)
+ MOLYBDENUM = SaltVersion("Molybdenum" , info=3032)
+ TECHNETIUM = SaltVersion("Technetium" , info=3033)
+ RUTHENIUM = SaltVersion("Ruthenium" , info=3034)
+ RHODIUM = SaltVersion("Rhodium" , info=3035)
+ PALLADIUM = SaltVersion("Palladium" , info=3036)
+ SILVER = SaltVersion("Silver" , info=3037)
+ CADMIUM = SaltVersion("Cadmium" , info=3038)
+ INDIUM = SaltVersion("Indium" , info=3039)
+ TIN = SaltVersion("Tin" , info=3040)
+ ANTIMONY = SaltVersion("Antimony" , info=3041)
+ TELLURIUM = SaltVersion("Tellurium" , info=3042)
+ IODINE = SaltVersion("Iodine" , info=3043)
+ XENON = SaltVersion("Xenon" , info=3044)
+ CESIUM = SaltVersion("Cesium" , info=3045)
+ BARIUM = SaltVersion("Barium" , info=3046)
+ LANTHANUM = SaltVersion("Lanthanum" , info=3047)
+ CERIUM = SaltVersion("Cerium" , info=3048)
+ PRASEODYMIUM = SaltVersion("Praseodymium" , info=3049)
+ NEODYMIUM = SaltVersion("Neodymium" , info=3050)
+ PROMETHIUM = SaltVersion("Promethium" , info=3051)
+ SAMARIUM = SaltVersion("Samarium" , info=3052)
+ EUROPIUM = SaltVersion("Europium" , info=3053)
+ GADOLINIUM = SaltVersion("Gadolinium" , info=3054)
+ TERBIUM = SaltVersion("Terbium" , info=3055)
+ DYSPROSIUM = SaltVersion("Dysprosium" , info=3056)
+ HOLMIUM = SaltVersion("Holmium" , info=3057)
+ ERBIUM = SaltVersion("Erbium" , info=3058)
+ THULIUM = SaltVersion("Thulium" , info=3059)
+ YTTERBIUM = SaltVersion("Ytterbium" , info=3060)
+ LUTETIUM = SaltVersion("Lutetium" , info=3061)
+ HAFNIUM = SaltVersion("Hafnium" , info=3062)
+ TANTALUM = SaltVersion("Tantalum" , info=3063)
+ TUNGSTEN = SaltVersion("Tungsten" , info=3064)
+ RHENIUM = SaltVersion("Rhenium" , info=3065)
+ OSMIUM = SaltVersion("Osmium" , info=3066)
+ IRIDIUM = SaltVersion("Iridium" , info=3067)
+ PLATINUM = SaltVersion("Platinum" , info=3068)
+ GOLD = SaltVersion("Gold" , info=3069)
+ MERCURY = SaltVersion("Mercury" , info=3070)
+ THALLIUM = SaltVersion("Thallium" , info=3071)
+ LEAD = SaltVersion("Lead" , info=3072)
+ BISMUTH = SaltVersion("Bismuth" , info=3073)
+ POLONIUM = SaltVersion("Polonium" , info=3074)
+ ASTATINE = SaltVersion("Astatine" , info=3075)
+ RADON = SaltVersion("Radon" , info=3076)
+ FRANCIUM = SaltVersion("Francium" , info=3077)
+ RADIUM = SaltVersion("Radium" , info=3078)
+ ACTINIUM = SaltVersion("Actinium" , info=3079)
+ THORIUM = SaltVersion("Thorium" , info=3080)
+ PROTACTINIUM = SaltVersion("Protactinium" , info=3081)
+ URANIUM = SaltVersion("Uranium" , info=3082)
+ NEPTUNIUM = SaltVersion("Neptunium" , info=3083)
+ PLUTONIUM = SaltVersion("Plutonium" , info=3084)
+ AMERICIUM = SaltVersion("Americium" , info=3085)
+ CURIUM = SaltVersion("Curium" , info=3086)
+ BERKELIUM = SaltVersion("Berkelium" , info=3087)
+ CALIFORNIUM = SaltVersion("Californium" , info=3088)
+ EINSTEINIUM = SaltVersion("Einsteinium" , info=3089)
+ FERMIUM = SaltVersion("Fermium" , info=3090)
+ MENDELEVIUM = SaltVersion("Mendelevium" , info=3091)
+ NOBELIUM = SaltVersion("Nobelium" , info=3092)
+ LAWRENCIUM = SaltVersion("Lawrencium" , info=3093)
+ RUTHERFORDIUM = SaltVersion("Rutherfordium", info=3094)
+ DUBNIUM = SaltVersion("Dubnium" , info=3095)
+ SEABORGIUM = SaltVersion("Seaborgium" , info=3096)
+ BOHRIUM = SaltVersion("Bohrium" , info=3097)
+ HASSIUM = SaltVersion("Hassium" , info=3098)
+ MEITNERIUM = SaltVersion("Meitnerium" , info=3099)
+ DARMSTADTIUM = SaltVersion("Darmstadtium" , info=3100)
+ ROENTGENIUM = SaltVersion("Roentgenium" , info=3101)
+ COPERNICIUM = SaltVersion("Copernicium" , info=3102)
+ NIHONIUM = SaltVersion("Nihonium" , info=3103)
+ FLEROVIUM = SaltVersion("Flerovium" , info=3104)
+ MOSCOVIUM = SaltVersion("Moscovium" , info=3105)
+ LIVERMORIUM = SaltVersion("Livermorium" , info=3106)
+ TENNESSINE = SaltVersion("Tennessine" , info=3107)
+ OGANESSON = SaltVersion("Oganesson" , info=3108)
# <---- Please refrain from fixing whitespace -----------------------------------
# The idea is to keep this readable.
# -------------------------------------------------------------------------------
@@ -323,9 +323,7 @@ class SaltStackVersion:
self.mbugfix = mbugfix
self.pre_type = pre_type
self.pre_num = pre_num
- if self.can_have_dot_zero(major):
- vnames_key = (major, 0)
- elif self.new_version(major):
+ if self.new_version(major):
vnames_key = (major,)
else:
vnames_key = (major, minor)
@@ -476,8 +474,12 @@ class SaltStackVersion:
version_string = self.string
if self.sse:
version_string += " Enterprise"
- if (self.major, self.minor) in self.RMATCH:
- version_string += " ({})".format(self.RMATCH[(self.major, self.minor)])
+ if self.new_version(self.major):
+ rmatch_key = (self.major,)
+ else:
+ rmatch_key = (self.major, self.minor)
+ if rmatch_key in self.RMATCH:
+ version_string += " ({})".format(self.RMATCH[rmatch_key])
return version_string
@property
diff --git a/tests/pytests/unit/modules/test_salt_version.py b/tests/pytests/unit/modules/test_salt_version.py
index 6d734f6a76..4b7a7cd073 100644
--- a/tests/pytests/unit/modules/test_salt_version.py
+++ b/tests/pytests/unit/modules/test_salt_version.py
@@ -2,8 +2,11 @@
Unit tests for salt/modules/salt_version.py
"""
+import pytest
+
import salt.modules.salt_version as salt_version
import salt.version
+from salt.exceptions import CommandExecutionError
from tests.support.mock import MagicMock, patch
@@ -21,7 +24,7 @@ def test_mocked_objects():
for k, v in salt.version.SaltStackVersion.LNAMES.items():
assert k == k.lower()
assert isinstance(v, tuple)
- if sv.new_version(major=v[0]) and not sv.can_have_dot_zero(major=v[0]):
+ if sv.new_version(major=v[0]):
assert len(v) == 1
else:
assert len(v) == 2
@@ -64,6 +67,13 @@ def test_get_release_number_success_new_version():
assert salt_version.get_release_number("Neon") == "3000"
+def test_get_release_number_success_new_version_with_dot():
+ """
+ Test that a version is returned for new versioning (3006)
+ """
+ assert salt_version.get_release_number("Sulfur") == "3006"
+
+
def test_equal_success():
"""
Test that the current version is equal to the codename
@@ -83,6 +93,16 @@ def test_equal_success_new_version():
assert salt_version.equal("foo") is True
+def test_equal_success_new_version_with_dot():
+ """
+ Test that the current version is equal to the codename
+ while using the new versioning
+ """
+ with patch("salt.version.SaltStackVersion", MagicMock(return_value="3006.1")):
+ with patch("salt.version.SaltStackVersion.LNAMES", {"foo": (3006,)}):
+ assert salt_version.equal("foo") is True
+
+
def test_equal_older_codename():
"""
Test that when an older codename is passed in, the function returns False.
@@ -142,6 +162,17 @@ def test_greater_than_success_new_version():
assert salt_version.greater_than("Nitrogen") is True
+def test_greater_than_success_new_version_with_dot():
+ """
+ Test that the current version is newer than the codename
+ """
+ with patch(
+ "salt.modules.salt_version.get_release_number", MagicMock(return_value="3000")
+ ):
+ with patch("salt.version.SaltStackVersion", MagicMock(return_value="3006.0")):
+ assert salt_version.greater_than("Neon") is True
+
+
def test_greater_than_with_equal_codename():
"""
Test that when an equal codename is passed in, the function returns False.
@@ -200,6 +231,28 @@ def test_less_than_success_new_version():
assert salt_version.less_than("Fluorine") is True
+def test_less_than_success_new_version_with_dot():
+ """
+ Test that when a newer codename is passed in, the function returns True
+ using new version
+ """
+ with patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")):
+ with patch(
+ "salt.modules.salt_version.get_release_number",
+ MagicMock(return_value="3006"),
+ ):
+ assert salt_version.less_than("Fluorine") is True
+
+
+def test_less_than_do_not_crash_when_input_is_a_number():
+ """
+ Test that less_than do not crash when unexpected inputs
+ """
+ with patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")):
+ with pytest.raises(CommandExecutionError):
+ salt_version.less_than(1234)
+
+
def test_less_than_with_equal_codename():
"""
Test that when an equal codename is passed in, the function returns False.
diff --git a/tests/pytests/unit/test_version.py b/tests/pytests/unit/test_version.py
index 73befea4cf..1cb94c619c 100644
--- a/tests/pytests/unit/test_version.py
+++ b/tests/pytests/unit/test_version.py
@@ -187,7 +187,7 @@ def test_string_new_version_minor():
ver = SaltStackVersion(major=maj_ver, minor=min_ver)
assert ver.minor == min_ver
assert not ver.bugfix
- assert ver.string == "{}.{}".format(maj_ver, min_ver)
+ assert ver.string == f"{maj_ver}.{min_ver}"
def test_string_new_version_minor_as_string():
@@ -201,13 +201,13 @@ def test_string_new_version_minor_as_string():
ver = SaltStackVersion(major=maj_ver, minor=min_ver)
assert ver.minor == int(min_ver)
assert not ver.bugfix
- assert ver.string == "{}.{}".format(maj_ver, min_ver)
+ assert ver.string == f"{maj_ver}.{min_ver}"
# This only seems to happen on a cloned repo without its tags
maj_ver = "3000"
min_ver = ""
ver = SaltStackVersion(major=maj_ver, minor=min_ver)
- assert ver.minor is None, "{!r} is not {!r}".format(ver.minor, min_ver)
+ assert ver.minor is None, f"{ver.minor!r} is not {min_ver!r}"
assert not ver.bugfix
assert ver.string == maj_ver
@@ -222,7 +222,7 @@ def test_string_old_version():
min_ver = "2"
ver = SaltStackVersion(major=maj_ver, minor=min_ver)
assert ver.bugfix == 0
- assert ver.string == "{}.{}.0".format(maj_ver, min_ver)
+ assert ver.string == f"{maj_ver}.{min_ver}.0"
@pytest.mark.parametrize(
@@ -537,6 +537,8 @@ def test_versions_report_no_extensions_available():
("3000.1", "3000.1", "Neon"),
("3005", "3005", "Phosphorus"),
("3006", "3006.0", "Sulfur"),
+ ("3006.0", "3006.0", "Sulfur"),
+ ("3006.1", "3006.1", "Sulfur"),
("3015.1", "3015.1", "Manganese"),
("3109.3", "3109.3", None),
],
--
2.41.0

View File

@ -1,3 +1,32 @@
-------------------------------------------------------------------
Wed Aug 9 15:13:50 UTC 2023 - Alexander Graul <alexander.graul@suse.com>
- Create minion_id with reproducible mtime
- Fix detection of Salt codename by "salt_version" execution module
- Fix regression: multiple values for keyword argument 'saltenv' (bsc#1212844)
- Fix the regression of user.present state when group is unset (bsc#1212855)
- Fix zypper repositories always being reconfigured
- Fix utf8 handling in 'pass' renderer and make it more robust
- Prevent _pygit2.GitError: error loading known_hosts when $HOME is not set (bsc#1210994)
- Fix ModuleNotFoundError and other issues raised by salt-support module (bsc#1211591)
- tornado: Fix an open redirect in StaticFileHandler (CVE-2023-28370, bsc#1211741)
- Make master_tops compatible with Salt 3000 and older minions (bsc#1212516) (bsc#1212517)
- Avoid failures due transactional_update module not available in Salt 3006.0 (bsc#1211754)
- Avoid conflicts with Salt dependencies versions (bsc#1211612)
- Added:
* fix-utf8-handling-in-pass-renderer-and-make-it-more-.patch
* fix-the-regression-of-user.present-state-when-group-.patch
* make-master_tops-compatible-with-salt-3000-and-older.patch
* avoid-conflicts-with-dependencies-versions-bsc-12116.patch
* tornado-fix-an-open-redirect-in-staticfilehandler-cv.patch
* fix-regression-multiple-values-for-keyword-argument-.patch
* zypper-pkgrepo-alreadyconfigured-585.patch
* mark-salt-3006-as-released-586.patch
* fix-some-issues-detected-in-salt-support-cli-module-.patch
* define-__virtualname__-for-transactional_update-modu.patch
* 3006.0-prevent-_pygit2.giterror-error-loading-known_.patch
-------------------------------------------------------------------
Fri May 5 08:29:26 UTC 2023 - Alexander Graul <alexander.graul@suse.com>

View File

@ -267,6 +267,28 @@ Patch60: skip-package-names-without-colon-bsc-1208691-578.patch
Patch61: fix-version-detection-and-avoid-building-and-testing.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64113
Patch62: make-sure-the-file-client-is-destroyed-upon-used.patch
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/581
Patch63: avoid-conflicts-with-dependencies-versions-bsc-12116.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64369
Patch64: define-__virtualname__-for-transactional_update-modu.patch
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/587
Patch65: make-master_tops-compatible-with-salt-3000-and-older.patch
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/583
Patch66: tornado-fix-an-open-redirect-in-staticfilehandler-cv.patch
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/580
Patch67: fix-some-issues-detected-in-salt-support-cli-module-.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64510
Patch68: 3006.0-prevent-_pygit2.giterror-error-loading-known_.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64300
Patch69: fix-utf8-handling-in-pass-renderer-and-make-it-more-.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/63403
Patch70: zypper-pkgrepo-alreadyconfigured-585.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64530
Patch71: fix-the-regression-of-user.present-state-when-group-.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64179
Patch72: fix-regression-multiple-values-for-keyword-argument-.patch
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64554
Patch73: mark-salt-3006-as-released-586.patch
### IMPORTANT: The line below is used as a snippet marker. Do not touch it.
### SALT PATCHES LIST END
@ -844,7 +866,7 @@ install -Dpm 0755 scripts/suse/watchdog/salt-daemon-watcher %{buildroot}%{_bindi
#
## install config files
install -Dpm 0640 conf/minion %{buildroot}%{_sysconfdir}/salt/minion
install -Dpm 0640 /dev/null %{buildroot}%{_sysconfdir}/salt/minion_id
touch -m 0640 -r conf/minion %{buildroot}%{_sysconfdir}/salt/minion_id # ghost file
install -Dpm 0640 conf/master %{buildroot}%{_sysconfdir}/salt/master
install -Dpm 0640 conf/roster %{buildroot}%{_sysconfdir}/salt/roster
install -Dpm 0640 conf/cloud %{buildroot}%{_sysconfdir}/salt/cloud

View File

@ -0,0 +1,35 @@
From 78f5a76315891168d24e923d2b08211baefefb4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
<psuarezhernandez@suse.com>
Date: Thu, 22 Jun 2023 16:36:20 +0100
Subject: [PATCH] tornado: Fix an open redirect in StaticFileHandler
(CVE-2023-28370, bsc#1211741) (#583)
---
salt/ext/tornado/web.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/salt/ext/tornado/web.py b/salt/ext/tornado/web.py
index 60bde695d3..97fadcf87d 100644
--- a/salt/ext/tornado/web.py
+++ b/salt/ext/tornado/web.py
@@ -2544,6 +2544,15 @@ class StaticFileHandler(RequestHandler):
# but there is some prefix to the path that was already
# trimmed by the routing
if not self.request.path.endswith("/"):
+ if self.request.path.startswith("//"):
+ # A redirect with two initial slashes is a "protocol-relative" URL.
+ # This means the next path segment is treated as a hostname instead
+ # of a part of the path, making this effectively an open redirect.
+ # Reject paths starting with two slashes to prevent this.
+ # This is only reachable under certain configurations.
+ raise HTTPError(
+ 403, "cannot redirect path with two initial slashes"
+ )
self.redirect(self.request.path + "/", permanent=True)
return
absolute_path = os.path.join(absolute_path, self.default_filename)
--
2.41.0

View File

@ -0,0 +1,366 @@
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