Sync from SUSE:ALP:Source:Standard:1.0 salt revision e2b3ae454b5ecd93e077fcfe287e4ca7
This commit is contained in:
parent
101832fd70
commit
981d99ce30
@ -1 +1 @@
|
|||||||
3becea2e5b00beff724c22a8ae320d4567031c7b
|
d0c2f35ff4a0b21786b20c884cbb191ad2e63904
|
97
allow-all-primitive-grain-types-for-autosign_grains-.patch
Normal file
97
allow-all-primitive-grain-types-for-autosign_grains-.patch
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
From ae4e1d1cc15b3c510bdd774a1dfeff67c522324a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marek Czernek <marek.czernek@suse.com>
|
||||||
|
Date: Tue, 17 Oct 2023 13:05:00 +0200
|
||||||
|
Subject: [PATCH] Allow all primitive grain types for autosign_grains
|
||||||
|
(#607)
|
||||||
|
|
||||||
|
* Allow all primitive grain types for autosign_grains
|
||||||
|
|
||||||
|
Signed-off-by: Marek Czernek <marek.czernek@suse.com>
|
||||||
|
|
||||||
|
* blacken daemons/masterapi.py and its test_auto_key
|
||||||
|
|
||||||
|
Signed-off-by: Marek Czernek <marek.czernek@suse.com>
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
Signed-off-by: Marek Czernek <marek.czernek@suse.com>
|
||||||
|
Co-authored-by: Alexander Graul <agraul@suse.com>
|
||||||
|
---
|
||||||
|
changelog/61416.fixed.md | 1 +
|
||||||
|
changelog/63708.fixed.md | 1 +
|
||||||
|
salt/daemons/masterapi.py | 2 +-
|
||||||
|
.../pytests/unit/daemons/masterapi/test_auto_key.py | 13 +++++++------
|
||||||
|
4 files changed, 10 insertions(+), 7 deletions(-)
|
||||||
|
create mode 100644 changelog/61416.fixed.md
|
||||||
|
create mode 100644 changelog/63708.fixed.md
|
||||||
|
|
||||||
|
diff --git a/changelog/61416.fixed.md b/changelog/61416.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..3203a0a1c6
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/61416.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Allow all primitive grain types for autosign_grains
|
||||||
|
diff --git a/changelog/63708.fixed.md b/changelog/63708.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..3203a0a1c6
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/63708.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Allow all primitive grain types for autosign_grains
|
||||||
|
diff --git a/salt/daemons/masterapi.py b/salt/daemons/masterapi.py
|
||||||
|
index 3716c63d99..54aca64a76 100644
|
||||||
|
--- a/salt/daemons/masterapi.py
|
||||||
|
+++ b/salt/daemons/masterapi.py
|
||||||
|
@@ -366,7 +366,7 @@ class AutoKey:
|
||||||
|
line = salt.utils.stringutils.to_unicode(line).strip()
|
||||||
|
if line.startswith("#"):
|
||||||
|
continue
|
||||||
|
- if autosign_grains[grain] == line:
|
||||||
|
+ if str(autosign_grains[grain]) == line:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
diff --git a/tests/pytests/unit/daemons/masterapi/test_auto_key.py b/tests/pytests/unit/daemons/masterapi/test_auto_key.py
|
||||||
|
index b3657b7f1b..54c3f22d2a 100644
|
||||||
|
--- a/tests/pytests/unit/daemons/masterapi/test_auto_key.py
|
||||||
|
+++ b/tests/pytests/unit/daemons/masterapi/test_auto_key.py
|
||||||
|
@@ -17,11 +17,11 @@ def gen_permissions(owner="", group="", others=""):
|
||||||
|
"""
|
||||||
|
ret = 0
|
||||||
|
for c in owner:
|
||||||
|
- ret |= getattr(stat, "S_I{}USR".format(c.upper()), 0)
|
||||||
|
+ ret |= getattr(stat, f"S_I{c.upper()}USR", 0)
|
||||||
|
for c in group:
|
||||||
|
- ret |= getattr(stat, "S_I{}GRP".format(c.upper()), 0)
|
||||||
|
+ ret |= getattr(stat, f"S_I{c.upper()}GRP", 0)
|
||||||
|
for c in others:
|
||||||
|
- ret |= getattr(stat, "S_I{}OTH".format(c.upper()), 0)
|
||||||
|
+ ret |= getattr(stat, f"S_I{c.upper()}OTH", 0)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@@ -256,16 +256,17 @@ def test_check_autosign_grains_no_autosign_grains_dir(auto_key):
|
||||||
|
_test_check_autosign_grains(test_func, auto_key, autosign_grains_dir=None)
|
||||||
|
|
||||||
|
|
||||||
|
-def test_check_autosign_grains_accept(auto_key):
|
||||||
|
+@pytest.mark.parametrize("grain_value", ["test_value", 123, True])
|
||||||
|
+def test_check_autosign_grains_accept(grain_value, auto_key):
|
||||||
|
"""
|
||||||
|
Asserts that autosigning from grains passes when a matching grain value is in an
|
||||||
|
autosign_grain file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_func(*args):
|
||||||
|
- assert auto_key.check_autosign_grains({"test_grain": "test_value"}) is True
|
||||||
|
+ assert auto_key.check_autosign_grains({"test_grain": grain_value}) is True
|
||||||
|
|
||||||
|
- file_content = "#test_ignore\ntest_value"
|
||||||
|
+ file_content = f"#test_ignore\n{grain_value}"
|
||||||
|
_test_check_autosign_grains(test_func, auto_key, file_content=file_content)
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
164
allow-kwargs-for-fileserver-roots-update-bsc-1218482.patch
Normal file
164
allow-kwargs-for-fileserver-roots-update-bsc-1218482.patch
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
From 8ae54e8a0e12193507f1936f363c3438b4a006ee Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Yeray=20Guti=C3=A9rrez=20Cedr=C3=A9s?=
|
||||||
|
<yeray.gutierrez@suse.com>
|
||||||
|
Date: Tue, 23 Jan 2024 15:33:28 +0000
|
||||||
|
Subject: [PATCH] Allow kwargs for fileserver roots update
|
||||||
|
(bsc#1218482) (#618)
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
* Allow kwargs for fileserver roots update (bsc#1218482)
|
||||||
|
|
||||||
|
* Prevent exceptions with fileserver.update when called via state
|
||||||
|
|
||||||
|
* Fix wrong logic and enhance tests around fileserver.update
|
||||||
|
|
||||||
|
* Remove test which is not longer valid
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
Co-authored-by: Pablo Suárez Hernández <psuarezhernandez@suse.com>
|
||||||
|
---
|
||||||
|
changelog/65819.fixed.md | 1 +
|
||||||
|
salt/fileserver/roots.py | 8 ++--
|
||||||
|
salt/runners/fileserver.py | 6 +++
|
||||||
|
tests/integration/runners/test_fileserver.py | 40 ++++++++++++++++++--
|
||||||
|
tests/pytests/unit/fileserver/test_roots.py | 2 +-
|
||||||
|
5 files changed, 47 insertions(+), 10 deletions(-)
|
||||||
|
create mode 100644 changelog/65819.fixed.md
|
||||||
|
|
||||||
|
diff --git a/changelog/65819.fixed.md b/changelog/65819.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..432f5c791c
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/65819.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Prevent exceptions with fileserver.update when called via state
|
||||||
|
diff --git a/salt/fileserver/roots.py b/salt/fileserver/roots.py
|
||||||
|
index 4880cbab9b..a02b597c6f 100644
|
||||||
|
--- a/salt/fileserver/roots.py
|
||||||
|
+++ b/salt/fileserver/roots.py
|
||||||
|
@@ -193,9 +193,7 @@ def update():
|
||||||
|
os.makedirs(mtime_map_path_dir)
|
||||||
|
with salt.utils.files.fopen(mtime_map_path, "wb") as fp_:
|
||||||
|
for file_path, mtime in new_mtime_map.items():
|
||||||
|
- fp_.write(
|
||||||
|
- salt.utils.stringutils.to_bytes("{}:{}\n".format(file_path, mtime))
|
||||||
|
- )
|
||||||
|
+ fp_.write(salt.utils.stringutils.to_bytes(f"{file_path}:{mtime}\n"))
|
||||||
|
|
||||||
|
if __opts__.get("fileserver_events", False):
|
||||||
|
# if there is a change, fire an event
|
||||||
|
@@ -326,11 +324,11 @@ def _file_lists(load, form):
|
||||||
|
return []
|
||||||
|
list_cache = os.path.join(
|
||||||
|
list_cachedir,
|
||||||
|
- "{}.p".format(salt.utils.files.safe_filename_leaf(actual_saltenv)),
|
||||||
|
+ f"{salt.utils.files.safe_filename_leaf(actual_saltenv)}.p",
|
||||||
|
)
|
||||||
|
w_lock = os.path.join(
|
||||||
|
list_cachedir,
|
||||||
|
- ".{}.w".format(salt.utils.files.safe_filename_leaf(actual_saltenv)),
|
||||||
|
+ f".{salt.utils.files.safe_filename_leaf(actual_saltenv)}.w",
|
||||||
|
)
|
||||||
|
cache_match, refresh_cache, save_cache = salt.fileserver.check_file_list_cache(
|
||||||
|
__opts__, form, list_cache, w_lock
|
||||||
|
diff --git a/salt/runners/fileserver.py b/salt/runners/fileserver.py
|
||||||
|
index d75d7de0cf..1ed05b68ca 100644
|
||||||
|
--- a/salt/runners/fileserver.py
|
||||||
|
+++ b/salt/runners/fileserver.py
|
||||||
|
@@ -350,6 +350,12 @@ def update(backend=None, **kwargs):
|
||||||
|
salt-run fileserver.update backend=git remotes=myrepo,yourrepo
|
||||||
|
"""
|
||||||
|
fileserver = salt.fileserver.Fileserver(__opts__)
|
||||||
|
+
|
||||||
|
+ # Remove possible '__pub_user' in kwargs as it is not expected
|
||||||
|
+ # on "update" function for the different fileserver backends.
|
||||||
|
+ if "__pub_user" in kwargs:
|
||||||
|
+ del kwargs["__pub_user"]
|
||||||
|
+
|
||||||
|
fileserver.update(back=backend, **kwargs)
|
||||||
|
return True
|
||||||
|
|
||||||
|
diff --git a/tests/integration/runners/test_fileserver.py b/tests/integration/runners/test_fileserver.py
|
||||||
|
index ae8ab766aa..62f0da0c4a 100644
|
||||||
|
--- a/tests/integration/runners/test_fileserver.py
|
||||||
|
+++ b/tests/integration/runners/test_fileserver.py
|
||||||
|
@@ -202,15 +202,31 @@ class FileserverTest(ShellCase):
|
||||||
|
fileserver.update
|
||||||
|
"""
|
||||||
|
ret = self.run_run_plus(fun="fileserver.update")
|
||||||
|
- self.assertTrue(ret["return"])
|
||||||
|
+ self.assertTrue(ret["return"] is True)
|
||||||
|
|
||||||
|
# Backend submitted as a string
|
||||||
|
ret = self.run_run_plus(fun="fileserver.update", backend="roots")
|
||||||
|
- self.assertTrue(ret["return"])
|
||||||
|
+ self.assertTrue(ret["return"] is True)
|
||||||
|
|
||||||
|
# Backend submitted as a list
|
||||||
|
ret = self.run_run_plus(fun="fileserver.update", backend=["roots"])
|
||||||
|
- self.assertTrue(ret["return"])
|
||||||
|
+ self.assertTrue(ret["return"] is True)
|
||||||
|
+
|
||||||
|
+ # Possible '__pub_user' is removed from kwargs
|
||||||
|
+ ret = self.run_run_plus(
|
||||||
|
+ fun="fileserver.update", backend=["roots"], __pub_user="foo"
|
||||||
|
+ )
|
||||||
|
+ self.assertTrue(ret["return"] is True)
|
||||||
|
+
|
||||||
|
+ # Unknown arguments
|
||||||
|
+ ret = self.run_run_plus(
|
||||||
|
+ fun="fileserver.update", backend=["roots"], unknown_arg="foo"
|
||||||
|
+ )
|
||||||
|
+ self.assertIn(
|
||||||
|
+ "Passed invalid arguments: update() got an unexpected keyword argument"
|
||||||
|
+ " 'unknown_arg'",
|
||||||
|
+ ret["return"],
|
||||||
|
+ )
|
||||||
|
|
||||||
|
# Other arguments are passed to backend
|
||||||
|
def mock_gitfs_update(remotes=None):
|
||||||
|
@@ -225,7 +241,23 @@ class FileserverTest(ShellCase):
|
||||||
|
ret = self.run_run_plus(
|
||||||
|
fun="fileserver.update", backend="gitfs", remotes="myrepo,yourrepo"
|
||||||
|
)
|
||||||
|
- self.assertTrue(ret["return"])
|
||||||
|
+ self.assertTrue(ret["return"] is True)
|
||||||
|
+ mock_backend_func.assert_called_once_with(remotes="myrepo,yourrepo")
|
||||||
|
+
|
||||||
|
+ # Possible '__pub_user' arguments are removed from kwargs
|
||||||
|
+ mock_backend_func = create_autospec(mock_gitfs_update)
|
||||||
|
+ mock_return_value = {
|
||||||
|
+ "gitfs.envs": None, # This is needed to activate the backend
|
||||||
|
+ "gitfs.update": mock_backend_func,
|
||||||
|
+ }
|
||||||
|
+ with patch("salt.loader.fileserver", MagicMock(return_value=mock_return_value)):
|
||||||
|
+ ret = self.run_run_plus(
|
||||||
|
+ fun="fileserver.update",
|
||||||
|
+ backend="gitfs",
|
||||||
|
+ remotes="myrepo,yourrepo",
|
||||||
|
+ __pub_user="foo",
|
||||||
|
+ )
|
||||||
|
+ self.assertTrue(ret["return"] is True)
|
||||||
|
mock_backend_func.assert_called_once_with(remotes="myrepo,yourrepo")
|
||||||
|
|
||||||
|
# Unknown arguments are passed to backend
|
||||||
|
diff --git a/tests/pytests/unit/fileserver/test_roots.py b/tests/pytests/unit/fileserver/test_roots.py
|
||||||
|
index a8a80eea17..96bceb0fd3 100644
|
||||||
|
--- a/tests/pytests/unit/fileserver/test_roots.py
|
||||||
|
+++ b/tests/pytests/unit/fileserver/test_roots.py
|
||||||
|
@@ -236,7 +236,7 @@ def test_update_mtime_map():
|
||||||
|
# between Python releases.
|
||||||
|
lines_written = sorted(mtime_map_mock.write_calls())
|
||||||
|
expected = sorted(
|
||||||
|
- salt.utils.stringutils.to_bytes("{key}:{val}\n".format(key=key, val=val))
|
||||||
|
+ salt.utils.stringutils.to_bytes(f"{key}:{val}\n")
|
||||||
|
for key, val in new_mtime_map.items()
|
||||||
|
)
|
||||||
|
assert lines_written == expected, lines_written
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
||||||
|
|
101
dereference-symlinks-to-set-proper-__cli-opt-bsc-121.patch
Normal file
101
dereference-symlinks-to-set-proper-__cli-opt-bsc-121.patch
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
From 9942c488b1e74f2c6f187fcef3556fe53382bb4c Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Mon, 13 Nov 2023 15:04:14 +0000
|
||||||
|
Subject: [PATCH] Dereference symlinks to set proper __cli opt
|
||||||
|
(bsc#1215963) (#611)
|
||||||
|
|
||||||
|
* Dereference symlinks to set proper __cli
|
||||||
|
|
||||||
|
* Add changelog entry
|
||||||
|
|
||||||
|
* Add unit tests to check path is expanded
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
Co-authored-by: vzhestkov <vzhestkov@suse.com>
|
||||||
|
---
|
||||||
|
changelog/65435.fixed.md | 1 +
|
||||||
|
salt/config/__init__.py | 8 ++++++--
|
||||||
|
tests/pytests/unit/config/test_master_config.py | 13 +++++++++++++
|
||||||
|
tests/pytests/unit/config/test_minion_config.py | 13 +++++++++++++
|
||||||
|
4 files changed, 33 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 changelog/65435.fixed.md
|
||||||
|
create mode 100644 tests/pytests/unit/config/test_master_config.py
|
||||||
|
create mode 100644 tests/pytests/unit/config/test_minion_config.py
|
||||||
|
|
||||||
|
diff --git a/changelog/65435.fixed.md b/changelog/65435.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..5fa532891d
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/65435.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Dereference symlinks to set proper __cli opt
|
||||||
|
diff --git a/salt/config/__init__.py b/salt/config/__init__.py
|
||||||
|
index 43182f3f92..d8258a4dbc 100644
|
||||||
|
--- a/salt/config/__init__.py
|
||||||
|
+++ b/salt/config/__init__.py
|
||||||
|
@@ -3747,7 +3747,9 @@ def apply_minion_config(
|
||||||
|
)
|
||||||
|
opts["fileserver_backend"][idx] = new_val
|
||||||
|
|
||||||
|
- opts["__cli"] = salt.utils.stringutils.to_unicode(os.path.basename(sys.argv[0]))
|
||||||
|
+ opts["__cli"] = salt.utils.stringutils.to_unicode(
|
||||||
|
+ os.path.basename(salt.utils.path.expand(sys.argv[0]))
|
||||||
|
+ )
|
||||||
|
|
||||||
|
# No ID provided. Will getfqdn save us?
|
||||||
|
using_ip_for_id = False
|
||||||
|
@@ -3949,7 +3951,9 @@ def apply_master_config(overrides=None, defaults=None):
|
||||||
|
)
|
||||||
|
opts["keep_acl_in_token"] = True
|
||||||
|
|
||||||
|
- opts["__cli"] = salt.utils.stringutils.to_unicode(os.path.basename(sys.argv[0]))
|
||||||
|
+ opts["__cli"] = salt.utils.stringutils.to_unicode(
|
||||||
|
+ os.path.basename(salt.utils.path.expand(sys.argv[0]))
|
||||||
|
+ )
|
||||||
|
|
||||||
|
if "environment" in opts:
|
||||||
|
if opts["saltenv"] is not None:
|
||||||
|
diff --git a/tests/pytests/unit/config/test_master_config.py b/tests/pytests/unit/config/test_master_config.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..c9de8a7892
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/pytests/unit/config/test_master_config.py
|
||||||
|
@@ -0,0 +1,13 @@
|
||||||
|
+import salt.config
|
||||||
|
+from tests.support.mock import MagicMock, patch
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test___cli_path_is_expanded():
|
||||||
|
+ defaults = salt.config.DEFAULT_MASTER_OPTS.copy()
|
||||||
|
+ overrides = {}
|
||||||
|
+ with patch(
|
||||||
|
+ "salt.utils.path.expand", MagicMock(return_value="/path/to/testcli")
|
||||||
|
+ ) as expand_mock:
|
||||||
|
+ opts = salt.config.apply_master_config(overrides, defaults)
|
||||||
|
+ assert expand_mock.called
|
||||||
|
+ assert opts["__cli"] == "testcli"
|
||||||
|
diff --git a/tests/pytests/unit/config/test_minion_config.py b/tests/pytests/unit/config/test_minion_config.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..34aa84daa7
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/pytests/unit/config/test_minion_config.py
|
||||||
|
@@ -0,0 +1,13 @@
|
||||||
|
+import salt.config
|
||||||
|
+from tests.support.mock import MagicMock, patch
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test___cli_path_is_expanded():
|
||||||
|
+ defaults = salt.config.DEFAULT_MINION_OPTS.copy()
|
||||||
|
+ overrides = {}
|
||||||
|
+ with patch(
|
||||||
|
+ "salt.utils.path.expand", MagicMock(return_value="/path/to/testcli")
|
||||||
|
+ ) as expand_mock:
|
||||||
|
+ opts = salt.config.apply_minion_config(overrides, defaults)
|
||||||
|
+ assert expand_mock.called
|
||||||
|
+ assert opts["__cli"] == "testcli"
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
||||||
|
|
346
enable-keepalive-probes-for-salt-ssh-executions-bsc-.patch
Normal file
346
enable-keepalive-probes-for-salt-ssh-executions-bsc-.patch
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
From 5303cc612bcbdb1ec45ede397ca1e2ca12ba3bd3 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Fri, 1 Dec 2023 10:59:30 +0000
|
||||||
|
Subject: [PATCH] Enable "KeepAlive" probes for Salt SSH executions
|
||||||
|
(bsc#1211649) (#610)
|
||||||
|
|
||||||
|
* Enable KeepAlive probes for Salt SSH connections (bsc#1211649)
|
||||||
|
|
||||||
|
* Add tests for Salt SSH keepalive options
|
||||||
|
|
||||||
|
* Add changelog file
|
||||||
|
|
||||||
|
* Make changes suggested by pre-commit
|
||||||
|
---
|
||||||
|
changelog/65488.added.md | 1 +
|
||||||
|
salt/client/ssh/__init__.py | 32 +++++++++---
|
||||||
|
salt/client/ssh/client.py | 13 ++++-
|
||||||
|
salt/client/ssh/shell.py | 12 +++++
|
||||||
|
salt/config/__init__.py | 6 +++
|
||||||
|
salt/utils/parsers.py | 19 +++++++
|
||||||
|
tests/pytests/unit/client/ssh/test_single.py | 55 ++++++++++++++++++++
|
||||||
|
tests/pytests/unit/client/ssh/test_ssh.py | 3 ++
|
||||||
|
8 files changed, 133 insertions(+), 8 deletions(-)
|
||||||
|
create mode 100644 changelog/65488.added.md
|
||||||
|
|
||||||
|
diff --git a/changelog/65488.added.md b/changelog/65488.added.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..78476cec11
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/65488.added.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Enable "KeepAlive" probes for Salt SSH executions
|
||||||
|
diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py
|
||||||
|
index 1e143f9e30..1d8426b7c2 100644
|
||||||
|
--- a/salt/client/ssh/__init__.py
|
||||||
|
+++ b/salt/client/ssh/__init__.py
|
||||||
|
@@ -50,8 +50,8 @@ import salt.utils.thin
|
||||||
|
import salt.utils.url
|
||||||
|
import salt.utils.verify
|
||||||
|
from salt._logging import LOG_LEVELS
|
||||||
|
-from salt._logging.mixins import MultiprocessingStateMixin
|
||||||
|
from salt._logging.impl import LOG_LOCK
|
||||||
|
+from salt._logging.mixins import MultiprocessingStateMixin
|
||||||
|
from salt.template import compile_template
|
||||||
|
from salt.utils.process import Process
|
||||||
|
from salt.utils.zeromq import zmq
|
||||||
|
@@ -307,6 +307,18 @@ class SSH(MultiprocessingStateMixin):
|
||||||
|
"ssh_timeout", salt.config.DEFAULT_MASTER_OPTS["ssh_timeout"]
|
||||||
|
)
|
||||||
|
+ self.opts.get("timeout", salt.config.DEFAULT_MASTER_OPTS["timeout"]),
|
||||||
|
+ "keepalive": self.opts.get(
|
||||||
|
+ "ssh_keepalive",
|
||||||
|
+ salt.config.DEFAULT_MASTER_OPTS["ssh_keepalive"],
|
||||||
|
+ ),
|
||||||
|
+ "keepalive_interval": self.opts.get(
|
||||||
|
+ "ssh_keepalive_interval",
|
||||||
|
+ salt.config.DEFAULT_MASTER_OPTS["ssh_keepalive_interval"],
|
||||||
|
+ ),
|
||||||
|
+ "keepalive_count_max": self.opts.get(
|
||||||
|
+ "ssh_keepalive_count_max",
|
||||||
|
+ salt.config.DEFAULT_MASTER_OPTS["ssh_keepalive_count_max"],
|
||||||
|
+ ),
|
||||||
|
"sudo": self.opts.get(
|
||||||
|
"ssh_sudo", salt.config.DEFAULT_MASTER_OPTS["ssh_sudo"]
|
||||||
|
),
|
||||||
|
@@ -557,7 +569,7 @@ class SSH(MultiprocessingStateMixin):
|
||||||
|
mods=self.mods,
|
||||||
|
fsclient=self.fsclient,
|
||||||
|
thin=self.thin,
|
||||||
|
- **target
|
||||||
|
+ **target,
|
||||||
|
)
|
||||||
|
if salt.utils.path.which("ssh-copy-id"):
|
||||||
|
# we have ssh-copy-id, use it!
|
||||||
|
@@ -573,7 +585,7 @@ class SSH(MultiprocessingStateMixin):
|
||||||
|
mods=self.mods,
|
||||||
|
fsclient=self.fsclient,
|
||||||
|
thin=self.thin,
|
||||||
|
- **target
|
||||||
|
+ **target,
|
||||||
|
)
|
||||||
|
stdout, stderr, retcode = single.cmd_block()
|
||||||
|
try:
|
||||||
|
@@ -601,7 +613,7 @@ class SSH(MultiprocessingStateMixin):
|
||||||
|
fsclient=self.fsclient,
|
||||||
|
thin=self.thin,
|
||||||
|
mine=mine,
|
||||||
|
- **target
|
||||||
|
+ **target,
|
||||||
|
)
|
||||||
|
ret = {"id": single.id}
|
||||||
|
stdout, stderr, retcode = single.run()
|
||||||
|
@@ -1022,7 +1034,10 @@ class Single:
|
||||||
|
remote_port_forwards=None,
|
||||||
|
winrm=False,
|
||||||
|
ssh_options=None,
|
||||||
|
- **kwargs
|
||||||
|
+ keepalive=True,
|
||||||
|
+ keepalive_interval=60,
|
||||||
|
+ keepalive_count_max=3,
|
||||||
|
+ **kwargs,
|
||||||
|
):
|
||||||
|
# Get mine setting and mine_functions if defined in kwargs (from roster)
|
||||||
|
self.mine = mine
|
||||||
|
@@ -1081,6 +1096,9 @@ class Single:
|
||||||
|
"priv": priv,
|
||||||
|
"priv_passwd": priv_passwd,
|
||||||
|
"timeout": timeout,
|
||||||
|
+ "keepalive": keepalive,
|
||||||
|
+ "keepalive_interval": keepalive_interval,
|
||||||
|
+ "keepalive_count_max": keepalive_count_max,
|
||||||
|
"sudo": sudo,
|
||||||
|
"tty": tty,
|
||||||
|
"mods": self.mods,
|
||||||
|
@@ -1302,7 +1320,7 @@ class Single:
|
||||||
|
self.id,
|
||||||
|
fsclient=self.fsclient,
|
||||||
|
minion_opts=self.minion_opts,
|
||||||
|
- **self.target
|
||||||
|
+ **self.target,
|
||||||
|
)
|
||||||
|
|
||||||
|
opts_pkg = pre_wrapper["test.opts_pkg"]() # pylint: disable=E1102
|
||||||
|
@@ -1388,7 +1406,7 @@ class Single:
|
||||||
|
self.id,
|
||||||
|
fsclient=self.fsclient,
|
||||||
|
minion_opts=self.minion_opts,
|
||||||
|
- **self.target
|
||||||
|
+ **self.target,
|
||||||
|
)
|
||||||
|
wrapper.fsclient.opts["cachedir"] = opts["cachedir"]
|
||||||
|
self.wfuncs = salt.loader.ssh_wrapper(opts, wrapper, self.context)
|
||||||
|
diff --git a/salt/client/ssh/client.py b/salt/client/ssh/client.py
|
||||||
|
index 0b67598fc6..a00f5de423 100644
|
||||||
|
--- a/salt/client/ssh/client.py
|
||||||
|
+++ b/salt/client/ssh/client.py
|
||||||
|
@@ -52,6 +52,9 @@ class SSHClient:
|
||||||
|
("ssh_priv_passwd", str),
|
||||||
|
("ssh_identities_only", bool),
|
||||||
|
("ssh_remote_port_forwards", str),
|
||||||
|
+ ("ssh_keepalive", bool),
|
||||||
|
+ ("ssh_keepalive_interval", int),
|
||||||
|
+ ("ssh_keepalive_count_max", int),
|
||||||
|
("ssh_options", list),
|
||||||
|
("ssh_max_procs", int),
|
||||||
|
("ssh_askpass", bool),
|
||||||
|
@@ -108,7 +111,15 @@ class SSHClient:
|
||||||
|
return sane_kwargs
|
||||||
|
|
||||||
|
def _prep_ssh(
|
||||||
|
- self, tgt, fun, arg=(), timeout=None, tgt_type="glob", kwarg=None, context=None, **kwargs
|
||||||
|
+ self,
|
||||||
|
+ tgt,
|
||||||
|
+ fun,
|
||||||
|
+ arg=(),
|
||||||
|
+ timeout=None,
|
||||||
|
+ tgt_type="glob",
|
||||||
|
+ kwarg=None,
|
||||||
|
+ context=None,
|
||||||
|
+ **kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Prepare the arguments
|
||||||
|
diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py
|
||||||
|
index bc1ad034df..182e2c19e3 100644
|
||||||
|
--- a/salt/client/ssh/shell.py
|
||||||
|
+++ b/salt/client/ssh/shell.py
|
||||||
|
@@ -85,6 +85,9 @@ class Shell:
|
||||||
|
remote_port_forwards=None,
|
||||||
|
winrm=False,
|
||||||
|
ssh_options=None,
|
||||||
|
+ keepalive=True,
|
||||||
|
+ keepalive_interval=None,
|
||||||
|
+ keepalive_count_max=None,
|
||||||
|
):
|
||||||
|
self.opts = opts
|
||||||
|
# ssh <ipv6>, but scp [<ipv6]:/path
|
||||||
|
@@ -95,6 +98,9 @@ class Shell:
|
||||||
|
self.priv = priv
|
||||||
|
self.priv_passwd = priv_passwd
|
||||||
|
self.timeout = timeout
|
||||||
|
+ self.keepalive = keepalive
|
||||||
|
+ self.keepalive_interval = keepalive_interval
|
||||||
|
+ self.keepalive_count_max = keepalive_count_max
|
||||||
|
self.sudo = sudo
|
||||||
|
self.tty = tty
|
||||||
|
self.mods = mods
|
||||||
|
@@ -130,6 +136,9 @@ class Shell:
|
||||||
|
if self.opts.get("_ssh_version", (0,)) > (4, 9):
|
||||||
|
options.append("GSSAPIAuthentication=no")
|
||||||
|
options.append("ConnectTimeout={}".format(self.timeout))
|
||||||
|
+ if self.keepalive:
|
||||||
|
+ options.append(f"ServerAliveInterval={self.keepalive_interval}")
|
||||||
|
+ options.append(f"ServerAliveCountMax={self.keepalive_count_max}")
|
||||||
|
if self.opts.get("ignore_host_keys"):
|
||||||
|
options.append("StrictHostKeyChecking=no")
|
||||||
|
if self.opts.get("no_host_keys"):
|
||||||
|
@@ -165,6 +174,9 @@ class Shell:
|
||||||
|
if self.opts["_ssh_version"] > (4, 9):
|
||||||
|
options.append("GSSAPIAuthentication=no")
|
||||||
|
options.append("ConnectTimeout={}".format(self.timeout))
|
||||||
|
+ if self.keepalive:
|
||||||
|
+ options.append(f"ServerAliveInterval={self.keepalive_interval}")
|
||||||
|
+ options.append(f"ServerAliveCountMax={self.keepalive_count_max}")
|
||||||
|
if self.opts.get("ignore_host_keys"):
|
||||||
|
options.append("StrictHostKeyChecking=no")
|
||||||
|
if self.opts.get("no_host_keys"):
|
||||||
|
diff --git a/salt/config/__init__.py b/salt/config/__init__.py
|
||||||
|
index d8258a4dbc..68f2b0f674 100644
|
||||||
|
--- a/salt/config/__init__.py
|
||||||
|
+++ b/salt/config/__init__.py
|
||||||
|
@@ -822,6 +822,9 @@ VALID_OPTS = immutabletypes.freeze(
|
||||||
|
"ssh_scan_ports": str,
|
||||||
|
"ssh_scan_timeout": float,
|
||||||
|
"ssh_identities_only": bool,
|
||||||
|
+ "ssh_keepalive": bool,
|
||||||
|
+ "ssh_keepalive_interval": int,
|
||||||
|
+ "ssh_keepalive_count_max": int,
|
||||||
|
"ssh_log_file": str,
|
||||||
|
"ssh_config_file": str,
|
||||||
|
"ssh_merge_pillar": bool,
|
||||||
|
@@ -1592,6 +1595,9 @@ DEFAULT_MASTER_OPTS = immutabletypes.freeze(
|
||||||
|
"ssh_scan_ports": "22",
|
||||||
|
"ssh_scan_timeout": 0.01,
|
||||||
|
"ssh_identities_only": False,
|
||||||
|
+ "ssh_keepalive": True,
|
||||||
|
+ "ssh_keepalive_interval": 60,
|
||||||
|
+ "ssh_keepalive_count_max": 3,
|
||||||
|
"ssh_log_file": os.path.join(salt.syspaths.LOGS_DIR, "ssh"),
|
||||||
|
"ssh_config_file": os.path.join(salt.syspaths.HOME_DIR, ".ssh", "config"),
|
||||||
|
"cluster_mode": False,
|
||||||
|
diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py
|
||||||
|
index dc125de7d7..6c7f9f2f66 100644
|
||||||
|
--- a/salt/utils/parsers.py
|
||||||
|
+++ b/salt/utils/parsers.py
|
||||||
|
@@ -3383,6 +3383,25 @@ class SaltSSHOptionParser(
|
||||||
|
"-R parameters."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+ ssh_group.add_option(
|
||||||
|
+ "--disable-keepalive",
|
||||||
|
+ default=True,
|
||||||
|
+ action="store_false",
|
||||||
|
+ dest="ssh_keepalive",
|
||||||
|
+ help=(
|
||||||
|
+ "Disable KeepAlive probes (ServerAliveInterval) for the SSH connection."
|
||||||
|
+ ),
|
||||||
|
+ )
|
||||||
|
+ ssh_group.add_option(
|
||||||
|
+ "--keepalive-interval",
|
||||||
|
+ dest="ssh_keepalive_interval",
|
||||||
|
+ help=("Define the value for ServerAliveInterval option."),
|
||||||
|
+ )
|
||||||
|
+ ssh_group.add_option(
|
||||||
|
+ "--keepalive-count-max",
|
||||||
|
+ dest="ssh_keepalive_count_max",
|
||||||
|
+ help=("Define the value for ServerAliveCountMax option."),
|
||||||
|
+ )
|
||||||
|
ssh_group.add_option(
|
||||||
|
"--ssh-option",
|
||||||
|
dest="ssh_options",
|
||||||
|
diff --git a/tests/pytests/unit/client/ssh/test_single.py b/tests/pytests/unit/client/ssh/test_single.py
|
||||||
|
index c88a1c2127..8d87da8700 100644
|
||||||
|
--- a/tests/pytests/unit/client/ssh/test_single.py
|
||||||
|
+++ b/tests/pytests/unit/client/ssh/test_single.py
|
||||||
|
@@ -63,6 +63,61 @@ def test_single_opts(opts, target):
|
||||||
|
**target,
|
||||||
|
)
|
||||||
|
|
||||||
|
+ assert single.shell._ssh_opts() == ""
|
||||||
|
+ expected_cmd = (
|
||||||
|
+ "ssh login1 "
|
||||||
|
+ "-o KbdInteractiveAuthentication=no -o "
|
||||||
|
+ "PasswordAuthentication=yes -o ConnectTimeout=65 -o ServerAliveInterval=60 "
|
||||||
|
+ "-o ServerAliveCountMax=3 -o Port=22 "
|
||||||
|
+ "-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa "
|
||||||
|
+ "-o User=root date +%s"
|
||||||
|
+ )
|
||||||
|
+ assert single.shell._cmd_str("date +%s") == expected_cmd
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_single_opts_custom_keepalive_options(opts, target):
|
||||||
|
+ """Sanity check for ssh.Single options with custom keepalive"""
|
||||||
|
+
|
||||||
|
+ single = ssh.Single(
|
||||||
|
+ opts,
|
||||||
|
+ opts["argv"],
|
||||||
|
+ "localhost",
|
||||||
|
+ mods={},
|
||||||
|
+ fsclient=None,
|
||||||
|
+ thin=salt.utils.thin.thin_path(opts["cachedir"]),
|
||||||
|
+ mine=False,
|
||||||
|
+ keepalive_interval=15,
|
||||||
|
+ keepalive_count_max=5,
|
||||||
|
+ **target,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ assert single.shell._ssh_opts() == ""
|
||||||
|
+ expected_cmd = (
|
||||||
|
+ "ssh login1 "
|
||||||
|
+ "-o KbdInteractiveAuthentication=no -o "
|
||||||
|
+ "PasswordAuthentication=yes -o ConnectTimeout=65 -o ServerAliveInterval=15 "
|
||||||
|
+ "-o ServerAliveCountMax=5 -o Port=22 "
|
||||||
|
+ "-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa "
|
||||||
|
+ "-o User=root date +%s"
|
||||||
|
+ )
|
||||||
|
+ assert single.shell._cmd_str("date +%s") == expected_cmd
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_single_opts_disable_keepalive(opts, target):
|
||||||
|
+ """Sanity check for ssh.Single options with custom keepalive"""
|
||||||
|
+
|
||||||
|
+ single = ssh.Single(
|
||||||
|
+ opts,
|
||||||
|
+ opts["argv"],
|
||||||
|
+ "localhost",
|
||||||
|
+ mods={},
|
||||||
|
+ fsclient=None,
|
||||||
|
+ thin=salt.utils.thin.thin_path(opts["cachedir"]),
|
||||||
|
+ mine=False,
|
||||||
|
+ keepalive=False,
|
||||||
|
+ **target,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
assert single.shell._ssh_opts() == ""
|
||||||
|
expected_cmd = (
|
||||||
|
"ssh login1 "
|
||||||
|
diff --git a/tests/pytests/unit/client/ssh/test_ssh.py b/tests/pytests/unit/client/ssh/test_ssh.py
|
||||||
|
index cece16026c..23223ba8ec 100644
|
||||||
|
--- a/tests/pytests/unit/client/ssh/test_ssh.py
|
||||||
|
+++ b/tests/pytests/unit/client/ssh/test_ssh.py
|
||||||
|
@@ -78,6 +78,9 @@ def roster():
|
||||||
|
("ssh_scan_ports", "test", True),
|
||||||
|
("ssh_scan_timeout", 1.0, True),
|
||||||
|
("ssh_timeout", 1, False),
|
||||||
|
+ ("ssh_keepalive", True, True),
|
||||||
|
+ ("ssh_keepalive_interval", 30, True),
|
||||||
|
+ ("ssh_keepalive_count_max", 3, True),
|
||||||
|
("ssh_log_file", "/tmp/test", True),
|
||||||
|
("raw_shell", True, True),
|
||||||
|
("refresh_cache", True, True),
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
||||||
|
|
69
fix-calculation-of-sls-context-vars-when-trailing-do.patch
Normal file
69
fix-calculation-of-sls-context-vars-when-trailing-do.patch
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
From 3403a7391df785be31b6fbe401a8229c2007ac19 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Mon, 2 Oct 2023 10:44:05 +0100
|
||||||
|
Subject: [PATCH] Fix calculation of SLS context vars when trailing dots
|
||||||
|
on targetted sls/state (bsc#1213518) (#598)
|
||||||
|
|
||||||
|
* Fix calculation of SLS context vars when trailing dots on targetted state
|
||||||
|
|
||||||
|
* Add changelog file
|
||||||
|
---
|
||||||
|
changelog/63411.fixed.md | 1 +
|
||||||
|
salt/utils/templates.py | 5 +++--
|
||||||
|
tests/unit/utils/test_templates.py | 14 ++++++++++++++
|
||||||
|
3 files changed, 18 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 changelog/63411.fixed.md
|
||||||
|
|
||||||
|
diff --git a/changelog/63411.fixed.md b/changelog/63411.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..65340e3652
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/63411.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Fix calculation of SLS context vars when trailing dots on targetted state
|
||||||
|
diff --git a/salt/utils/templates.py b/salt/utils/templates.py
|
||||||
|
index 4a8adf2a14..8639ea703e 100644
|
||||||
|
--- a/salt/utils/templates.py
|
||||||
|
+++ b/salt/utils/templates.py
|
||||||
|
@@ -113,8 +113,9 @@ def generate_sls_context(tmplpath, sls):
|
||||||
|
|
||||||
|
sls_context = {}
|
||||||
|
|
||||||
|
- # Normalize SLS as path.
|
||||||
|
- slspath = sls.replace(".", "/")
|
||||||
|
+ # Normalize SLS as path and remove possible trailing slashes
|
||||||
|
+ # to prevent matching issues and wrong vars calculation
|
||||||
|
+ slspath = sls.replace(".", "/").rstrip("/")
|
||||||
|
|
||||||
|
if tmplpath:
|
||||||
|
# Normalize template path
|
||||||
|
diff --git a/tests/unit/utils/test_templates.py b/tests/unit/utils/test_templates.py
|
||||||
|
index 4ba2f52d7b..264b4ae801 100644
|
||||||
|
--- a/tests/unit/utils/test_templates.py
|
||||||
|
+++ b/tests/unit/utils/test_templates.py
|
||||||
|
@@ -320,6 +320,20 @@ class WrapRenderTestCase(TestCase):
|
||||||
|
slspath="foo",
|
||||||
|
)
|
||||||
|
|
||||||
|
+ def test_generate_sls_context__one_level_init_implicit_with_trailing_dot(self):
|
||||||
|
+ """generate_sls_context - Basic one level with implicit init.sls with trailing dot"""
|
||||||
|
+ self._test_generated_sls_context(
|
||||||
|
+ "/tmp/foo/init.sls",
|
||||||
|
+ "foo.",
|
||||||
|
+ tplfile="foo/init.sls",
|
||||||
|
+ tpldir="foo",
|
||||||
|
+ tpldot="foo",
|
||||||
|
+ slsdotpath="foo",
|
||||||
|
+ slscolonpath="foo",
|
||||||
|
+ sls_path="foo",
|
||||||
|
+ slspath="foo",
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
def test_generate_sls_context__one_level_init_explicit(self):
|
||||||
|
"""generate_sls_context - Basic one level with explicit init.sls"""
|
||||||
|
self._test_generated_sls_context(
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
||||||
|
|
1163
fix-cve-2023-34049-bsc-1215157.patch
Normal file
1163
fix-cve-2023-34049-bsc-1215157.patch
Normal file
File diff suppressed because it is too large
Load Diff
544
fix-cve-2024-22231-and-cve-2024-22232-bsc-1219430-bs.patch
Normal file
544
fix-cve-2024-22231-and-cve-2024-22232-bsc-1219430-bs.patch
Normal file
@ -0,0 +1,544 @@
|
|||||||
|
From 5710bc3ff3887762182f8326bd74f40d3872a69f Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Thu, 1 Feb 2024 11:50:16 +0000
|
||||||
|
Subject: [PATCH] Fix "CVE-2024-22231" and "CVE-2024-22232"
|
||||||
|
(bsc#1219430, bsc#1219431) (#621)
|
||||||
|
|
||||||
|
* Fix CVE-2024-22231 and CVE-2024-22232
|
||||||
|
|
||||||
|
* Add changelogs for CVE-2024-22231 and CVE-2024-22232
|
||||||
|
|
||||||
|
* Fix linter issue
|
||||||
|
|
||||||
|
* Add credit
|
||||||
|
|
||||||
|
* Fix wart in patch
|
||||||
|
|
||||||
|
* Clean up test fixtures
|
||||||
|
|
||||||
|
* Fix test on windows
|
||||||
|
|
||||||
|
* Update changelog file name
|
||||||
|
|
||||||
|
* Fix fileroots tests
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
Co-authored-by: Daniel A. Wozniak <dwozniak@vmware.com>
|
||||||
|
---
|
||||||
|
changelog/565.security.md | 4 +
|
||||||
|
salt/fileserver/__init__.py | 9 +-
|
||||||
|
salt/fileserver/roots.py | 26 +++++
|
||||||
|
salt/master.py | 15 ++-
|
||||||
|
tests/pytests/unit/fileserver/test_roots.py | 58 +++++++--
|
||||||
|
tests/pytests/unit/test_fileserver.py | 123 ++++++++++++++++++++
|
||||||
|
tests/pytests/unit/test_master.py | 33 ++++++
|
||||||
|
tests/unit/test_fileserver.py | 79 -------------
|
||||||
|
8 files changed, 250 insertions(+), 97 deletions(-)
|
||||||
|
create mode 100644 changelog/565.security.md
|
||||||
|
create mode 100644 tests/pytests/unit/test_fileserver.py
|
||||||
|
delete mode 100644 tests/unit/test_fileserver.py
|
||||||
|
|
||||||
|
diff --git a/changelog/565.security.md b/changelog/565.security.md
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000..5d7ec8202ba
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/565.security.md
|
||||||
|
@@ -0,0 +1,4 @@
|
||||||
|
+CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master
|
||||||
|
+CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method.
|
||||||
|
+These vulerablities were discovered and reported by:
|
||||||
|
+Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab)
|
||||||
|
diff --git a/salt/fileserver/__init__.py b/salt/fileserver/__init__.py
|
||||||
|
index 99f12387f91..4eca98d14a4 100644
|
||||||
|
--- a/salt/fileserver/__init__.py
|
||||||
|
+++ b/salt/fileserver/__init__.py
|
||||||
|
@@ -568,11 +568,6 @@ class Fileserver:
|
||||||
|
saltenv = salt.utils.stringutils.to_unicode(saltenv)
|
||||||
|
back = self.backends(back)
|
||||||
|
kwargs = {}
|
||||||
|
- fnd = {"path": "", "rel": ""}
|
||||||
|
- if os.path.isabs(path):
|
||||||
|
- return fnd
|
||||||
|
- if "../" in path:
|
||||||
|
- return fnd
|
||||||
|
if salt.utils.url.is_escaped(path):
|
||||||
|
# don't attempt to find URL query arguments in the path
|
||||||
|
path = salt.utils.url.unescape(path)
|
||||||
|
@@ -588,6 +583,10 @@ class Fileserver:
|
||||||
|
args = comp.split("=", 1)
|
||||||
|
kwargs[args[0]] = args[1]
|
||||||
|
|
||||||
|
+ fnd = {"path": "", "rel": ""}
|
||||||
|
+ if os.path.isabs(path) or "../" in path:
|
||||||
|
+ return fnd
|
||||||
|
+
|
||||||
|
if "env" in kwargs:
|
||||||
|
# "env" is not supported; Use "saltenv".
|
||||||
|
kwargs.pop("env")
|
||||||
|
diff --git a/salt/fileserver/roots.py b/salt/fileserver/roots.py
|
||||||
|
index a02b597c6f8..e2ea92029c3 100644
|
||||||
|
--- a/salt/fileserver/roots.py
|
||||||
|
+++ b/salt/fileserver/roots.py
|
||||||
|
@@ -27,6 +27,7 @@ import salt.utils.hashutils
|
||||||
|
import salt.utils.path
|
||||||
|
import salt.utils.platform
|
||||||
|
import salt.utils.stringutils
|
||||||
|
+import salt.utils.verify
|
||||||
|
import salt.utils.versions
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
@@ -98,6 +99,11 @@ def find_file(path, saltenv="base", **kwargs):
|
||||||
|
if saltenv == "__env__":
|
||||||
|
root = root.replace("__env__", actual_saltenv)
|
||||||
|
full = os.path.join(root, path)
|
||||||
|
+
|
||||||
|
+ # Refuse to serve file that is not under the root.
|
||||||
|
+ if not salt.utils.verify.clean_path(root, full, subdir=True):
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
if os.path.isfile(full) and not salt.fileserver.is_file_ignored(__opts__, full):
|
||||||
|
fnd["path"] = full
|
||||||
|
fnd["rel"] = path
|
||||||
|
@@ -128,6 +134,26 @@ def serve_file(load, fnd):
|
||||||
|
ret["dest"] = fnd["rel"]
|
||||||
|
gzip = load.get("gzip", None)
|
||||||
|
fpath = os.path.normpath(fnd["path"])
|
||||||
|
+
|
||||||
|
+ actual_saltenv = saltenv = load["saltenv"]
|
||||||
|
+ if saltenv not in __opts__["file_roots"]:
|
||||||
|
+ if "__env__" in __opts__["file_roots"]:
|
||||||
|
+ log.debug(
|
||||||
|
+ "salt environment '%s' maps to __env__ file_roots directory", saltenv
|
||||||
|
+ )
|
||||||
|
+ saltenv = "__env__"
|
||||||
|
+ else:
|
||||||
|
+ return fnd
|
||||||
|
+ file_in_root = False
|
||||||
|
+ for root in __opts__["file_roots"][saltenv]:
|
||||||
|
+ if saltenv == "__env__":
|
||||||
|
+ root = root.replace("__env__", actual_saltenv)
|
||||||
|
+ # Refuse to serve file that is not under the root.
|
||||||
|
+ if salt.utils.verify.clean_path(root, fpath, subdir=True):
|
||||||
|
+ file_in_root = True
|
||||||
|
+ if not file_in_root:
|
||||||
|
+ return ret
|
||||||
|
+
|
||||||
|
with salt.utils.files.fopen(fpath, "rb") as fp_:
|
||||||
|
fp_.seek(load["loc"])
|
||||||
|
data = fp_.read(__opts__["file_buffer_size"])
|
||||||
|
diff --git a/salt/master.py b/salt/master.py
|
||||||
|
index 3d2ba1e29de..425b4121481 100644
|
||||||
|
--- a/salt/master.py
|
||||||
|
+++ b/salt/master.py
|
||||||
|
@@ -1038,7 +1038,10 @@ class MWorker(salt.utils.process.SignalHandlingProcess):
|
||||||
|
"""
|
||||||
|
key = payload["enc"]
|
||||||
|
load = payload["load"]
|
||||||
|
- ret = {"aes": self._handle_aes, "clear": self._handle_clear}[key](load)
|
||||||
|
+ if key == "aes":
|
||||||
|
+ ret = self._handle_aes(load)
|
||||||
|
+ else:
|
||||||
|
+ ret = self._handle_clear(load)
|
||||||
|
raise salt.ext.tornado.gen.Return(ret)
|
||||||
|
|
||||||
|
def _post_stats(self, start, cmd):
|
||||||
|
@@ -1213,7 +1216,7 @@ class AESFuncs(TransportMethods):
|
||||||
|
"_dir_list",
|
||||||
|
"_symlink_list",
|
||||||
|
"_file_envs",
|
||||||
|
- "_ext_nodes", # To keep compatibility with old Salt minion versions
|
||||||
|
+ "_ext_nodes", # To keep compatibility with old Salt minion versions
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, opts, context=None):
|
||||||
|
@@ -1746,10 +1749,16 @@ class AESFuncs(TransportMethods):
|
||||||
|
self.mminion.returners[fstr](load["jid"], load["load"])
|
||||||
|
|
||||||
|
# Register the syndic
|
||||||
|
+
|
||||||
|
+ # We are creating a path using user suplied input. Use the
|
||||||
|
+ # clean_path to prevent a directory traversal.
|
||||||
|
+ root = os.path.join(self.opts["cachedir"], "syndics")
|
||||||
|
syndic_cache_path = os.path.join(
|
||||||
|
self.opts["cachedir"], "syndics", load["id"]
|
||||||
|
)
|
||||||
|
- if not os.path.exists(syndic_cache_path):
|
||||||
|
+ if salt.utils.verify.clean_path(
|
||||||
|
+ root, syndic_cache_path
|
||||||
|
+ ) and not os.path.exists(syndic_cache_path):
|
||||||
|
path_name = os.path.split(syndic_cache_path)[0]
|
||||||
|
if not os.path.exists(path_name):
|
||||||
|
os.makedirs(path_name)
|
||||||
|
diff --git a/tests/pytests/unit/fileserver/test_roots.py b/tests/pytests/unit/fileserver/test_roots.py
|
||||||
|
index 96bceb0fd3d..c1660280bc5 100644
|
||||||
|
--- a/tests/pytests/unit/fileserver/test_roots.py
|
||||||
|
+++ b/tests/pytests/unit/fileserver/test_roots.py
|
||||||
|
@@ -5,6 +5,7 @@
|
||||||
|
import copy
|
||||||
|
import pathlib
|
||||||
|
import shutil
|
||||||
|
+import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
@@ -28,14 +29,14 @@ def unicode_dirname():
|
||||||
|
return "соль"
|
||||||
|
|
||||||
|
|
||||||
|
-@pytest.fixture(autouse=True)
|
||||||
|
+@pytest.fixture
|
||||||
|
def testfile(tmp_path):
|
||||||
|
fp = tmp_path / "testfile"
|
||||||
|
fp.write_text("This is a testfile")
|
||||||
|
return fp
|
||||||
|
|
||||||
|
|
||||||
|
-@pytest.fixture(autouse=True)
|
||||||
|
+@pytest.fixture
|
||||||
|
def tmp_state_tree(tmp_path, testfile, unicode_filename, unicode_dirname):
|
||||||
|
dirname = tmp_path / "roots_tmp_state_tree"
|
||||||
|
dirname.mkdir(parents=True, exist_ok=True)
|
||||||
|
@@ -54,11 +55,15 @@ def tmp_state_tree(tmp_path, testfile, unicode_filename, unicode_dirname):
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
-def configure_loader_modules(tmp_state_tree, temp_salt_master):
|
||||||
|
- opts = temp_salt_master.config.copy()
|
||||||
|
+def testfilepath(tmp_state_tree, testfile):
|
||||||
|
+ return tmp_state_tree / testfile.name
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@pytest.fixture
|
||||||
|
+def configure_loader_modules(tmp_state_tree, master_opts):
|
||||||
|
overrides = {"file_roots": {"base": [str(tmp_state_tree)]}}
|
||||||
|
- opts.update(overrides)
|
||||||
|
- return {roots: {"__opts__": opts}}
|
||||||
|
+ master_opts.update(overrides)
|
||||||
|
+ return {roots: {"__opts__": master_opts}}
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_list(unicode_filename):
|
||||||
|
@@ -75,17 +80,17 @@ def test_find_file(tmp_state_tree):
|
||||||
|
assert full_path_to_file == ret["path"]
|
||||||
|
|
||||||
|
|
||||||
|
-def test_serve_file(testfile):
|
||||||
|
+def test_serve_file(testfilepath):
|
||||||
|
with patch.dict(roots.__opts__, {"file_buffer_size": 262144}):
|
||||||
|
load = {
|
||||||
|
"saltenv": "base",
|
||||||
|
- "path": str(testfile),
|
||||||
|
+ "path": str(testfilepath),
|
||||||
|
"loc": 0,
|
||||||
|
}
|
||||||
|
- fnd = {"path": str(testfile), "rel": "testfile"}
|
||||||
|
+ fnd = {"path": str(testfilepath), "rel": "testfile"}
|
||||||
|
ret = roots.serve_file(load, fnd)
|
||||||
|
|
||||||
|
- with salt.utils.files.fopen(str(testfile), "rb") as fp_:
|
||||||
|
+ with salt.utils.files.fopen(str(testfilepath), "rb") as fp_:
|
||||||
|
data = fp_.read()
|
||||||
|
|
||||||
|
assert ret == {"data": data, "dest": "testfile"}
|
||||||
|
@@ -277,3 +282,36 @@ def test_update_mtime_map_unicode_error(tmp_path):
|
||||||
|
},
|
||||||
|
"backend": "roots",
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_find_file_not_in_root(tmp_state_tree):
|
||||||
|
+ """
|
||||||
|
+ Fileroots should never 'find' a file that is outside of it's root.
|
||||||
|
+ """
|
||||||
|
+ badfile = pathlib.Path(tmp_state_tree).parent / "bar"
|
||||||
|
+ badfile.write_text("Bad file")
|
||||||
|
+ badpath = f"../bar"
|
||||||
|
+ ret = roots.find_file(badpath)
|
||||||
|
+ assert ret == {"path": "", "rel": ""}
|
||||||
|
+ badpath = f"{tmp_state_tree / '..' / 'bar'}"
|
||||||
|
+ ret = roots.find_file(badpath)
|
||||||
|
+ assert ret == {"path": "", "rel": ""}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_serve_file_not_in_root(tmp_state_tree):
|
||||||
|
+ """
|
||||||
|
+ Fileroots should never 'serve' a file that is outside of it's root.
|
||||||
|
+ """
|
||||||
|
+ badfile = pathlib.Path(tmp_state_tree).parent / "bar"
|
||||||
|
+ badfile.write_text("Bad file")
|
||||||
|
+ badpath = f"../bar"
|
||||||
|
+ load = {"path": "salt://|..\\bar", "saltenv": "base", "loc": 0}
|
||||||
|
+ fnd = {
|
||||||
|
+ "path": f"{tmp_state_tree / '..' / 'bar'}",
|
||||||
|
+ "rel": f"{pathlib.Path('..') / 'bar'}",
|
||||||
|
+ }
|
||||||
|
+ ret = roots.serve_file(load, fnd)
|
||||||
|
+ if "win" in sys.platform:
|
||||||
|
+ assert ret == {"data": "", "dest": "..\\bar"}
|
||||||
|
+ else:
|
||||||
|
+ assert ret == {"data": "", "dest": "../bar"}
|
||||||
|
diff --git a/tests/pytests/unit/test_fileserver.py b/tests/pytests/unit/test_fileserver.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000..8dd3ea0a27d
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/pytests/unit/test_fileserver.py
|
||||||
|
@@ -0,0 +1,123 @@
|
||||||
|
+import datetime
|
||||||
|
+import os
|
||||||
|
+import time
|
||||||
|
+
|
||||||
|
+import salt.fileserver
|
||||||
|
+import salt.utils.files
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_diff_with_diffent_keys():
|
||||||
|
+ """
|
||||||
|
+ Test that different maps are indeed reported different
|
||||||
|
+ """
|
||||||
|
+ map1 = {"file1": 1234}
|
||||||
|
+ map2 = {"file2": 1234}
|
||||||
|
+ assert salt.fileserver.diff_mtime_map(map1, map2) is True
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_diff_with_diffent_values():
|
||||||
|
+ """
|
||||||
|
+ Test that different maps are indeed reported different
|
||||||
|
+ """
|
||||||
|
+ map1 = {"file1": 12345}
|
||||||
|
+ map2 = {"file1": 1234}
|
||||||
|
+ assert salt.fileserver.diff_mtime_map(map1, map2) is True
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_whitelist():
|
||||||
|
+ opts = {
|
||||||
|
+ "fileserver_backend": ["roots", "git", "s3fs", "hgfs", "svn"],
|
||||||
|
+ "extension_modules": "",
|
||||||
|
+ }
|
||||||
|
+ fs = salt.fileserver.Fileserver(opts)
|
||||||
|
+ assert sorted(fs.servers.whitelist) == sorted(
|
||||||
|
+ ["git", "gitfs", "hg", "hgfs", "svn", "svnfs", "roots", "s3fs"]
|
||||||
|
+ ), fs.servers.whitelist
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_future_file_list_cache_file_ignored(tmp_path):
|
||||||
|
+ opts = {
|
||||||
|
+ "fileserver_backend": ["roots"],
|
||||||
|
+ "cachedir": tmp_path,
|
||||||
|
+ "extension_modules": "",
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ back_cachedir = os.path.join(tmp_path, "file_lists/roots")
|
||||||
|
+ os.makedirs(os.path.join(back_cachedir))
|
||||||
|
+
|
||||||
|
+ # Touch a couple files
|
||||||
|
+ for filename in ("base.p", "foo.txt"):
|
||||||
|
+ with salt.utils.files.fopen(os.path.join(back_cachedir, filename), "wb") as _f:
|
||||||
|
+ if filename == "base.p":
|
||||||
|
+ _f.write(b"\x80")
|
||||||
|
+
|
||||||
|
+ # Set modification time to file list cache file to 1 year in the future
|
||||||
|
+ now = datetime.datetime.utcnow()
|
||||||
|
+ future = now + datetime.timedelta(days=365)
|
||||||
|
+ mod_time = time.mktime(future.timetuple())
|
||||||
|
+ os.utime(os.path.join(back_cachedir, "base.p"), (mod_time, mod_time))
|
||||||
|
+
|
||||||
|
+ list_cache = os.path.join(back_cachedir, "base.p")
|
||||||
|
+ w_lock = os.path.join(back_cachedir, ".base.w")
|
||||||
|
+ ret = salt.fileserver.check_file_list_cache(opts, "files", list_cache, w_lock)
|
||||||
|
+ assert (
|
||||||
|
+ ret[1] is True
|
||||||
|
+ ), "Cache file list cache file is not refreshed when future modification time"
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_file_server_url_escape(tmp_path):
|
||||||
|
+ (tmp_path / "srv").mkdir()
|
||||||
|
+ (tmp_path / "srv" / "salt").mkdir()
|
||||||
|
+ (tmp_path / "foo").mkdir()
|
||||||
|
+ (tmp_path / "foo" / "bar").write_text("Bad file")
|
||||||
|
+ fileroot = str(tmp_path / "srv" / "salt")
|
||||||
|
+ badfile = str(tmp_path / "foo" / "bar")
|
||||||
|
+ opts = {
|
||||||
|
+ "fileserver_backend": ["roots"],
|
||||||
|
+ "extension_modules": "",
|
||||||
|
+ "optimization_order": [
|
||||||
|
+ 0,
|
||||||
|
+ ],
|
||||||
|
+ "file_roots": {
|
||||||
|
+ "base": [fileroot],
|
||||||
|
+ },
|
||||||
|
+ "file_ignore_regex": "",
|
||||||
|
+ "file_ignore_glob": "",
|
||||||
|
+ }
|
||||||
|
+ fs = salt.fileserver.Fileserver(opts)
|
||||||
|
+ ret = fs.find_file(
|
||||||
|
+ "salt://|..\\..\\..\\foo/bar",
|
||||||
|
+ "base",
|
||||||
|
+ )
|
||||||
|
+ assert ret == {"path": "", "rel": ""}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_file_server_serve_url_escape(tmp_path):
|
||||||
|
+ (tmp_path / "srv").mkdir()
|
||||||
|
+ (tmp_path / "srv" / "salt").mkdir()
|
||||||
|
+ (tmp_path / "foo").mkdir()
|
||||||
|
+ (tmp_path / "foo" / "bar").write_text("Bad file")
|
||||||
|
+ fileroot = str(tmp_path / "srv" / "salt")
|
||||||
|
+ badfile = str(tmp_path / "foo" / "bar")
|
||||||
|
+ opts = {
|
||||||
|
+ "fileserver_backend": ["roots"],
|
||||||
|
+ "extension_modules": "",
|
||||||
|
+ "optimization_order": [
|
||||||
|
+ 0,
|
||||||
|
+ ],
|
||||||
|
+ "file_roots": {
|
||||||
|
+ "base": [fileroot],
|
||||||
|
+ },
|
||||||
|
+ "file_ignore_regex": "",
|
||||||
|
+ "file_ignore_glob": "",
|
||||||
|
+ "file_buffer_size": 2048,
|
||||||
|
+ }
|
||||||
|
+ fs = salt.fileserver.Fileserver(opts)
|
||||||
|
+ ret = fs.serve_file(
|
||||||
|
+ {
|
||||||
|
+ "path": "salt://|..\\..\\..\\foo/bar",
|
||||||
|
+ "saltenv": "base",
|
||||||
|
+ "loc": 0,
|
||||||
|
+ }
|
||||||
|
+ )
|
||||||
|
+ assert ret == {"data": "", "dest": ""}
|
||||||
|
diff --git a/tests/pytests/unit/test_master.py b/tests/pytests/unit/test_master.py
|
||||||
|
index 98c796912aa..d338307d1f8 100644
|
||||||
|
--- a/tests/pytests/unit/test_master.py
|
||||||
|
+++ b/tests/pytests/unit/test_master.py
|
||||||
|
@@ -1,3 +1,4 @@
|
||||||
|
+import pathlib
|
||||||
|
import time
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
@@ -249,3 +250,35 @@ def test_mworker_pass_context():
|
||||||
|
loadler_pillars_mock.call_args_list[0][1].get("pack").get("__context__")
|
||||||
|
== test_context
|
||||||
|
)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_syndic_return_cache_dir_creation(encrypted_requests):
|
||||||
|
+ """master's cachedir for a syndic will be created by AESFuncs._syndic_return method"""
|
||||||
|
+ cachedir = pathlib.Path(encrypted_requests.opts["cachedir"])
|
||||||
|
+ assert not (cachedir / "syndics").exists()
|
||||||
|
+ encrypted_requests._syndic_return(
|
||||||
|
+ {
|
||||||
|
+ "id": "mamajama",
|
||||||
|
+ "jid": "",
|
||||||
|
+ "return": {},
|
||||||
|
+ }
|
||||||
|
+ )
|
||||||
|
+ assert (cachedir / "syndics").exists()
|
||||||
|
+ assert (cachedir / "syndics" / "mamajama").exists()
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_syndic_return_cache_dir_creation_traversal(encrypted_requests):
|
||||||
|
+ """
|
||||||
|
+ master's AESFuncs._syndic_return method cachdir creation is not vulnerable to a directory traversal
|
||||||
|
+ """
|
||||||
|
+ cachedir = pathlib.Path(encrypted_requests.opts["cachedir"])
|
||||||
|
+ assert not (cachedir / "syndics").exists()
|
||||||
|
+ encrypted_requests._syndic_return(
|
||||||
|
+ {
|
||||||
|
+ "id": "../mamajama",
|
||||||
|
+ "jid": "",
|
||||||
|
+ "return": {},
|
||||||
|
+ }
|
||||||
|
+ )
|
||||||
|
+ assert not (cachedir / "syndics").exists()
|
||||||
|
+ assert not (cachedir / "mamajama").exists()
|
||||||
|
diff --git a/tests/unit/test_fileserver.py b/tests/unit/test_fileserver.py
|
||||||
|
deleted file mode 100644
|
||||||
|
index c290b16b7e4..00000000000
|
||||||
|
--- a/tests/unit/test_fileserver.py
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,79 +0,0 @@
|
||||||
|
-"""
|
||||||
|
- :codeauthor: Joao Mesquita <jmesquita@sangoma.com>
|
||||||
|
-"""
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-import datetime
|
||||||
|
-import os
|
||||||
|
-import time
|
||||||
|
-
|
||||||
|
-import salt.utils.files
|
||||||
|
-from salt import fileserver
|
||||||
|
-from tests.support.helpers import with_tempdir
|
||||||
|
-from tests.support.mixins import LoaderModuleMockMixin
|
||||||
|
-from tests.support.unit import TestCase
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-class MapDiffTestCase(TestCase):
|
||||||
|
- def test_diff_with_diffent_keys(self):
|
||||||
|
- """
|
||||||
|
- Test that different maps are indeed reported different
|
||||||
|
- """
|
||||||
|
- map1 = {"file1": 1234}
|
||||||
|
- map2 = {"file2": 1234}
|
||||||
|
- assert fileserver.diff_mtime_map(map1, map2) is True
|
||||||
|
-
|
||||||
|
- def test_diff_with_diffent_values(self):
|
||||||
|
- """
|
||||||
|
- Test that different maps are indeed reported different
|
||||||
|
- """
|
||||||
|
- map1 = {"file1": 12345}
|
||||||
|
- map2 = {"file1": 1234}
|
||||||
|
- assert fileserver.diff_mtime_map(map1, map2) is True
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-class VCSBackendWhitelistCase(TestCase, LoaderModuleMockMixin):
|
||||||
|
- def setup_loader_modules(self):
|
||||||
|
- return {fileserver: {}}
|
||||||
|
-
|
||||||
|
- def test_whitelist(self):
|
||||||
|
- opts = {
|
||||||
|
- "fileserver_backend": ["roots", "git", "s3fs", "hgfs", "svn"],
|
||||||
|
- "extension_modules": "",
|
||||||
|
- }
|
||||||
|
- fs = fileserver.Fileserver(opts)
|
||||||
|
- assert sorted(fs.servers.whitelist) == sorted(
|
||||||
|
- ["git", "gitfs", "hg", "hgfs", "svn", "svnfs", "roots", "s3fs"]
|
||||||
|
- ), fs.servers.whitelist
|
||||||
|
-
|
||||||
|
- @with_tempdir()
|
||||||
|
- def test_future_file_list_cache_file_ignored(self, cachedir):
|
||||||
|
- opts = {
|
||||||
|
- "fileserver_backend": ["roots"],
|
||||||
|
- "cachedir": cachedir,
|
||||||
|
- "extension_modules": "",
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- back_cachedir = os.path.join(cachedir, "file_lists/roots")
|
||||||
|
- os.makedirs(os.path.join(back_cachedir))
|
||||||
|
-
|
||||||
|
- # Touch a couple files
|
||||||
|
- for filename in ("base.p", "foo.txt"):
|
||||||
|
- with salt.utils.files.fopen(
|
||||||
|
- os.path.join(back_cachedir, filename), "wb"
|
||||||
|
- ) as _f:
|
||||||
|
- if filename == "base.p":
|
||||||
|
- _f.write(b"\x80")
|
||||||
|
-
|
||||||
|
- # Set modification time to file list cache file to 1 year in the future
|
||||||
|
- now = datetime.datetime.utcnow()
|
||||||
|
- future = now + datetime.timedelta(days=365)
|
||||||
|
- mod_time = time.mktime(future.timetuple())
|
||||||
|
- os.utime(os.path.join(back_cachedir, "base.p"), (mod_time, mod_time))
|
||||||
|
-
|
||||||
|
- list_cache = os.path.join(back_cachedir, "base.p")
|
||||||
|
- w_lock = os.path.join(back_cachedir, ".base.w")
|
||||||
|
- ret = fileserver.check_file_list_cache(opts, "files", list_cache, w_lock)
|
||||||
|
- assert (
|
||||||
|
- ret[1] is True
|
||||||
|
- ), "Cache file list cache file is not refreshed when future modification time"
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
||||||
|
|
2024
fix-gitfs-__env__-and-improve-cache-cleaning-bsc-119.patch
Normal file
2024
fix-gitfs-__env__-and-improve-cache-cleaning-bsc-119.patch
Normal file
File diff suppressed because it is too large
Load Diff
62
fix-optimization_order-opt-to-prevent-test-fails.patch
Normal file
62
fix-optimization_order-opt-to-prevent-test-fails.patch
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
From aaf593d17f51a517e0adb6e9ec1c0d768ab5f855 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Mon, 2 Oct 2023 14:24:27 +0200
|
||||||
|
Subject: [PATCH] Fix optimization_order opt to prevent test fails
|
||||||
|
|
||||||
|
---
|
||||||
|
tests/pytests/unit/grains/test_core.py | 4 ++--
|
||||||
|
tests/pytests/unit/loader/test_loader.py | 2 +-
|
||||||
|
tests/pytests/unit/test_config.py | 2 +-
|
||||||
|
3 files changed, 4 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py
|
||||||
|
index 993c723950..36545287b9 100644
|
||||||
|
--- a/tests/pytests/unit/grains/test_core.py
|
||||||
|
+++ b/tests/pytests/unit/grains/test_core.py
|
||||||
|
@@ -156,7 +156,7 @@ def test_network_grains_secondary_ip(tmp_path):
|
||||||
|
opts = {
|
||||||
|
"cachedir": str(cache_dir),
|
||||||
|
"extension_modules": str(extmods),
|
||||||
|
- "optimization_order": [0],
|
||||||
|
+ "optimization_order": [0, 1, 2],
|
||||||
|
}
|
||||||
|
with patch("salt.utils.network.interfaces", side_effect=[data]):
|
||||||
|
grains = salt.loader.grain_funcs(opts)
|
||||||
|
@@ -243,7 +243,7 @@ def test_network_grains_cache(tmp_path):
|
||||||
|
opts = {
|
||||||
|
"cachedir": str(cache_dir),
|
||||||
|
"extension_modules": str(extmods),
|
||||||
|
- "optimization_order": [0],
|
||||||
|
+ "optimization_order": [0, 1, 2],
|
||||||
|
}
|
||||||
|
with patch(
|
||||||
|
"salt.utils.network.interfaces", side_effect=[call_1, call_2]
|
||||||
|
diff --git a/tests/pytests/unit/loader/test_loader.py b/tests/pytests/unit/loader/test_loader.py
|
||||||
|
index f4a4b51a58..86348749db 100644
|
||||||
|
--- a/tests/pytests/unit/loader/test_loader.py
|
||||||
|
+++ b/tests/pytests/unit/loader/test_loader.py
|
||||||
|
@@ -57,7 +57,7 @@ def test_raw_mod_functions():
|
||||||
|
"Ensure functions loaded by raw_mod are LoaderFunc instances"
|
||||||
|
opts = {
|
||||||
|
"extension_modules": "",
|
||||||
|
- "optimization_order": [0],
|
||||||
|
+ "optimization_order": [0, 1, 2],
|
||||||
|
}
|
||||||
|
ret = salt.loader.raw_mod(opts, "grains", "get")
|
||||||
|
for k, v in ret.items():
|
||||||
|
diff --git a/tests/pytests/unit/test_config.py b/tests/pytests/unit/test_config.py
|
||||||
|
index cb343cb75e..76d5605360 100644
|
||||||
|
--- a/tests/pytests/unit/test_config.py
|
||||||
|
+++ b/tests/pytests/unit/test_config.py
|
||||||
|
@@ -16,7 +16,7 @@ def test_call_id_function(tmp_path):
|
||||||
|
"cachedir": str(cache_dir),
|
||||||
|
"extension_modules": str(extmods),
|
||||||
|
"grains": {"osfinger": "meh"},
|
||||||
|
- "optimization_order": [0],
|
||||||
|
+ "optimization_order": [0, 1, 2],
|
||||||
|
}
|
||||||
|
ret = salt.config.call_id_function(opts)
|
||||||
|
assert ret == "meh"
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
25
fix-the-aptpkg.py-unit-test-failure.patch
Normal file
25
fix-the-aptpkg.py-unit-test-failure.patch
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
From 4bc3be7814daf5365d63b88f164f791ea53b418f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marek Czernek <marek.czernek@suse.com>
|
||||||
|
Date: Wed, 17 Jan 2024 15:04:53 +0100
|
||||||
|
Subject: [PATCH] Fix the aptpkg.py unit test failure
|
||||||
|
|
||||||
|
---
|
||||||
|
salt/modules/aptpkg.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py
|
||||||
|
index 9885e9fb60..ad5450c415 100644
|
||||||
|
--- a/salt/modules/aptpkg.py
|
||||||
|
+++ b/salt/modules/aptpkg.py
|
||||||
|
@@ -3128,7 +3128,7 @@ def expand_repo_def(**kwargs):
|
||||||
|
NOT USABLE IN THE CLI
|
||||||
|
"""
|
||||||
|
warn_until_date(
|
||||||
|
- "20240101",
|
||||||
|
+ "20250101",
|
||||||
|
"The pkg.expand_repo_def function is deprecated and set for removal "
|
||||||
|
"after {date}. This is only unsed internally by the apt pkg state "
|
||||||
|
"module. If that's not the case, please file an new issue requesting "
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
121
fixed-keyerror-in-logs-when-running-a-state-that-fai.patch
Normal file
121
fixed-keyerror-in-logs-when-running-a-state-that-fai.patch
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
From f41a8e2a142a8487e13af481990928e0afb5f15e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Thu, 18 Jan 2024 17:02:03 +0100
|
||||||
|
Subject: [PATCH] Fixed KeyError in logs when running a state that
|
||||||
|
fails. (#615)
|
||||||
|
|
||||||
|
Co-authored-by: Megan Wilhite <mwilhite@vmware.com>
|
||||||
|
---
|
||||||
|
changelog/64231.fixed.md | 1 +
|
||||||
|
salt/master.py | 2 +-
|
||||||
|
salt/minion.py | 4 ++
|
||||||
|
salt/utils/event.py | 3 +-
|
||||||
|
.../integration/states/test_state_test.py | 38 +++++++++++++++++++
|
||||||
|
5 files changed, 46 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 changelog/64231.fixed.md
|
||||||
|
create mode 100644 tests/pytests/integration/states/test_state_test.py
|
||||||
|
|
||||||
|
diff --git a/changelog/64231.fixed.md b/changelog/64231.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..0991c5a8b9
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/64231.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Fixed KeyError in logs when running a state that fails.
|
||||||
|
diff --git a/salt/master.py b/salt/master.py
|
||||||
|
index fc243ef674..3d2ba1e29d 100644
|
||||||
|
--- a/salt/master.py
|
||||||
|
+++ b/salt/master.py
|
||||||
|
@@ -1790,7 +1790,7 @@ class AESFuncs(TransportMethods):
|
||||||
|
def pub_ret(self, load):
|
||||||
|
"""
|
||||||
|
Request the return data from a specific jid, only allowed
|
||||||
|
- if the requesting minion also initialted the execution.
|
||||||
|
+ if the requesting minion also initiated the execution.
|
||||||
|
|
||||||
|
:param dict load: The minion payload
|
||||||
|
|
||||||
|
diff --git a/salt/minion.py b/salt/minion.py
|
||||||
|
index 4db0d31bd4..2ccd0cd5a9 100644
|
||||||
|
--- a/salt/minion.py
|
||||||
|
+++ b/salt/minion.py
|
||||||
|
@@ -2022,6 +2022,8 @@ class Minion(MinionBase):
|
||||||
|
ret["jid"] = data["jid"]
|
||||||
|
ret["fun"] = data["fun"]
|
||||||
|
ret["fun_args"] = data["arg"]
|
||||||
|
+ if "user" in data:
|
||||||
|
+ ret["user"] = data["user"]
|
||||||
|
if "master_id" in data:
|
||||||
|
ret["master_id"] = data["master_id"]
|
||||||
|
if "metadata" in data:
|
||||||
|
@@ -2141,6 +2143,8 @@ class Minion(MinionBase):
|
||||||
|
ret["jid"] = data["jid"]
|
||||||
|
ret["fun"] = data["fun"]
|
||||||
|
ret["fun_args"] = data["arg"]
|
||||||
|
+ if "user" in data:
|
||||||
|
+ ret["user"] = data["user"]
|
||||||
|
if "metadata" in data:
|
||||||
|
ret["metadata"] = data["metadata"]
|
||||||
|
if minion_instance.connected:
|
||||||
|
diff --git a/salt/utils/event.py b/salt/utils/event.py
|
||||||
|
index 869e12a140..e6d7b00520 100644
|
||||||
|
--- a/salt/utils/event.py
|
||||||
|
+++ b/salt/utils/event.py
|
||||||
|
@@ -902,7 +902,8 @@ class SaltEvent:
|
||||||
|
data["success"] = False
|
||||||
|
data["return"] = "Error: {}.{}".format(tags[0], tags[-1])
|
||||||
|
data["fun"] = fun
|
||||||
|
- data["user"] = load["user"]
|
||||||
|
+ if "user" in load:
|
||||||
|
+ data["user"] = load["user"]
|
||||||
|
self.fire_event(
|
||||||
|
data,
|
||||||
|
tagify([load["jid"], "sub", load["id"], "error", fun], "job"),
|
||||||
|
diff --git a/tests/pytests/integration/states/test_state_test.py b/tests/pytests/integration/states/test_state_test.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..b2328a4c2b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/pytests/integration/states/test_state_test.py
|
||||||
|
@@ -0,0 +1,38 @@
|
||||||
|
+def test_failing_sls(salt_master, salt_minion, salt_cli, caplog):
|
||||||
|
+ """
|
||||||
|
+ Test when running state.sls and the state fails.
|
||||||
|
+ When the master stores the job and attempts to send
|
||||||
|
+ an event a KeyError was previously being logged.
|
||||||
|
+ This test ensures we do not log an error when
|
||||||
|
+ attempting to send an event about a failing state.
|
||||||
|
+ """
|
||||||
|
+ statesls = """
|
||||||
|
+ test_state:
|
||||||
|
+ test.fail_without_changes:
|
||||||
|
+ - name: "bla"
|
||||||
|
+ """
|
||||||
|
+ with salt_master.state_tree.base.temp_file("test_failure.sls", statesls):
|
||||||
|
+ ret = salt_cli.run("state.sls", "test_failure", minion_tgt=salt_minion.id)
|
||||||
|
+ for message in caplog.messages:
|
||||||
|
+ assert "Event iteration failed with" not in message
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_failing_sls_compound(salt_master, salt_minion, salt_cli, caplog):
|
||||||
|
+ """
|
||||||
|
+ Test when running state.sls in a compound command and the state fails.
|
||||||
|
+ When the master stores the job and attempts to send
|
||||||
|
+ an event a KeyError was previously being logged.
|
||||||
|
+ This test ensures we do not log an error when
|
||||||
|
+ attempting to send an event about a failing state.
|
||||||
|
+ """
|
||||||
|
+ statesls = """
|
||||||
|
+ test_state:
|
||||||
|
+ test.fail_without_changes:
|
||||||
|
+ - name: "bla"
|
||||||
|
+ """
|
||||||
|
+ with salt_master.state_tree.base.temp_file("test_failure.sls", statesls):
|
||||||
|
+ ret = salt_cli.run(
|
||||||
|
+ "state.sls,cmd.run", "test_failure,ls", minion_tgt=salt_minion.id
|
||||||
|
+ )
|
||||||
|
+ for message in caplog.messages:
|
||||||
|
+ assert "Event iteration failed with" not in message
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
||||||
|
|
145
implement-the-calling-for-batch-async-from-the-salt-.patch
Normal file
145
implement-the-calling-for-batch-async-from-the-salt-.patch
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
From 7ab208fd2d23eaa582cdbba912d4538d8c87e5f4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Mon, 2 Oct 2023 13:24:15 +0200
|
||||||
|
Subject: [PATCH] Implement the calling for batch async from the salt
|
||||||
|
CLI
|
||||||
|
|
||||||
|
* Implement calling batch async with salt CLI
|
||||||
|
|
||||||
|
* Add the test for calling batch async with salt CLI
|
||||||
|
---
|
||||||
|
salt/cli/salt.py | 53 ++++++++++++++++++++++++++++-
|
||||||
|
tests/pytests/unit/cli/test_salt.py | 50 +++++++++++++++++++++++++++
|
||||||
|
2 files changed, 102 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 tests/pytests/unit/cli/test_salt.py
|
||||||
|
|
||||||
|
diff --git a/salt/cli/salt.py b/salt/cli/salt.py
|
||||||
|
index f90057f668..e19cfa5ce6 100644
|
||||||
|
--- a/salt/cli/salt.py
|
||||||
|
+++ b/salt/cli/salt.py
|
||||||
|
@@ -47,7 +47,12 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
||||||
|
self.exit(2, "{}\n".format(exc))
|
||||||
|
return
|
||||||
|
|
||||||
|
- if self.options.batch or self.options.static:
|
||||||
|
+ if self.options.batch and self.config["async"]:
|
||||||
|
+ # _run_batch_async() will just return the jid and exit
|
||||||
|
+ # Execution will not continue past this point
|
||||||
|
+ # in batch async mode. Batch async is handled by the master.
|
||||||
|
+ self._run_batch_async()
|
||||||
|
+ elif self.options.batch or self.options.static:
|
||||||
|
# _run_batch() will handle all output and
|
||||||
|
# exit with the appropriate error condition
|
||||||
|
# Execution will not continue past this point
|
||||||
|
@@ -296,6 +301,52 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
|
||||||
|
retcode = job_retcode
|
||||||
|
sys.exit(retcode)
|
||||||
|
|
||||||
|
+ def _run_batch_async(self):
|
||||||
|
+ kwargs = {
|
||||||
|
+ "tgt": self.config["tgt"],
|
||||||
|
+ "fun": self.config["fun"],
|
||||||
|
+ "arg": self.config["arg"],
|
||||||
|
+ "timeout": self.options.timeout,
|
||||||
|
+ "show_timeout": self.options.show_timeout,
|
||||||
|
+ "show_jid": self.options.show_jid,
|
||||||
|
+ "batch": self.config["batch"],
|
||||||
|
+ }
|
||||||
|
+ tgt = kwargs.pop("tgt", "")
|
||||||
|
+ fun = kwargs.pop("fun", "")
|
||||||
|
+
|
||||||
|
+ if self.config.get("eauth", ""):
|
||||||
|
+ kwargs.update(
|
||||||
|
+ {
|
||||||
|
+ "eauth": self.config["eauth"],
|
||||||
|
+ }
|
||||||
|
+ )
|
||||||
|
+ for opt in ("username", "password"):
|
||||||
|
+ if opt in self.config:
|
||||||
|
+ kwargs[opt] = self.config[opt]
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ ret = self.local_client.run_job(tgt, fun, **kwargs)
|
||||||
|
+ except (
|
||||||
|
+ AuthenticationError,
|
||||||
|
+ AuthorizationError,
|
||||||
|
+ SaltInvocationError,
|
||||||
|
+ EauthAuthenticationError,
|
||||||
|
+ SaltClientError,
|
||||||
|
+ ) as exc:
|
||||||
|
+ ret = str(exc)
|
||||||
|
+ self.exit(2, "ERROR: {}\n".format(exc))
|
||||||
|
+ if "jid" in ret and "error" not in ret:
|
||||||
|
+ salt.utils.stringutils.print_cli(
|
||||||
|
+ "Executed command with job ID: {}".format(ret["jid"])
|
||||||
|
+ )
|
||||||
|
+ else:
|
||||||
|
+ self._output_ret(ret, self.config.get("output", "nested"))
|
||||||
|
+
|
||||||
|
+ if "error" in ret:
|
||||||
|
+ sys.exit(1)
|
||||||
|
+
|
||||||
|
+ sys.exit(0)
|
||||||
|
+
|
||||||
|
def _print_errors_summary(self, errors):
|
||||||
|
if errors:
|
||||||
|
salt.utils.stringutils.print_cli("\n")
|
||||||
|
diff --git a/tests/pytests/unit/cli/test_salt.py b/tests/pytests/unit/cli/test_salt.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..d9f4b5b097
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/pytests/unit/cli/test_salt.py
|
||||||
|
@@ -0,0 +1,50 @@
|
||||||
|
+import pytest
|
||||||
|
+
|
||||||
|
+from tests.support.mock import MagicMock, patch
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_saltcmd_batch_async_call():
|
||||||
|
+ """
|
||||||
|
+ Test calling batch async with salt CLI
|
||||||
|
+ """
|
||||||
|
+ import salt.cli.salt
|
||||||
|
+
|
||||||
|
+ local_client = MagicMock()
|
||||||
|
+ local_client.run_job = MagicMock(return_value={"jid": 123456})
|
||||||
|
+ with pytest.raises(SystemExit) as exit_info, patch(
|
||||||
|
+ "sys.argv",
|
||||||
|
+ [
|
||||||
|
+ "salt",
|
||||||
|
+ "--batch=10",
|
||||||
|
+ "--async",
|
||||||
|
+ "*",
|
||||||
|
+ "test.arg",
|
||||||
|
+ "arg1",
|
||||||
|
+ "arg2",
|
||||||
|
+ "kwarg1=val1",
|
||||||
|
+ ],
|
||||||
|
+ ), patch("salt.cli.salt.SaltCMD.process_config_dir", MagicMock), patch(
|
||||||
|
+ "salt.output.display_output", MagicMock()
|
||||||
|
+ ), patch(
|
||||||
|
+ "salt.client.get_local_client", return_value=local_client
|
||||||
|
+ ), patch(
|
||||||
|
+ "salt.utils.stringutils.print_cli", MagicMock()
|
||||||
|
+ ) as print_cli:
|
||||||
|
+ salt_cmd = salt.cli.salt.SaltCMD()
|
||||||
|
+ salt_cmd.config = {
|
||||||
|
+ "async": True,
|
||||||
|
+ "batch": 10,
|
||||||
|
+ "tgt": "*",
|
||||||
|
+ "fun": "test.arg",
|
||||||
|
+ "arg": ["arg1", "arg2", {"__kwarg__": True, "kwarg1": "val1"}],
|
||||||
|
+ }
|
||||||
|
+ salt_cmd._mixin_after_parsed_funcs = []
|
||||||
|
+ salt_cmd.run()
|
||||||
|
+
|
||||||
|
+ local_client.run_job.assert_called_once()
|
||||||
|
+ assert local_client.run_job.mock_calls[0].args[0] == "*"
|
||||||
|
+ assert local_client.run_job.mock_calls[0].args[1] == "test.arg"
|
||||||
|
+ assert local_client.run_job.mock_calls[0].kwargs["arg"] == ["arg1", "arg2", {"__kwarg__": True, "kwarg1": "val1"}]
|
||||||
|
+ assert local_client.run_job.mock_calls[0].kwargs["batch"] == 10
|
||||||
|
+ print_cli.assert_called_once_with("Executed command with job ID: 123456")
|
||||||
|
+ assert exit_info.value.code == 0
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
113
improve-pip-target-override-condition-with-venv_pip_.patch
Normal file
113
improve-pip-target-override-condition-with-venv_pip_.patch
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
From da938aa8a572138b5b9b1535c5c3d69326e5194e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Thu, 18 Jan 2024 17:02:23 +0100
|
||||||
|
Subject: [PATCH] Improve pip target override condition with
|
||||||
|
VENV_PIP_TARGET environment variable (bsc#1216850) (#613)
|
||||||
|
|
||||||
|
* Improve pip target override condition
|
||||||
|
|
||||||
|
* Improve pip test with different condition of overriding the target
|
||||||
|
|
||||||
|
* Add changelog entry
|
||||||
|
---
|
||||||
|
changelog/65562.fixed.md | 1 +
|
||||||
|
salt/modules/pip.py | 6 ++--
|
||||||
|
tests/pytests/unit/modules/test_pip.py | 50 +++++++++++++++++---------
|
||||||
|
3 files changed, 38 insertions(+), 19 deletions(-)
|
||||||
|
create mode 100644 changelog/65562.fixed.md
|
||||||
|
|
||||||
|
diff --git a/changelog/65562.fixed.md b/changelog/65562.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..ba483b4b77
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/65562.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Improve the condition of overriding target for pip with VENV_PIP_TARGET environment variable.
|
||||||
|
diff --git a/salt/modules/pip.py b/salt/modules/pip.py
|
||||||
|
index a60bdca0bb..68a2a442a1 100644
|
||||||
|
--- a/salt/modules/pip.py
|
||||||
|
+++ b/salt/modules/pip.py
|
||||||
|
@@ -857,9 +857,11 @@ def install(
|
||||||
|
cmd.extend(["--build", build])
|
||||||
|
|
||||||
|
# Use VENV_PIP_TARGET environment variable value as target
|
||||||
|
- # if set and no target specified on the function call
|
||||||
|
+ # if set and no target specified on the function call.
|
||||||
|
+ # Do not set target if bin_env specified, use default
|
||||||
|
+ # for specified binary environment or expect explicit target specification.
|
||||||
|
target_env = os.environ.get("VENV_PIP_TARGET", None)
|
||||||
|
- if target is None and target_env is not None:
|
||||||
|
+ if target is None and target_env is not None and bin_env is None:
|
||||||
|
target = target_env
|
||||||
|
|
||||||
|
if target:
|
||||||
|
diff --git a/tests/pytests/unit/modules/test_pip.py b/tests/pytests/unit/modules/test_pip.py
|
||||||
|
index b7ad1ea3fd..c03e6ed292 100644
|
||||||
|
--- a/tests/pytests/unit/modules/test_pip.py
|
||||||
|
+++ b/tests/pytests/unit/modules/test_pip.py
|
||||||
|
@@ -1738,28 +1738,44 @@ def test_when_version_is_called_with_a_user_it_should_be_passed_to_undelying_run
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def test_install_target_from_VENV_PIP_TARGET_in_resulting_command(python_binary):
|
||||||
|
+@pytest.mark.parametrize(
|
||||||
|
+ "bin_env,target,target_env,expected_target",
|
||||||
|
+ [
|
||||||
|
+ (None, None, None, None),
|
||||||
|
+ (None, "/tmp/foo", None, "/tmp/foo"),
|
||||||
|
+ (None, None, "/tmp/bar", "/tmp/bar"),
|
||||||
|
+ (None, "/tmp/foo", "/tmp/bar", "/tmp/foo"),
|
||||||
|
+ ("/tmp/venv", "/tmp/foo", None, "/tmp/foo"),
|
||||||
|
+ ("/tmp/venv", None, "/tmp/bar", None),
|
||||||
|
+ ("/tmp/venv", "/tmp/foo", "/tmp/bar", "/tmp/foo"),
|
||||||
|
+ ],
|
||||||
|
+)
|
||||||
|
+def test_install_target_from_VENV_PIP_TARGET_in_resulting_command(
|
||||||
|
+ python_binary, bin_env, target, target_env, expected_target
|
||||||
|
+):
|
||||||
|
pkg = "pep8"
|
||||||
|
- target = "/tmp/foo"
|
||||||
|
- target_env = "/tmp/bar"
|
||||||
|
mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
|
||||||
|
environment = os.environ.copy()
|
||||||
|
- environment["VENV_PIP_TARGET"] = target_env
|
||||||
|
+ real_get_pip_bin = pip._get_pip_bin
|
||||||
|
+
|
||||||
|
+ def mock_get_pip_bin(bin_env):
|
||||||
|
+ if not bin_env:
|
||||||
|
+ return real_get_pip_bin(bin_env)
|
||||||
|
+ return [f"{bin_env}/bin/pip"]
|
||||||
|
+
|
||||||
|
+ if target_env is not None:
|
||||||
|
+ environment["VENV_PIP_TARGET"] = target_env
|
||||||
|
with patch.dict(pip.__salt__, {"cmd.run_all": mock}), patch.object(
|
||||||
|
os, "environ", environment
|
||||||
|
- ):
|
||||||
|
- pip.install(pkg)
|
||||||
|
- expected = [*python_binary, "install", "--target", target_env, pkg]
|
||||||
|
- mock.assert_called_with(
|
||||||
|
- expected,
|
||||||
|
- saltenv="base",
|
||||||
|
- runas=None,
|
||||||
|
- use_vt=False,
|
||||||
|
- python_shell=False,
|
||||||
|
- )
|
||||||
|
- mock.reset_mock()
|
||||||
|
- pip.install(pkg, target=target)
|
||||||
|
- expected = [*python_binary, "install", "--target", target, pkg]
|
||||||
|
+ ), patch.object(pip, "_get_pip_bin", mock_get_pip_bin):
|
||||||
|
+ pip.install(pkg, bin_env=bin_env, target=target)
|
||||||
|
+ expected_binary = python_binary
|
||||||
|
+ if bin_env is not None:
|
||||||
|
+ expected_binary = [f"{bin_env}/bin/pip"]
|
||||||
|
+ if expected_target is not None:
|
||||||
|
+ expected = [*expected_binary, "install", "--target", expected_target, pkg]
|
||||||
|
+ else:
|
||||||
|
+ expected = [*expected_binary, "install", pkg]
|
||||||
|
mock.assert_called_with(
|
||||||
|
expected,
|
||||||
|
saltenv="base",
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
||||||
|
|
204
improve-salt.utils.json.find_json-bsc-1213293.patch
Normal file
204
improve-salt.utils.json.find_json-bsc-1213293.patch
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
From 4e6b445f2dbe8a79d220c697abff946e00b2e57b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Mon, 2 Oct 2023 13:26:20 +0200
|
||||||
|
Subject: [PATCH] Improve salt.utils.json.find_json (bsc#1213293)
|
||||||
|
|
||||||
|
* Improve salt.utils.json.find_json
|
||||||
|
|
||||||
|
* Move tests of find_json to pytest
|
||||||
|
---
|
||||||
|
salt/utils/json.py | 39 +++++++-
|
||||||
|
tests/pytests/unit/utils/test_json.py | 122 ++++++++++++++++++++++++++
|
||||||
|
2 files changed, 158 insertions(+), 3 deletions(-)
|
||||||
|
create mode 100644 tests/pytests/unit/utils/test_json.py
|
||||||
|
|
||||||
|
diff --git a/salt/utils/json.py b/salt/utils/json.py
|
||||||
|
index 33cdbf401d..0845b64694 100644
|
||||||
|
--- a/salt/utils/json.py
|
||||||
|
+++ b/salt/utils/json.py
|
||||||
|
@@ -32,18 +32,51 @@ def find_json(raw):
|
||||||
|
"""
|
||||||
|
ret = {}
|
||||||
|
lines = __split(raw)
|
||||||
|
+ lengths = list(map(len, lines))
|
||||||
|
+ starts = []
|
||||||
|
+ ends = []
|
||||||
|
+
|
||||||
|
+ # Search for possible starts end ends of the json fragments
|
||||||
|
for ind, _ in enumerate(lines):
|
||||||
|
+ line = lines[ind].lstrip()
|
||||||
|
+ if line == "{" or line == "[":
|
||||||
|
+ starts.append((ind, line))
|
||||||
|
+ if line == "}" or line == "]":
|
||||||
|
+ ends.append((ind, line))
|
||||||
|
+
|
||||||
|
+ # List all the possible pairs of starts and ends,
|
||||||
|
+ # and fill the length of each block to sort by size after
|
||||||
|
+ starts_ends = []
|
||||||
|
+ for start, start_br in starts:
|
||||||
|
+ for end, end_br in reversed(ends):
|
||||||
|
+ if end > start and (
|
||||||
|
+ (start_br == "{" and end_br == "}")
|
||||||
|
+ or (start_br == "[" and end_br == "]")
|
||||||
|
+ ):
|
||||||
|
+ starts_ends.append((start, end, sum(lengths[start : end + 1])))
|
||||||
|
+
|
||||||
|
+ # Iterate through all the possible pairs starting from the largest
|
||||||
|
+ starts_ends.sort(key=lambda x: (x[2], x[1] - x[0], x[0]), reverse=True)
|
||||||
|
+ for start, end, _ in starts_ends:
|
||||||
|
+ working = "\n".join(lines[start : end + 1])
|
||||||
|
try:
|
||||||
|
- working = "\n".join(lines[ind:])
|
||||||
|
- except UnicodeDecodeError:
|
||||||
|
- working = "\n".join(salt.utils.data.decode(lines[ind:]))
|
||||||
|
+ ret = json.loads(working)
|
||||||
|
+ except ValueError:
|
||||||
|
+ continue
|
||||||
|
+ if ret:
|
||||||
|
+ return ret
|
||||||
|
|
||||||
|
+ # Fall back to old implementation for backward compatibility
|
||||||
|
+ # excpecting json after the text
|
||||||
|
+ for ind, _ in enumerate(lines):
|
||||||
|
+ working = "\n".join(lines[ind:])
|
||||||
|
try:
|
||||||
|
ret = json.loads(working)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if ret:
|
||||||
|
return ret
|
||||||
|
+
|
||||||
|
if not ret:
|
||||||
|
# Not json, raise an error
|
||||||
|
raise ValueError
|
||||||
|
diff --git a/tests/pytests/unit/utils/test_json.py b/tests/pytests/unit/utils/test_json.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..72b1023003
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/pytests/unit/utils/test_json.py
|
||||||
|
@@ -0,0 +1,122 @@
|
||||||
|
+"""
|
||||||
|
+Tests for salt.utils.json
|
||||||
|
+"""
|
||||||
|
+
|
||||||
|
+import textwrap
|
||||||
|
+
|
||||||
|
+import pytest
|
||||||
|
+
|
||||||
|
+import salt.utils.json
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_find_json():
|
||||||
|
+ some_junk_text = textwrap.dedent(
|
||||||
|
+ """
|
||||||
|
+ Just some junk text
|
||||||
|
+ with multiline
|
||||||
|
+ """
|
||||||
|
+ )
|
||||||
|
+ some_warning_message = textwrap.dedent(
|
||||||
|
+ """
|
||||||
|
+ [WARNING] Test warning message
|
||||||
|
+ """
|
||||||
|
+ )
|
||||||
|
+ test_small_json = textwrap.dedent(
|
||||||
|
+ """
|
||||||
|
+ {
|
||||||
|
+ "local": true
|
||||||
|
+ }
|
||||||
|
+ """
|
||||||
|
+ )
|
||||||
|
+ test_sample_json = """
|
||||||
|
+ {
|
||||||
|
+ "glossary": {
|
||||||
|
+ "title": "example glossary",
|
||||||
|
+ "GlossDiv": {
|
||||||
|
+ "title": "S",
|
||||||
|
+ "GlossList": {
|
||||||
|
+ "GlossEntry": {
|
||||||
|
+ "ID": "SGML",
|
||||||
|
+ "SortAs": "SGML",
|
||||||
|
+ "GlossTerm": "Standard Generalized Markup Language",
|
||||||
|
+ "Acronym": "SGML",
|
||||||
|
+ "Abbrev": "ISO 8879:1986",
|
||||||
|
+ "GlossDef": {
|
||||||
|
+ "para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||||
|
+ "GlossSeeAlso": ["GML", "XML"]
|
||||||
|
+ },
|
||||||
|
+ "GlossSee": "markup"
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ """
|
||||||
|
+ expected_ret = {
|
||||||
|
+ "glossary": {
|
||||||
|
+ "GlossDiv": {
|
||||||
|
+ "GlossList": {
|
||||||
|
+ "GlossEntry": {
|
||||||
|
+ "GlossDef": {
|
||||||
|
+ "GlossSeeAlso": ["GML", "XML"],
|
||||||
|
+ "para": (
|
||||||
|
+ "A meta-markup language, used to create markup"
|
||||||
|
+ " languages such as DocBook."
|
||||||
|
+ ),
|
||||||
|
+ },
|
||||||
|
+ "GlossSee": "markup",
|
||||||
|
+ "Acronym": "SGML",
|
||||||
|
+ "GlossTerm": "Standard Generalized Markup Language",
|
||||||
|
+ "SortAs": "SGML",
|
||||||
|
+ "Abbrev": "ISO 8879:1986",
|
||||||
|
+ "ID": "SGML",
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ "title": "S",
|
||||||
|
+ },
|
||||||
|
+ "title": "example glossary",
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ # First test the valid JSON
|
||||||
|
+ ret = salt.utils.json.find_json(test_sample_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now pre-pend some garbage and re-test
|
||||||
|
+ garbage_prepend_json = f"{some_junk_text}{test_sample_json}"
|
||||||
|
+ ret = salt.utils.json.find_json(garbage_prepend_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now post-pend some garbage and re-test
|
||||||
|
+ garbage_postpend_json = f"{test_sample_json}{some_junk_text}"
|
||||||
|
+ ret = salt.utils.json.find_json(garbage_postpend_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now pre-pend some warning and re-test
|
||||||
|
+ warning_prepend_json = f"{some_warning_message}{test_sample_json}"
|
||||||
|
+ ret = salt.utils.json.find_json(warning_prepend_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now post-pend some warning and re-test
|
||||||
|
+ warning_postpend_json = f"{test_sample_json}{some_warning_message}"
|
||||||
|
+ ret = salt.utils.json.find_json(warning_postpend_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now put around some garbage and re-test
|
||||||
|
+ garbage_around_json = f"{some_junk_text}{test_sample_json}{some_junk_text}"
|
||||||
|
+ ret = salt.utils.json.find_json(garbage_around_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now pre-pend small json and re-test
|
||||||
|
+ small_json_pre_json = f"{test_small_json}{test_sample_json}"
|
||||||
|
+ ret = salt.utils.json.find_json(small_json_pre_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Now post-pend small json and re-test
|
||||||
|
+ small_json_post_json = f"{test_sample_json}{test_small_json}"
|
||||||
|
+ ret = salt.utils.json.find_json(small_json_post_json)
|
||||||
|
+ assert ret == expected_ret
|
||||||
|
+
|
||||||
|
+ # Test to see if a ValueError is raised if no JSON is passed in
|
||||||
|
+ with pytest.raises(ValueError):
|
||||||
|
+ ret = salt.utils.json.find_json(some_junk_text)
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
31
only-call-native_str-on-curl_debug-message-in-tornad.patch
Normal file
31
only-call-native_str-on-curl_debug-message-in-tornad.patch
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
From b76b74bd9640adf3b6798e4de4b89aaa7af62c9f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Mon, 2 Oct 2023 13:24:43 +0200
|
||||||
|
Subject: [PATCH] Only call native_str on curl_debug message in tornado
|
||||||
|
when needed
|
||||||
|
|
||||||
|
Co-authored-by: Ben Darnell <ben@bendarnell.com>
|
||||||
|
---
|
||||||
|
salt/ext/tornado/curl_httpclient.py | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/salt/ext/tornado/curl_httpclient.py b/salt/ext/tornado/curl_httpclient.py
|
||||||
|
index 8652343cf7..9e4133fd13 100644
|
||||||
|
--- a/salt/ext/tornado/curl_httpclient.py
|
||||||
|
+++ b/salt/ext/tornado/curl_httpclient.py
|
||||||
|
@@ -494,10 +494,11 @@ class CurlAsyncHTTPClient(AsyncHTTPClient):
|
||||||
|
|
||||||
|
def _curl_debug(self, debug_type, debug_msg):
|
||||||
|
debug_types = ('I', '<', '>', '<', '>')
|
||||||
|
- debug_msg = native_str(debug_msg)
|
||||||
|
if debug_type == 0:
|
||||||
|
+ debug_msg = native_str(debug_msg)
|
||||||
|
curl_log.debug('%s', debug_msg.strip())
|
||||||
|
elif debug_type in (1, 2):
|
||||||
|
+ debug_msg = native_str(debug_msg)
|
||||||
|
for line in debug_msg.splitlines():
|
||||||
|
curl_log.debug('%s %s', debug_types[debug_type], line)
|
||||||
|
elif debug_type == 4:
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
135
prefer-unittest.mock-for-python-versions-that-are-su.patch
Normal file
135
prefer-unittest.mock-for-python-versions-that-are-su.patch
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
From 107de57586f0b0f784771543b942dfb6bb70453a Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Yeray=20Guti=C3=A9rrez=20Cedr=C3=A9s?=
|
||||||
|
<yeray.gutierrez@suse.com>
|
||||||
|
Date: Wed, 13 Dec 2023 11:03:45 +0000
|
||||||
|
Subject: [PATCH] Prefer unittest.mock for Python versions that are
|
||||||
|
sufficient
|
||||||
|
|
||||||
|
---
|
||||||
|
requirements/pytest.txt | 2 +-
|
||||||
|
.../unit/cloud/clouds/test_dimensiondata.py | 4 +-
|
||||||
|
tests/pytests/unit/cloud/clouds/test_gce.py | 4 +-
|
||||||
|
tests/support/mock.py | 48 +++++++++----------
|
||||||
|
4 files changed, 25 insertions(+), 33 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/requirements/pytest.txt b/requirements/pytest.txt
|
||||||
|
index 5b67583a3d..0bead83f5b 100644
|
||||||
|
--- a/requirements/pytest.txt
|
||||||
|
+++ b/requirements/pytest.txt
|
||||||
|
@@ -1,4 +1,4 @@
|
||||||
|
-mock >= 3.0.0
|
||||||
|
+mock >= 3.0.0; python_version < '3.8'
|
||||||
|
# PyTest
|
||||||
|
pytest >= 7.0.1; python_version <= "3.6"
|
||||||
|
pytest >= 7.2.0; python_version > "3.6"
|
||||||
|
diff --git a/tests/pytests/unit/cloud/clouds/test_dimensiondata.py b/tests/pytests/unit/cloud/clouds/test_dimensiondata.py
|
||||||
|
index e196805004..aab2e686f2 100644
|
||||||
|
--- a/tests/pytests/unit/cloud/clouds/test_dimensiondata.py
|
||||||
|
+++ b/tests/pytests/unit/cloud/clouds/test_dimensiondata.py
|
||||||
|
@@ -11,7 +11,6 @@ from salt.cloud.clouds import dimensiondata
|
||||||
|
from salt.exceptions import SaltCloudSystemExit
|
||||||
|
from salt.utils.versions import Version
|
||||||
|
from tests.support.mock import MagicMock
|
||||||
|
-from tests.support.mock import __version__ as mock_version
|
||||||
|
from tests.support.mock import patch
|
||||||
|
|
||||||
|
try:
|
||||||
|
@@ -144,8 +143,7 @@ def test_import():
|
||||||
|
with patch("salt.config.check_driver_dependencies", return_value=True) as p:
|
||||||
|
get_deps = dimensiondata.get_dependencies()
|
||||||
|
assert get_deps is True
|
||||||
|
- if Version(mock_version) >= Version("2.0.0"):
|
||||||
|
- assert p.call_count >= 1
|
||||||
|
+ assert p.call_count >= 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_provider_matches():
|
||||||
|
diff --git a/tests/pytests/unit/cloud/clouds/test_gce.py b/tests/pytests/unit/cloud/clouds/test_gce.py
|
||||||
|
index 265818016e..ec1346a978 100644
|
||||||
|
--- a/tests/pytests/unit/cloud/clouds/test_gce.py
|
||||||
|
+++ b/tests/pytests/unit/cloud/clouds/test_gce.py
|
||||||
|
@@ -13,7 +13,6 @@ from salt.cloud.clouds import gce
|
||||||
|
from salt.exceptions import SaltCloudSystemExit
|
||||||
|
from salt.utils.versions import Version
|
||||||
|
from tests.support.mock import MagicMock
|
||||||
|
-from tests.support.mock import __version__ as mock_version
|
||||||
|
from tests.support.mock import call, patch
|
||||||
|
|
||||||
|
|
||||||
|
@@ -281,8 +280,7 @@ def test_import():
|
||||||
|
with patch("salt.config.check_driver_dependencies", return_value=True) as p:
|
||||||
|
get_deps = gce.get_dependencies()
|
||||||
|
assert get_deps is True
|
||||||
|
- if Version(mock_version) >= Version("2.0.0"):
|
||||||
|
- p.assert_called_once()
|
||||||
|
+ p.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
diff --git a/tests/support/mock.py b/tests/support/mock.py
|
||||||
|
index 2256ad8f5d..59e5fcbc8e 100644
|
||||||
|
--- a/tests/support/mock.py
|
||||||
|
+++ b/tests/support/mock.py
|
||||||
|
@@ -18,37 +18,33 @@ import copy
|
||||||
|
import errno
|
||||||
|
import fnmatch
|
||||||
|
import sys
|
||||||
|
-
|
||||||
|
-# By these days, we should blowup if mock is not available
|
||||||
|
-import mock # pylint: disable=blacklisted-external-import
|
||||||
|
-
|
||||||
|
-# pylint: disable=no-name-in-module,no-member
|
||||||
|
-from mock import (
|
||||||
|
- ANY,
|
||||||
|
- DEFAULT,
|
||||||
|
- FILTER_DIR,
|
||||||
|
- MagicMock,
|
||||||
|
- Mock,
|
||||||
|
- NonCallableMagicMock,
|
||||||
|
- NonCallableMock,
|
||||||
|
- PropertyMock,
|
||||||
|
- __version__,
|
||||||
|
- call,
|
||||||
|
- create_autospec,
|
||||||
|
- patch,
|
||||||
|
- sentinel,
|
||||||
|
-)
|
||||||
|
+import importlib
|
||||||
|
+
|
||||||
|
+current_version = (sys.version_info.major, sys.version_info.minor)
|
||||||
|
+
|
||||||
|
+# Prefer unittest.mock for Python versions that are sufficient
|
||||||
|
+if current_version >= (3,8):
|
||||||
|
+ mock = importlib.import_module('unittest.mock')
|
||||||
|
+else:
|
||||||
|
+ mock = importlib.import_module('mock')
|
||||||
|
+
|
||||||
|
+ANY = mock.ANY
|
||||||
|
+DEFAULT = mock.DEFAULT
|
||||||
|
+FILTER_DIR = mock.FILTER_DIR
|
||||||
|
+MagicMock = mock.MagicMock
|
||||||
|
+Mock = mock.Mock
|
||||||
|
+NonCallableMagicMock = mock.NonCallableMagicMock
|
||||||
|
+NonCallableMock = mock.NonCallableMock
|
||||||
|
+PropertyMock = mock.PropertyMock
|
||||||
|
+call = mock.call
|
||||||
|
+create_autospec = mock.create_autospec
|
||||||
|
+patch = mock.patch
|
||||||
|
+sentinel = mock.sentinel
|
||||||
|
|
||||||
|
import salt.utils.stringutils
|
||||||
|
|
||||||
|
# pylint: disable=no-name-in-module,no-member
|
||||||
|
|
||||||
|
-
|
||||||
|
-__mock_version = tuple(
|
||||||
|
- int(part) for part in mock.__version__.split(".") if part.isdigit()
|
||||||
|
-) # pylint: disable=no-member
|
||||||
|
-
|
||||||
|
-
|
||||||
|
class MockFH:
|
||||||
|
def __init__(self, filename, read_data, *args, **kwargs):
|
||||||
|
self.filename = filename
|
||||||
|
--
|
||||||
|
2.41.0
|
||||||
|
|
194
revert-make-sure-configured-user-is-properly-set-by-.patch
Normal file
194
revert-make-sure-configured-user-is-properly-set-by-.patch
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
From d9980c8d2cfedfd6f08543face6ee7e34e9d1b54 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
|
||||||
|
<psuarezhernandez@suse.com>
|
||||||
|
Date: Thu, 16 Nov 2023 09:23:58 +0000
|
||||||
|
Subject: [PATCH] Revert "Make sure configured user is properly set by
|
||||||
|
Salt (bsc#1210994) (#596)" (#614)
|
||||||
|
|
||||||
|
This reverts commit 5ea4add5c8e2bed50b9825edfff7565e5f6124f3.
|
||||||
|
---
|
||||||
|
pkg/common/salt-master.service | 1 -
|
||||||
|
pkg/old/deb/salt-master.service | 1 -
|
||||||
|
pkg/old/suse/salt-master.service | 1 -
|
||||||
|
salt/cli/daemons.py | 27 -------------------
|
||||||
|
salt/cli/ssh.py | 8 ------
|
||||||
|
salt/utils/verify.py | 4 +--
|
||||||
|
.../integration/cli/test_salt_minion.py | 4 +--
|
||||||
|
7 files changed, 4 insertions(+), 42 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/pkg/common/salt-master.service b/pkg/common/salt-master.service
|
||||||
|
index 257ecc283f..377c87afeb 100644
|
||||||
|
--- a/pkg/common/salt-master.service
|
||||||
|
+++ b/pkg/common/salt-master.service
|
||||||
|
@@ -8,7 +8,6 @@ LimitNOFILE=100000
|
||||||
|
Type=notify
|
||||||
|
NotifyAccess=all
|
||||||
|
ExecStart=/usr/bin/salt-master
|
||||||
|
-User=salt
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
diff --git a/pkg/old/deb/salt-master.service b/pkg/old/deb/salt-master.service
|
||||||
|
index f9dca296b4..b5d0cdd22c 100644
|
||||||
|
--- a/pkg/old/deb/salt-master.service
|
||||||
|
+++ b/pkg/old/deb/salt-master.service
|
||||||
|
@@ -7,7 +7,6 @@ LimitNOFILE=16384
|
||||||
|
Type=notify
|
||||||
|
NotifyAccess=all
|
||||||
|
ExecStart=/usr/bin/salt-master
|
||||||
|
-User=salt
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
diff --git a/pkg/old/suse/salt-master.service b/pkg/old/suse/salt-master.service
|
||||||
|
index caabca511c..9e002d16ca 100644
|
||||||
|
--- a/pkg/old/suse/salt-master.service
|
||||||
|
+++ b/pkg/old/suse/salt-master.service
|
||||||
|
@@ -8,7 +8,6 @@ LimitNOFILE=100000
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/salt-master
|
||||||
|
TasksMax=infinity
|
||||||
|
-User=salt
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
diff --git a/salt/cli/daemons.py b/salt/cli/daemons.py
|
||||||
|
index c9ee9ced91..ecc05c919e 100644
|
||||||
|
--- a/salt/cli/daemons.py
|
||||||
|
+++ b/salt/cli/daemons.py
|
||||||
|
@@ -7,7 +7,6 @@ import logging
|
||||||
|
import os
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
-import salt.defaults.exitcodes
|
||||||
|
import salt.utils.kinds as kinds
|
||||||
|
from salt.exceptions import SaltClientError, SaltSystemExit, get_error_message
|
||||||
|
from salt.utils import migrations
|
||||||
|
@@ -74,16 +73,6 @@ class DaemonsMixin: # pylint: disable=no-init
|
||||||
|
self.__class__.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
- def verify_user(self):
|
||||||
|
- """
|
||||||
|
- Verify Salt configured user for Salt and shutdown daemon if not valid.
|
||||||
|
-
|
||||||
|
- :return:
|
||||||
|
- """
|
||||||
|
- if not check_user(self.config["user"]):
|
||||||
|
- self.action_log_info("Cannot switch to configured user for Salt. Exiting")
|
||||||
|
- self.shutdown(salt.defaults.exitcodes.EX_NOUSER)
|
||||||
|
-
|
||||||
|
def action_log_info(self, action):
|
||||||
|
"""
|
||||||
|
Say daemon starting.
|
||||||
|
@@ -189,10 +178,6 @@ class Master(
|
||||||
|
self.config["interface"] = ip_bracket(self.config["interface"])
|
||||||
|
migrations.migrate_paths(self.config)
|
||||||
|
|
||||||
|
- # Ensure configured user is valid and environment is properly set
|
||||||
|
- # before initializating rest of the stack.
|
||||||
|
- self.verify_user()
|
||||||
|
-
|
||||||
|
# Late import so logging works correctly
|
||||||
|
import salt.master
|
||||||
|
|
||||||
|
@@ -305,10 +290,6 @@ class Minion(
|
||||||
|
|
||||||
|
transport = self.config.get("transport").lower()
|
||||||
|
|
||||||
|
- # Ensure configured user is valid and environment is properly set
|
||||||
|
- # before initializating rest of the stack.
|
||||||
|
- self.verify_user()
|
||||||
|
-
|
||||||
|
try:
|
||||||
|
# Late import so logging works correctly
|
||||||
|
import salt.minion
|
||||||
|
@@ -497,10 +478,6 @@ class ProxyMinion(
|
||||||
|
self.action_log_info("An instance is already running. Exiting")
|
||||||
|
self.shutdown(1)
|
||||||
|
|
||||||
|
- # Ensure configured user is valid and environment is properly set
|
||||||
|
- # before initializating rest of the stack.
|
||||||
|
- self.verify_user()
|
||||||
|
-
|
||||||
|
# TODO: AIO core is separate from transport
|
||||||
|
# Late import so logging works correctly
|
||||||
|
import salt.minion
|
||||||
|
@@ -599,10 +576,6 @@ class Syndic(
|
||||||
|
|
||||||
|
self.action_log_info('Setting up "{}"'.format(self.config["id"]))
|
||||||
|
|
||||||
|
- # Ensure configured user is valid and environment is properly set
|
||||||
|
- # before initializating rest of the stack.
|
||||||
|
- self.verify_user()
|
||||||
|
-
|
||||||
|
# Late import so logging works correctly
|
||||||
|
import salt.minion
|
||||||
|
|
||||||
|
diff --git a/salt/cli/ssh.py b/salt/cli/ssh.py
|
||||||
|
index 672f32b8c0..6048cb5f58 100644
|
||||||
|
--- a/salt/cli/ssh.py
|
||||||
|
+++ b/salt/cli/ssh.py
|
||||||
|
@@ -1,9 +1,7 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import salt.client.ssh
|
||||||
|
-import salt.defaults.exitcodes
|
||||||
|
import salt.utils.parsers
|
||||||
|
-from salt.utils.verify import check_user
|
||||||
|
|
||||||
|
|
||||||
|
class SaltSSH(salt.utils.parsers.SaltSSHOptionParser):
|
||||||
|
@@ -17,11 +15,5 @@ class SaltSSH(salt.utils.parsers.SaltSSHOptionParser):
|
||||||
|
# that won't be used anyways with -H or --hosts
|
||||||
|
self.parse_args()
|
||||||
|
|
||||||
|
- if not check_user(self.config["user"]):
|
||||||
|
- self.exit(
|
||||||
|
- salt.defaults.exitcodes.EX_NOUSER,
|
||||||
|
- "Cannot switch to configured user for Salt. Exiting",
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
ssh = salt.client.ssh.SSH(self.config)
|
||||||
|
ssh.run()
|
||||||
|
diff --git a/salt/utils/verify.py b/salt/utils/verify.py
|
||||||
|
index 7899fbe538..879128f231 100644
|
||||||
|
--- a/salt/utils/verify.py
|
||||||
|
+++ b/salt/utils/verify.py
|
||||||
|
@@ -335,8 +335,8 @@ def check_user(user):
|
||||||
|
|
||||||
|
# We could just reset the whole environment but let's just override
|
||||||
|
# the variables we can get from pwuser
|
||||||
|
- # We ensure HOME is always present and set according to pwuser
|
||||||
|
- os.environ["HOME"] = pwuser.pw_dir
|
||||||
|
+ if "HOME" in os.environ:
|
||||||
|
+ os.environ["HOME"] = pwuser.pw_dir
|
||||||
|
|
||||||
|
if "SHELL" in os.environ:
|
||||||
|
os.environ["SHELL"] = pwuser.pw_shell
|
||||||
|
diff --git a/tests/pytests/integration/cli/test_salt_minion.py b/tests/pytests/integration/cli/test_salt_minion.py
|
||||||
|
index bde2dd51d7..c0d6013474 100644
|
||||||
|
--- a/tests/pytests/integration/cli/test_salt_minion.py
|
||||||
|
+++ b/tests/pytests/integration/cli/test_salt_minion.py
|
||||||
|
@@ -41,7 +41,7 @@ def test_exit_status_unknown_user(salt_master, minion_id):
|
||||||
|
factory = salt_master.salt_minion_daemon(
|
||||||
|
minion_id, overrides={"user": "unknown-user"}
|
||||||
|
)
|
||||||
|
- factory.start(start_timeout=30, max_start_attempts=1)
|
||||||
|
+ factory.start(start_timeout=10, max_start_attempts=1)
|
||||||
|
|
||||||
|
assert exc.value.process_result.returncode == salt.defaults.exitcodes.EX_NOUSER
|
||||||
|
assert "The user is not available." in exc.value.process_result.stderr
|
||||||
|
@@ -53,7 +53,7 @@ def test_exit_status_unknown_argument(salt_master, minion_id):
|
||||||
|
"""
|
||||||
|
with pytest.raises(FactoryNotStarted) as exc:
|
||||||
|
factory = salt_master.salt_minion_daemon(minion_id)
|
||||||
|
- factory.start("--unknown-argument", start_timeout=30, max_start_attempts=1)
|
||||||
|
+ factory.start("--unknown-argument", start_timeout=10, max_start_attempts=1)
|
||||||
|
|
||||||
|
assert exc.value.process_result.returncode == salt.defaults.exitcodes.EX_USAGE
|
||||||
|
assert "Usage" in exc.value.process_result.stderr
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
||||||
|
|
51
salt.changes
51
salt.changes
@ -1,3 +1,54 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Thu Feb 1 14:48:40 UTC 2024 - Pablo Suárez Hernández <pablo.suarezhernandez@suse.com>
|
||||||
|
|
||||||
|
- Prevent directory traversal when creating syndic cache directory
|
||||||
|
on the master (CVE-2024-22231, bsc#1219430)
|
||||||
|
- Prevent directory traversal attacks in the master's serve_file
|
||||||
|
method (CVE-2024-22232, bsc#1219431)
|
||||||
|
- Prevent exceptions with fileserver.update when called via state (bsc#1218482)
|
||||||
|
- Improve pip target override condition with VENV_PIP_TARGET
|
||||||
|
environment variable (bsc#1216850)
|
||||||
|
- Fixed KeyError in logs when running a state that fails
|
||||||
|
- Ensure that pillar refresh loads beacons from pillar without restart
|
||||||
|
- Fix the aptpkg.py unit test failure
|
||||||
|
- Prefer unittest.mock to python-mock in test suite
|
||||||
|
- Enable "KeepAlive" probes for Salt SSH executions (bsc#1211649)
|
||||||
|
- Revert changes to set Salt configured user early in the stack (bsc#1216284)
|
||||||
|
- Align behavior of some modules when using salt-call via symlink (bsc#1215963)
|
||||||
|
- Fix gitfs "__env__" and improve cache cleaning (bsc#1193948)
|
||||||
|
- Remove python-boto dependency for the python3-salt-testsuite package for Tumbleweed
|
||||||
|
- Randomize pre_flight_script path (CVE-2023-34049 bsc#1215157)
|
||||||
|
- Allow all primitive grain types for autosign_grains (bsc#1214477)
|
||||||
|
- Fix optimization_order opt to prevent testsuite fails
|
||||||
|
- Improve salt.utils.json.find_json to avoid fails (bsc#1213293)
|
||||||
|
- Use salt-call from salt bundle with transactional_update
|
||||||
|
- Only call native_str on curl_debug message in tornado when needed
|
||||||
|
- Implement the calling for batch async from the salt CLI
|
||||||
|
- Fix calculation of SLS context vars when trailing dots
|
||||||
|
on targetted sls/state (bsc#1213518)
|
||||||
|
- Rename salt-tests to python3-salt-testsuite
|
||||||
|
|
||||||
|
- Added:
|
||||||
|
* enable-keepalive-probes-for-salt-ssh-executions-bsc-.patch
|
||||||
|
* allow-all-primitive-grain-types-for-autosign_grains-.patch
|
||||||
|
* fixed-keyerror-in-logs-when-running-a-state-that-fai.patch
|
||||||
|
* use-salt-call-from-salt-bundle-with-transactional_up.patch
|
||||||
|
* implement-the-calling-for-batch-async-from-the-salt-.patch
|
||||||
|
* fix-calculation-of-sls-context-vars-when-trailing-do.patch
|
||||||
|
* prefer-unittest.mock-for-python-versions-that-are-su.patch
|
||||||
|
* fix-cve-2023-34049-bsc-1215157.patch
|
||||||
|
* fix-gitfs-__env__-and-improve-cache-cleaning-bsc-119.patch
|
||||||
|
* allow-kwargs-for-fileserver-roots-update-bsc-1218482.patch
|
||||||
|
* dereference-symlinks-to-set-proper-__cli-opt-bsc-121.patch
|
||||||
|
* revert-make-sure-configured-user-is-properly-set-by-.patch
|
||||||
|
* fix-cve-2024-22231-and-cve-2024-22232-bsc-1219430-bs.patch
|
||||||
|
* improve-pip-target-override-condition-with-venv_pip_.patch
|
||||||
|
* only-call-native_str-on-curl_debug-message-in-tornad.patch
|
||||||
|
* update-__pillar__-during-pillar_refresh.patch
|
||||||
|
* improve-salt.utils.json.find_json-bsc-1213293.patch
|
||||||
|
* fix-the-aptpkg.py-unit-test-failure.patch
|
||||||
|
* fix-optimization_order-opt-to-prevent-test-fails.patch
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Wed Sep 20 15:04:34 UTC 2023 - Pablo Suárez Hernández <pablo.suarezhernandez@suse.com>
|
Wed Sep 20 15:04:34 UTC 2023 - Pablo Suárez Hernández <pablo.suarezhernandez@suse.com>
|
||||||
|
|
||||||
|
87
salt.spec
87
salt.spec
@ -296,7 +296,7 @@ Patch75: fix-tests-to-make-them-running-with-salt-testsuite.patch
|
|||||||
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/f82860b8ad3ee786762fa02fa1a6eaf6e24dc8d4
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/f82860b8ad3ee786762fa02fa1a6eaf6e24dc8d4
|
||||||
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65020
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65020
|
||||||
Patch76: do-not-fail-on-bad-message-pack-message-bsc-1213441-.patch
|
Patch76: do-not-fail-on-bad-message-pack-message-bsc-1213441-.patch
|
||||||
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64510
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64510 (dropped at patch 91)
|
||||||
Patch77: make-sure-configured-user-is-properly-set-by-salt-bs.patch
|
Patch77: make-sure-configured-user-is-properly-set-by-salt-bs.patch
|
||||||
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64959
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64959
|
||||||
Patch78: fixed-gitfs-cachedir_basename-to-avoid-hash-collisio.patch
|
Patch78: fixed-gitfs-cachedir_basename-to-avoid-hash-collisio.patch
|
||||||
@ -304,6 +304,46 @@ Patch78: fixed-gitfs-cachedir_basename-to-avoid-hash-collisio.patch
|
|||||||
Patch79: revert-usage-of-long-running-req-channel-bsc-1213960.patch
|
Patch79: revert-usage-of-long-running-req-channel-bsc-1213960.patch
|
||||||
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65238
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65238
|
||||||
Patch80: write-salt-version-before-building-when-using-with-s.patch
|
Patch80: write-salt-version-before-building-when-using-with-s.patch
|
||||||
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65036
|
||||||
|
Patch81: fix-calculation-of-sls-context-vars-when-trailing-do.patch
|
||||||
|
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/594
|
||||||
|
Patch82: implement-the-calling-for-batch-async-from-the-salt-.patch
|
||||||
|
# PATCH-FIX_UPSTREAM: https://github.com/tornadoweb/tornado/pull/2277
|
||||||
|
Patch83: only-call-native_str-on-curl_debug-message-in-tornad.patch
|
||||||
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65204
|
||||||
|
Patch84: use-salt-call-from-salt-bundle-with-transactional_up.patch
|
||||||
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65181
|
||||||
|
Patch85: improve-salt.utils.json.find_json-bsc-1213293.patch
|
||||||
|
# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65266
|
||||||
|
Patch86: fix-optimization_order-opt-to-prevent-test-fails.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65232
|
||||||
|
Patch87: allow-all-primitive-grain-types-for-autosign_grains-.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65482
|
||||||
|
Patch88: fix-cve-2023-34049-bsc-1215157.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65017
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65136
|
||||||
|
Patch89: fix-gitfs-__env__-and-improve-cache-cleaning-bsc-119.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65435
|
||||||
|
Patch90: dereference-symlinks-to-set-proper-__cli-opt-bsc-121.patch
|
||||||
|
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/614 (revert patch 77)
|
||||||
|
Patch91: revert-make-sure-configured-user-is-properly-set-by-.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65488
|
||||||
|
Patch92: enable-keepalive-probes-for-salt-ssh-executions-bsc-.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65644
|
||||||
|
Patch93: prefer-unittest.mock-for-python-versions-that-are-su.patch
|
||||||
|
# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/620
|
||||||
|
Patch94: fix-the-aptpkg.py-unit-test-failure.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65092
|
||||||
|
Patch95: update-__pillar__-during-pillar_refresh.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65969
|
||||||
|
Patch96: fix-cve-2024-22231-and-cve-2024-22232-bsc-1219430-bs.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65009
|
||||||
|
Patch97: fixed-keyerror-in-logs-when-running-a-state-that-fai.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65562
|
||||||
|
Patch98: improve-pip-target-override-condition-with-venv_pip_.patch
|
||||||
|
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/65819
|
||||||
|
Patch99: allow-kwargs-for-fileserver-roots-update-bsc-1218482.patch
|
||||||
|
|
||||||
|
|
||||||
### IMPORTANT: The line below is used as a snippet marker. Do not touch it.
|
### IMPORTANT: The line below is used as a snippet marker. Do not touch it.
|
||||||
### SALT PATCHES LIST END
|
### SALT PATCHES LIST END
|
||||||
@ -659,12 +699,30 @@ Requires(pre): %fillup_prereq
|
|||||||
Salt ssh is a master running without zmq.
|
Salt ssh is a master running without zmq.
|
||||||
it enables the management of minions over a ssh connection.
|
it enables the management of minions over a ssh connection.
|
||||||
|
|
||||||
%package tests
|
%package -n python3-salt-testsuite
|
||||||
Summary: Unit and integration tests for Salt
|
Summary: Unit and integration tests for Salt
|
||||||
Requires: %{name} = %{version}-%{release}
|
Requires: %{name} = %{version}-%{release}
|
||||||
|
Requires: python3-CherryPy
|
||||||
|
Requires: python3-Genshi
|
||||||
|
Requires: python3-Mako
|
||||||
|
%if !0%{?suse_version} > 1600 || 0%{?centos}
|
||||||
|
Requires: python3-boto
|
||||||
|
%endif
|
||||||
|
Requires: python3-boto3
|
||||||
|
Requires: python3-docker
|
||||||
|
Requires: python3-mock
|
||||||
|
Requires: python3-pygit2
|
||||||
|
Requires: python3-pytest >= 7.0.1
|
||||||
|
Requires: python3-pytest-httpserver
|
||||||
|
Requires: python3-pytest-salt-factories >= 1.0.0~rc21
|
||||||
|
Requires: python3-pytest-subtests
|
||||||
|
Requires: python3-testinfra
|
||||||
|
Requires: python3-yamllint
|
||||||
|
|
||||||
%description tests
|
Obsoletes: %{name}-tests
|
||||||
Collections of unit and integration tests for Salt
|
|
||||||
|
%description -n python3-salt-testsuite
|
||||||
|
Collection of unit, functional, and integration tests for %{name}.
|
||||||
|
|
||||||
%if %{with bash_completion}
|
%if %{with bash_completion}
|
||||||
%package bash-completion
|
%package bash-completion
|
||||||
@ -812,10 +870,12 @@ install -Dd -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d/
|
|||||||
install -Dpm 0644 salt/cli/support/profiles/* %{buildroot}%{python3_sitelib}/salt/cli/support/profiles
|
install -Dpm 0644 salt/cli/support/profiles/* %{buildroot}%{python3_sitelib}/salt/cli/support/profiles
|
||||||
|
|
||||||
# Install Salt tests
|
# Install Salt tests
|
||||||
install -Dd -m 0750 %{buildroot}%{_datadir}/salt
|
install -Dd %{buildroot}%{python3_sitelib}/salt-testsuite
|
||||||
install -Dd -m 0750 %{buildroot}%{_datadir}/salt/tests
|
cp -a tests %{buildroot}%{python3_sitelib}/salt-testsuite/
|
||||||
cp -a tests/* %{buildroot}%{_datadir}/salt/tests/
|
# Remove runtests.py which is not used as deprecated method of running the tests
|
||||||
sed -i '1s=^#!/usr/bin/\(python\|env python\)[0-9.]*=#!/usr/bin/python3=' %{buildroot}%{_datadir}/salt/tests/runtests.py
|
rm %{buildroot}%{python3_sitelib}/salt-testsuite/tests/runtests.py
|
||||||
|
# Copy conf files to the testsuite as they are used by the tests
|
||||||
|
cp -a conf %{buildroot}%{python3_sitelib}/salt-testsuite/
|
||||||
|
|
||||||
## Install Zypper plugins only on SUSE machines
|
## Install Zypper plugins only on SUSE machines
|
||||||
%if 0%{?suse_version}
|
%if 0%{?suse_version}
|
||||||
@ -1392,7 +1452,10 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version
|
|||||||
|
|
||||||
%files -n python3-salt
|
%files -n python3-salt
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%{python3_sitelib}/*
|
%dir %{python3_sitelib}/salt
|
||||||
|
%dir %{python3_sitelib}/salt-*.egg-info
|
||||||
|
%{python3_sitelib}/salt/*
|
||||||
|
%{python3_sitelib}/salt-*.egg-info/*
|
||||||
%exclude %{python3_sitelib}/salt/cloud/deploy/*.sh
|
%exclude %{python3_sitelib}/salt/cloud/deploy/*.sh
|
||||||
|
|
||||||
%if %{with docs}
|
%if %{with docs}
|
||||||
@ -1401,10 +1464,8 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version
|
|||||||
%doc doc/_build/html
|
%doc doc/_build/html
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%files tests
|
%files -n python3-salt-testsuite
|
||||||
%dir %{_datadir}/salt/
|
%{python3_sitelib}/salt-testsuite
|
||||||
%dir %{_datadir}/salt/tests/
|
|
||||||
%{_datadir}/salt/tests/*
|
|
||||||
|
|
||||||
%if %{with bash_completion}
|
%if %{with bash_completion}
|
||||||
%files bash-completion
|
%files bash-completion
|
||||||
|
169
update-__pillar__-during-pillar_refresh.patch
Normal file
169
update-__pillar__-during-pillar_refresh.patch
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
From 3e7c5d95423491f83d0016eb7c02285cd0b1bcf4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marek Czernek <marek.czernek@suse.com>
|
||||||
|
Date: Wed, 17 Jan 2024 15:39:41 +0100
|
||||||
|
Subject: [PATCH] Update __pillar__ during pillar_refresh
|
||||||
|
|
||||||
|
---
|
||||||
|
changelog/63583.fixed.md | 1 +
|
||||||
|
salt/minion.py | 1 +
|
||||||
|
.../integration/modules/test_pillar.py | 110 +++++++++++++++++-
|
||||||
|
3 files changed, 111 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 changelog/63583.fixed.md
|
||||||
|
|
||||||
|
diff --git a/changelog/63583.fixed.md b/changelog/63583.fixed.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..f1b6e32507
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/changelog/63583.fixed.md
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Need to make sure we update __pillar__ during a pillar refresh to ensure that process_beacons has the updated beacons loaded from pillar.
|
||||||
|
diff --git a/salt/minion.py b/salt/minion.py
|
||||||
|
index 9597d6e63a..4db0d31bd4 100644
|
||||||
|
--- a/salt/minion.py
|
||||||
|
+++ b/salt/minion.py
|
||||||
|
@@ -2498,6 +2498,7 @@ class Minion(MinionBase):
|
||||||
|
current_schedule, new_schedule
|
||||||
|
)
|
||||||
|
self.opts["pillar"] = new_pillar
|
||||||
|
+ self.functions.pack["__pillar__"] = self.opts["pillar"]
|
||||||
|
finally:
|
||||||
|
async_pillar.destroy()
|
||||||
|
self.matchers_refresh()
|
||||||
|
diff --git a/tests/pytests/integration/modules/test_pillar.py b/tests/pytests/integration/modules/test_pillar.py
|
||||||
|
index 66f7b9e47b..5db9a1630a 100644
|
||||||
|
--- a/tests/pytests/integration/modules/test_pillar.py
|
||||||
|
+++ b/tests/pytests/integration/modules/test_pillar.py
|
||||||
|
@@ -1,9 +1,14 @@
|
||||||
|
+import logging
|
||||||
|
import pathlib
|
||||||
|
import time
|
||||||
|
+import types
|
||||||
|
|
||||||
|
import attr
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
+log = logging.getLogger(__name__)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
pytestmark = [
|
||||||
|
pytest.mark.slow_test,
|
||||||
|
pytest.mark.windows_whitelisted,
|
||||||
|
@@ -210,7 +215,7 @@ class PillarRefresh:
|
||||||
|
"top.sls", top_file_contents
|
||||||
|
)
|
||||||
|
self.minion_1_pillar = self.master.pillar_tree.base.temp_file(
|
||||||
|
- "minion-1-pillar.sls", "{}: true".format(self.pillar_key)
|
||||||
|
+ "minion-1-pillar.sls", f"{self.pillar_key}: true"
|
||||||
|
)
|
||||||
|
self.top_file.__enter__()
|
||||||
|
self.minion_1_pillar.__enter__()
|
||||||
|
@@ -588,3 +593,106 @@ def test_pillar_ext_59975(salt_call_cli):
|
||||||
|
"""
|
||||||
|
ret = salt_call_cli.run("pillar.ext", '{"libvert": _}')
|
||||||
|
assert "ext_pillar_opts" in ret.data
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@pytest.fixture
|
||||||
|
+def event_listerner_timeout(grains):
|
||||||
|
+ if grains["os"] == "Windows":
|
||||||
|
+ if grains["osrelease"].startswith("2019"):
|
||||||
|
+ return types.SimpleNamespace(catch=120, miss=30)
|
||||||
|
+ return types.SimpleNamespace(catch=90, miss=10)
|
||||||
|
+ return types.SimpleNamespace(catch=60, miss=10)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@pytest.mark.slow_test
|
||||||
|
+def test_pillar_refresh_pillar_beacons(
|
||||||
|
+ base_env_pillar_tree_root_dir,
|
||||||
|
+ salt_cli,
|
||||||
|
+ salt_minion,
|
||||||
|
+ salt_master,
|
||||||
|
+ event_listener,
|
||||||
|
+ event_listerner_timeout,
|
||||||
|
+):
|
||||||
|
+ """
|
||||||
|
+ Ensure beacons jobs in pillar are started after
|
||||||
|
+ a pillar refresh and then not running when pillar
|
||||||
|
+ is cleared.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ top_sls = """
|
||||||
|
+ base:
|
||||||
|
+ '{}':
|
||||||
|
+ - test_beacons
|
||||||
|
+ """.format(
|
||||||
|
+ salt_minion.id
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ test_beacons_sls_empty = ""
|
||||||
|
+
|
||||||
|
+ test_beacons_sls = """
|
||||||
|
+ beacons:
|
||||||
|
+ status:
|
||||||
|
+ - loadavg:
|
||||||
|
+ - 1-min
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ assert salt_minion.is_running()
|
||||||
|
+
|
||||||
|
+ top_tempfile = pytest.helpers.temp_file(
|
||||||
|
+ "top.sls", top_sls, base_env_pillar_tree_root_dir
|
||||||
|
+ )
|
||||||
|
+ beacon_tempfile = pytest.helpers.temp_file(
|
||||||
|
+ "test_beacons.sls", test_beacons_sls_empty, base_env_pillar_tree_root_dir
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ with top_tempfile, beacon_tempfile:
|
||||||
|
+ # Calling refresh_pillar to update in-memory pillars
|
||||||
|
+ salt_cli.run("saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id)
|
||||||
|
+
|
||||||
|
+ # Ensure beacons start when pillar is refreshed
|
||||||
|
+ with salt_master.pillar_tree.base.temp_file(
|
||||||
|
+ "test_beacons.sls", test_beacons_sls
|
||||||
|
+ ):
|
||||||
|
+ # Calling refresh_pillar to update in-memory pillars
|
||||||
|
+ salt_cli.run(
|
||||||
|
+ "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ # Give the beacons a chance to start
|
||||||
|
+ time.sleep(5)
|
||||||
|
+
|
||||||
|
+ event_tag = f"salt/beacon/*/status/*"
|
||||||
|
+ start_time = time.time()
|
||||||
|
+
|
||||||
|
+ event_pattern = (salt_master.id, event_tag)
|
||||||
|
+ matched_events = event_listener.wait_for_events(
|
||||||
|
+ [event_pattern],
|
||||||
|
+ after_time=start_time,
|
||||||
|
+ timeout=event_listerner_timeout.catch,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ assert matched_events.found_all_events
|
||||||
|
+
|
||||||
|
+ # Ensure beacons sttop when pillar is refreshed
|
||||||
|
+ with salt_master.pillar_tree.base.temp_file(
|
||||||
|
+ "test_beacons.sls", test_beacons_sls_empty
|
||||||
|
+ ):
|
||||||
|
+ # Calling refresh_pillar to update in-memory pillars
|
||||||
|
+ salt_cli.run(
|
||||||
|
+ "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ # Give the beacons a chance to stop
|
||||||
|
+ time.sleep(5)
|
||||||
|
+
|
||||||
|
+ event_tag = f"salt/beacon/*/status/*"
|
||||||
|
+ start_time = time.time()
|
||||||
|
+
|
||||||
|
+ event_pattern = (salt_master.id, event_tag)
|
||||||
|
+ matched_events = event_listener.wait_for_events(
|
||||||
|
+ [event_pattern],
|
||||||
|
+ after_time=start_time,
|
||||||
|
+ timeout=event_listerner_timeout.miss,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ assert not matched_events.found_all_events
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
103
use-salt-call-from-salt-bundle-with-transactional_up.patch
Normal file
103
use-salt-call-from-salt-bundle-with-transactional_up.patch
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
From 0459d3f711eb9898f56a97d0bf0eb66fd1421a56 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Zhestkov <vzhestkov@suse.com>
|
||||||
|
Date: Mon, 2 Oct 2023 13:25:52 +0200
|
||||||
|
Subject: [PATCH] Use salt-call from salt bundle with
|
||||||
|
transactional_update
|
||||||
|
|
||||||
|
* Use salt-call from the bundle with transactional_update
|
||||||
|
|
||||||
|
* Add test checking which salt-call is selected by executable
|
||||||
|
---
|
||||||
|
salt/modules/transactional_update.py | 13 +++++-
|
||||||
|
.../unit/modules/test_transactional_update.py | 44 +++++++++++++++++++
|
||||||
|
2 files changed, 56 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/salt/modules/transactional_update.py b/salt/modules/transactional_update.py
|
||||||
|
index 658ebccc6b..d6915475f5 100644
|
||||||
|
--- a/salt/modules/transactional_update.py
|
||||||
|
+++ b/salt/modules/transactional_update.py
|
||||||
|
@@ -276,6 +276,9 @@ transaction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
+import os.path
|
||||||
|
+import pathlib
|
||||||
|
+import sys
|
||||||
|
|
||||||
|
import salt.client.ssh.state
|
||||||
|
import salt.client.ssh.wrapper.state
|
||||||
|
@@ -941,10 +944,18 @@ def call(function, *args, **kwargs):
|
||||||
|
activate_transaction = kwargs.pop("activate_transaction", False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
+ # Set default salt-call command
|
||||||
|
+ salt_call_cmd = "salt-call"
|
||||||
|
+ python_exec_dir = os.path.dirname(sys.executable)
|
||||||
|
+ if "venv-salt-minion" in pathlib.Path(python_exec_dir).parts:
|
||||||
|
+ # If the module is executed with the Salt Bundle,
|
||||||
|
+ # use salt-call from the Salt Bundle
|
||||||
|
+ salt_call_cmd = os.path.join(python_exec_dir, "salt-call")
|
||||||
|
+
|
||||||
|
safe_kwargs = salt.utils.args.clean_kwargs(**kwargs)
|
||||||
|
salt_argv = (
|
||||||
|
[
|
||||||
|
- "salt-call",
|
||||||
|
+ salt_call_cmd,
|
||||||
|
"--out",
|
||||||
|
"json",
|
||||||
|
"-l",
|
||||||
|
diff --git a/tests/pytests/unit/modules/test_transactional_update.py b/tests/pytests/unit/modules/test_transactional_update.py
|
||||||
|
index 5d9294c49b..dbd72fd74b 100644
|
||||||
|
--- a/tests/pytests/unit/modules/test_transactional_update.py
|
||||||
|
+++ b/tests/pytests/unit/modules/test_transactional_update.py
|
||||||
|
@@ -670,3 +670,47 @@ def test_single_queue_true():
|
||||||
|
"salt.modules.transactional_update.call", MagicMock(return_value="result")
|
||||||
|
):
|
||||||
|
assert tu.single("pkg.installed", name="emacs", queue=True) == "result"
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@pytest.mark.parametrize(
|
||||||
|
+ "executable,salt_call_cmd",
|
||||||
|
+ [
|
||||||
|
+ ("/usr/bin/python3", "salt-call"),
|
||||||
|
+ (
|
||||||
|
+ "/usr/lib/venv-salt-minion/bin/python",
|
||||||
|
+ "/usr/lib/venv-salt-minion/bin/salt-call",
|
||||||
|
+ ),
|
||||||
|
+ ],
|
||||||
|
+)
|
||||||
|
+def test_call_which_salt_call_selected_with_executable(executable, salt_call_cmd):
|
||||||
|
+ """Test transactional_update.chroot which salt-call used"""
|
||||||
|
+ utils_mock = {
|
||||||
|
+ "json.find_json": MagicMock(return_value={"return": "result"}),
|
||||||
|
+ }
|
||||||
|
+ salt_mock = {
|
||||||
|
+ "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
|
||||||
|
+ }
|
||||||
|
+ with patch("sys.executable", executable), patch.dict(
|
||||||
|
+ tu.__utils__, utils_mock
|
||||||
|
+ ), patch.dict(tu.__salt__, salt_mock):
|
||||||
|
+ assert tu.call("test.ping") == "result"
|
||||||
|
+
|
||||||
|
+ salt_mock["cmd.run_all"].assert_called_with(
|
||||||
|
+ [
|
||||||
|
+ "transactional-update",
|
||||||
|
+ "--non-interactive",
|
||||||
|
+ "--drop-if-no-change",
|
||||||
|
+ "--no-selfupdate",
|
||||||
|
+ "--continue",
|
||||||
|
+ "--quiet",
|
||||||
|
+ "run",
|
||||||
|
+ salt_call_cmd,
|
||||||
|
+ "--out",
|
||||||
|
+ "json",
|
||||||
|
+ "-l",
|
||||||
|
+ "quiet",
|
||||||
|
+ "--no-return-event",
|
||||||
|
+ "--",
|
||||||
|
+ "test.ping",
|
||||||
|
+ ]
|
||||||
|
+ )
|
||||||
|
--
|
||||||
|
2.42.0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user