diff --git a/_lastrevision b/_lastrevision
index 1cf8c6b..5c1d88e 100644
--- a/_lastrevision
+++ b/_lastrevision
@@ -1 +1 @@
-597049dd3d38cffb3d6e555ded591bc36ed09a58
\ No newline at end of file
+cc4e56e0465b20664e2f24bfe7034e5fee37232f
\ No newline at end of file
diff --git a/_service b/_service
index 792f133..dbfc8cf 100644
--- a/_service
+++ b/_service
@@ -3,7 +3,7 @@
https://github.com/openSUSE/salt-packaging.git
salt
package
- MU/5.0.1
+ MU/5.0.2
git
diff --git a/allow-namedloadercontexts-to-be-returned-from-loader.patch b/allow-namedloadercontexts-to-be-returned-from-loader.patch
new file mode 100644
index 0000000..ca9720c
--- /dev/null
+++ b/allow-namedloadercontexts-to-be-returned-from-loader.patch
@@ -0,0 +1,73 @@
+From 1be3f92ef3bf14e47340e2e075291204b3e75e98 Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Wed, 25 Sep 2024 14:07:42 +0300
+Subject: [PATCH] Allow NamedLoaderContexts to be returned from loader
+
+It is useful in some cases to return NamedLoaderContexts from loaded
+functions. Instead of choking or requireing implimenters to call the
+context's value() method before being de-scoped, detect when a
+NamedLoaderContext has been returned and return the value from the
+current context.
+
+Co-authored-by: Daniel A. Wozniak
+---
+ salt/loader/lazy.py | 5 ++++-
+ tests/pytests/integration/modules/test_config.py | 8 ++++++++
+ tests/pytests/unit/loader/test_loader.py | 13 +++++++++++++
+ 3 files changed, 25 insertions(+), 1 deletion(-)
+ create mode 100644 tests/pytests/integration/modules/test_config.py
+
+diff --git a/salt/loader/lazy.py b/salt/loader/lazy.py
+index 5de995d446..b7fd97f0e1 100644
+--- a/salt/loader/lazy.py
++++ b/salt/loader/lazy.py
+@@ -1246,7 +1246,10 @@ class LazyLoader(salt.utils.lazy.LazyDict):
+ self.parent_loader = current_loader
+ token = salt.loader.context.loader_ctxvar.set(self)
+ try:
+- return _func_or_method(*args, **kwargs)
++ ret = _func_or_method(*args, **kwargs)
++ if isinstance(ret, salt.loader.context.NamedLoaderContext):
++ ret = ret.value()
++ return ret
+ finally:
+ self.parent_loader = None
+ salt.loader.context.loader_ctxvar.reset(token)
+diff --git a/tests/pytests/integration/modules/test_config.py b/tests/pytests/integration/modules/test_config.py
+new file mode 100644
+index 0000000000..afdf470605
+--- /dev/null
++++ b/tests/pytests/integration/modules/test_config.py
+@@ -0,0 +1,8 @@
++import pytest
++
++
++@pytest.mark.slow_test
++def test_config_items(salt_cli, salt_minion):
++ ret = salt_cli.run("config.items", minion_tgt=salt_minion.id)
++ assert ret.returncode == 0
++ assert isinstance(ret.data, dict)
+diff --git a/tests/pytests/unit/loader/test_loader.py b/tests/pytests/unit/loader/test_loader.py
+index 86348749db..aba605f42a 100644
+--- a/tests/pytests/unit/loader/test_loader.py
++++ b/tests/pytests/unit/loader/test_loader.py
+@@ -62,3 +62,16 @@ def test_raw_mod_functions():
+ ret = salt.loader.raw_mod(opts, "grains", "get")
+ for k, v in ret.items():
+ assert isinstance(v, salt.loader.lazy.LoadedFunc)
++
++
++def test_return_named_context_from_loaded_func(tmp_path):
++ opts = {
++ "optimization_order": [0],
++ }
++ contents = """
++ def foobar():
++ return __test__
++ """
++ with pytest.helpers.temp_file("mymod.py", contents, directory=tmp_path):
++ loader = salt.loader.LazyLoader([tmp_path], opts, pack={"__test__": "meh"})
++ assert loader["mymod.foobar"]() == "meh"
+--
+2.46.1
+
diff --git a/avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch b/avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch
new file mode 100644
index 0000000..f726aba
--- /dev/null
+++ b/avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch
@@ -0,0 +1,153 @@
+From b2faa019f0f5aa03b03e6c54c9aa60b7f6aa4f91 Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Fri, 30 Aug 2024 14:35:33 +0200
+Subject: [PATCH] Avoid crash on wrong output of systemctl version
+ (bsc#1229539)
+
+* Better handling output of systemctl --version
+
+* Add more cases to test grains.core._systemd
+---
+ salt/grains/core.py | 27 +++++++-
+ tests/pytests/unit/grains/test_core.py | 89 ++++++++++++++++++++++++++
+ 2 files changed, 113 insertions(+), 3 deletions(-)
+
+diff --git a/salt/grains/core.py b/salt/grains/core.py
+index 4454c303fe..98bbd3868e 100644
+--- a/salt/grains/core.py
++++ b/salt/grains/core.py
+@@ -2432,10 +2432,31 @@ def _systemd():
+ """
+ Return the systemd grain
+ """
+- systemd_info = __salt__["cmd.run"]("systemctl --version").splitlines()
++ systemd_version = "UNDEFINED"
++ systemd_features = ""
++ try:
++ systemd_output = __salt__["cmd.run_all"]("systemctl --version")
++ except Exception: # pylint: disable=broad-except
++ log.error("Exception while executing `systemctl --version`", exc_info=True)
++ return {
++ "version": systemd_version,
++ "features": systemd_features,
++ }
++ if systemd_output.get("retcode") == 0:
++ systemd_info = systemd_output.get("stdout", "").splitlines()
++ try:
++ if systemd_info[0].startswith("systemd "):
++ systemd_version = systemd_info[0].split()[1]
++ systemd_features = systemd_info[1]
++ except IndexError:
++ pass
++ if systemd_version == "UNDEFINED" or systemd_features == "":
++ log.error(
++ "Unexpected output returned by `systemctl --version`: %s", systemd_output
++ )
+ return {
+- "version": systemd_info[0].split()[1],
+- "features": systemd_info[1],
++ "version": systemd_version,
++ "features": systemd_features,
+ }
+
+
+diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py
+index 36545287b9..b64b8c4bf8 100644
+--- a/tests/pytests/unit/grains/test_core.py
++++ b/tests/pytests/unit/grains/test_core.py
+@@ -3593,3 +3593,92 @@ def test_virtual_set_virtual_ec2():
+
+ assert virtual_grains["virtual"] == "Nitro"
+ assert virtual_grains["virtual_subtype"] == "Amazon EC2"
++
++
++@pytest.mark.parametrize(
++ "systemd_data,expected",
++ (
++ (
++ {
++ "pid": 1234,
++ "retcode": 0,
++ "stdout": "systemd 254 (254.3-1)\n+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK "
++ "+SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS "
++ "+FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 "
++ "-PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD "
++ "+BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified",
++ "stderr": "",
++ },
++ {
++ "version": "254",
++ "features": "+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL "
++ "+ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP "
++ "+LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +TPM2 +BZIP2 +LZ4 +XZ "
++ "+ZLIB +ZSTD +BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified",
++ },
++ ),
++ (
++ {
++ "pid": 2345,
++ "retcode": 1,
++ "stdout": "",
++ "stderr": "some garbage in the output",
++ },
++ {
++ "version": "UNDEFINED",
++ "features": "",
++ },
++ ),
++ (
++ {
++ "pid": 3456,
++ "retcode": 0,
++ "stdout": "unexpected stdout\none more line",
++ "stderr": "",
++ },
++ {
++ "version": "UNDEFINED",
++ "features": "",
++ },
++ ),
++ (
++ {
++ "pid": 4567,
++ "retcode": 0,
++ "stdout": "",
++ "stderr": "",
++ },
++ {
++ "version": "UNDEFINED",
++ "features": "",
++ },
++ ),
++ (
++ Exception("Some exception on calling `systemctl --version`"),
++ {
++ "version": "UNDEFINED",
++ "features": "",
++ },
++ ),
++ ),
++)
++def test__systemd(systemd_data, expected):
++ """
++ test _systemd
++ """
++
++ def mock_run_all_systemd(_):
++ if isinstance(systemd_data, Exception):
++ raise systemd_data
++ return systemd_data
++
++ with patch.dict(
++ core.__salt__,
++ {
++ "cmd.run_all": mock_run_all_systemd,
++ },
++ ):
++ ret = core._systemd()
++ assert "version" in ret
++ assert "features" in ret
++ assert ret == expected
+--
+2.46.0
+
diff --git a/avoid-explicit-reading-of-etc-salt-minion-bsc-122035.patch b/avoid-explicit-reading-of-etc-salt-minion-bsc-122035.patch
new file mode 100644
index 0000000..fba9803
--- /dev/null
+++ b/avoid-explicit-reading-of-etc-salt-minion-bsc-122035.patch
@@ -0,0 +1,27 @@
+From bbdb56932845dceb47332a4c967c13a9a78b88bc Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Wed, 25 Sep 2024 14:08:20 +0300
+Subject: [PATCH] Avoid explicit reading of /etc/salt/minion
+ (bsc#1220357)
+
+Co-authored-by: Daniel A. Wozniak
+---
+ salt/utils/azurearm.py | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/salt/utils/azurearm.py b/salt/utils/azurearm.py
+index 276cbb66b3..9ae128273c 100644
+--- a/salt/utils/azurearm.py
++++ b/salt/utils/azurearm.py
+@@ -47,8 +47,6 @@ try:
+ except ImportError:
+ HAS_AZURE = False
+
+-__opts__ = salt.config.minion_config("/etc/salt/minion")
+-__salt__ = salt.loader.minion_mods(__opts__)
+
+ log = logging.getLogger(__name__)
+
+--
+2.46.1
+
diff --git a/fix-deprecated-code-677.patch b/fix-deprecated-code-677.patch
new file mode 100644
index 0000000..b64f339
--- /dev/null
+++ b/fix-deprecated-code-677.patch
@@ -0,0 +1,166 @@
+From d5f3df07783d8aaf3a897ca2f209e662973b930c Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Wed, 4 Sep 2024 13:11:33 +0200
+Subject: [PATCH] Fix deprecated code (#677)
+
+Due to SUSE's extended support policy, we won't remove
+code from Salt until next major release.
+---
+ salt/_logging/handlers.py | 6 +++---
+ salt/log/__init__.py | 2 +-
+ salt/log/handlers/__init__.py | 2 +-
+ salt/log/mixins.py | 2 +-
+ salt/log/setup.py | 4 ++--
+ salt/modules/aptpkg.py | 2 +-
+ salt/modules/cassandra_mod.py | 2 +-
+ salt/returners/cassandra_return.py | 2 +-
+ salt/returners/django_return.py | 2 +-
+ 9 files changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/salt/_logging/handlers.py b/salt/_logging/handlers.py
+index f4b0b6fec3d..5a1a1613137 100644
+--- a/salt/_logging/handlers.py
++++ b/salt/_logging/handlers.py
+@@ -36,7 +36,7 @@ class TemporaryLoggingHandler(logging.NullHandler):
+
+ def __init__(self, level=logging.NOTSET, max_queue_size=10000):
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}.TemporaryLoggingHandler'. "
+ "'{name}.TemporaryLoggingHandler' will go away after "
+ "{{date}}.".format(name=__name__),
+@@ -225,7 +225,7 @@ if sys.version_info < (3, 7):
+ def __init__(self, queue): # pylint: disable=useless-super-delegation
+ super().__init__(queue)
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}.QueueHandler' and instead "
+ "use 'logging.handlers.QueueHandler'. "
+ "'{name}.QueueHandler' will go away after "
+@@ -283,7 +283,7 @@ else:
+ def __init__(self, queue): # pylint: disable=useless-super-delegation
+ super().__init__(queue)
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}.QueueHandler' and instead "
+ "use 'logging.handlers.QueueHandler'. "
+ "'{name}.QueueHandler' will go away after "
+diff --git a/salt/log/__init__.py b/salt/log/__init__.py
+index 3458474f2ca..69bfa8ed15b 100644
+--- a/salt/log/__init__.py
++++ b/salt/log/__init__.py
+@@ -24,7 +24,7 @@ from salt.log.setup import (
+ from salt.utils.versions import warn_until_date
+
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}' and instead use 'salt._logging'. "
+ "'{name}' will go away after {{date}}.".format(name=__name__),
+ stacklevel=3,
+diff --git a/salt/log/handlers/__init__.py b/salt/log/handlers/__init__.py
+index 8bc740e20f1..55cf10cdb78 100644
+--- a/salt/log/handlers/__init__.py
++++ b/salt/log/handlers/__init__.py
+@@ -12,7 +12,7 @@ from salt._logging.handlers import (
+ from salt.utils.versions import warn_until_date
+
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}' and instead use 'salt._logging.handlers'. "
+ "'{name}' will go away after {{date}}.".format(name=__name__),
+ )
+diff --git a/salt/log/mixins.py b/salt/log/mixins.py
+index 6619b564198..65f5ed7f78a 100644
+--- a/salt/log/mixins.py
++++ b/salt/log/mixins.py
+@@ -11,7 +11,7 @@ from salt.utils.versions import warn_until_date
+ # pylint: enable=unused-import
+
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}' and instead use 'salt._logging.mixins'. "
+ "'{name}' will go away after {{date}}.".format(name=__name__),
+ )
+diff --git a/salt/log/setup.py b/salt/log/setup.py
+index 74bd7bbd3e1..f4c80b0f280 100644
+--- a/salt/log/setup.py
++++ b/salt/log/setup.py
+@@ -21,7 +21,7 @@ from salt._logging.impl import set_log_record_factory as setLogRecordFactory
+ from salt.utils.versions import warn_until_date
+
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using '{name}' and instead use 'salt._logging'. "
+ "'{name}' will go away after {{date}}. Do note however that "
+ "'salt._logging' is now considered a non public implementation "
+@@ -34,7 +34,7 @@ def _deprecated_warning(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "Please stop using 'salt.log.setup.{name}()' as it no longer does anything and "
+ "will go away after {{date}}.".format(name=func.__qualname__),
+ stacklevel=4,
+diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py
+index ad5450c4151..cd40aea54f1 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(
+- "20250101",
++ "20260101",
+ "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 "
+diff --git a/salt/modules/cassandra_mod.py b/salt/modules/cassandra_mod.py
+index 029fd08fb9b..db9c8821920 100644
+--- a/salt/modules/cassandra_mod.py
++++ b/salt/modules/cassandra_mod.py
+@@ -45,7 +45,7 @@ def __virtual__():
+ )
+
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "The cassandra returner is broken and deprecated, and will be removed"
+ " after {date}. Use the cassandra_cql returner instead",
+ )
+diff --git a/salt/returners/cassandra_return.py b/salt/returners/cassandra_return.py
+index ac01a4e46cb..5fcc00ee8ce 100644
+--- a/salt/returners/cassandra_return.py
++++ b/salt/returners/cassandra_return.py
+@@ -53,7 +53,7 @@ def __virtual__():
+ if not HAS_PYCASSA:
+ return False, "Could not import cassandra returner; pycassa is not installed."
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "The cassandra returner is broken and deprecated, and will be removed"
+ " after {date}. Use the cassandra_cql returner instead",
+ )
+diff --git a/salt/returners/django_return.py b/salt/returners/django_return.py
+index 36386875552..474653f3831 100644
+--- a/salt/returners/django_return.py
++++ b/salt/returners/django_return.py
+@@ -57,7 +57,7 @@ __virtualname__ = "django"
+
+ def __virtual__():
+ warn_until_date(
+- "20240101",
++ "20260101",
+ "The django returner is broken and deprecated, and will be removed"
+ " after {date}.",
+ )
+--
+2.46.0
+
diff --git a/fix-test_debian-to-work-in-our-infrastructure-676.patch b/fix-test_debian-to-work-in-our-infrastructure-676.patch
new file mode 100644
index 0000000..d639188
--- /dev/null
+++ b/fix-test_debian-to-work-in-our-infrastructure-676.patch
@@ -0,0 +1,25 @@
+From a6d27a6f50bbbea539ec64bf96a5b9755e32bf69 Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Wed, 4 Sep 2024 13:11:05 +0200
+Subject: [PATCH] Fix test_debian to work in our infrastructure (#676)
+
+---
+ tests/pytests/functional/states/pkgrepo/test_debian.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/pytests/functional/states/pkgrepo/test_debian.py b/tests/pytests/functional/states/pkgrepo/test_debian.py
+index 87716706d5e..7bda100b634 100644
+--- a/tests/pytests/functional/states/pkgrepo/test_debian.py
++++ b/tests/pytests/functional/states/pkgrepo/test_debian.py
+@@ -205,7 +205,7 @@ def ubuntu_state_tree(system_aptsources, state_tree, grains):
+ - dist: {{ codename }}
+ - file: /etc/apt/sources.list.d/firefox-beta.list
+ - keyid: CE49EC21
+- - keyserver: keyserver.ubuntu.com
++ - keyserver: hkp://keyserver.ubuntu.com:80
+ {%- endif %}
+
+ {%- if backports %}{%- do ubuntu_repos.append('kubuntu-ppa') %}
+--
+2.46.0
+
diff --git a/fix-test_system-flaky-setup_teardown-fn.patch b/fix-test_system-flaky-setup_teardown-fn.patch
new file mode 100644
index 0000000..b3cbcd0
--- /dev/null
+++ b/fix-test_system-flaky-setup_teardown-fn.patch
@@ -0,0 +1,44 @@
+From 5567f2bd51d66b7797c986cf64f79f71ca57eb63 Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Wed, 4 Sep 2024 13:10:44 +0200
+Subject: [PATCH] Fix test_system flaky setup_teardown fn
+
+---
+ tests/pytests/functional/modules/test_system.py | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/tests/pytests/functional/modules/test_system.py b/tests/pytests/functional/modules/test_system.py
+index 2cd03a3a3e4..270aafbe2cd 100644
+--- a/tests/pytests/functional/modules/test_system.py
++++ b/tests/pytests/functional/modules/test_system.py
+@@ -4,10 +4,12 @@ import os
+ import signal
+ import subprocess
+ import textwrap
++import time
+
+ import pytest
+
+ import salt.utils.files
++from salt.exceptions import CommandExecutionError
+
+ INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container"
+
+@@ -80,7 +82,13 @@ def setup_teardown_vars(file, service, system):
+ file.remove("/etc/machine-info")
+
+ if _systemd_timesyncd_available_:
+- res = service.start("systemd-timesyncd")
++ try:
++ res = service.start("systemd-timesyncd")
++ except CommandExecutionError:
++ # We possibly did too many restarts in too short time
++ # Wait 10s (default systemd timeout) and try again
++ time.sleep(10)
++ res = service.start("systemd-timesyncd")
+ assert res
+
+
+--
+2.46.0
+
diff --git a/fix-the-selinux-context-for-salt-minion-service-bsc-.patch b/fix-the-selinux-context-for-salt-minion-service-bsc-.patch
new file mode 100644
index 0000000..a4281ff
--- /dev/null
+++ b/fix-the-selinux-context-for-salt-minion-service-bsc-.patch
@@ -0,0 +1,83 @@
+From d933c8f0795fdada84a01a2cc754586fa720993d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
+
+Date: Tue, 10 Sep 2024 13:46:09 +0100
+Subject: [PATCH] Fix the SELinux context for Salt Minion service
+ (bsc#1219041) (#670)
+
+Currently there are no SELinux policies for Salt.
+
+By default, the Salt Minion service runs as 'unconfined_service_t' when
+SELinux is enabled. This works fine in most cases but generates a problem
+then trying to transition to an 'unconfined_t', i.a. when running
+"cmd.run .... runas=nobody". Then we see this denied in audit logs:
+
+type=AVC msg=audit(1722870119.142:718): avc: denied { transition } for pid=3421 comm="su" path="/usr/bin/bash" dev="vda3" ino=28565 scontext=system_u:system_r:unconfined_service_t:s0 tcontext=unconfined_u:unconfined_r:unconfined_t:s0 tclass=process permissive=0
+
+(This happens for cmd.run at the time of trying to invoke a shell as a
+different user to gather the environment variables from this particular
+user)
+
+Fixing the SELinuxContext for the Salt Minion systemd service to a
+general 'unconfined_t' workarounds this situation.
+
+SELinuxContext attribute was added on systemd version 209.
+---
+ pkg/common/salt-minion.service | 1 +
+ pkg/old/deb/salt-minion.service | 1 +
+ pkg/old/suse/salt-minion.service | 1 +
+ pkg/old/suse/salt-minion.service.rhel7 | 1 +
+ 4 files changed, 4 insertions(+)
+
+diff --git a/pkg/common/salt-minion.service b/pkg/common/salt-minion.service
+index 69aff18c583..696d0263c39 100644
+--- a/pkg/common/salt-minion.service
++++ b/pkg/common/salt-minion.service
+@@ -9,6 +9,7 @@ Type=notify
+ NotifyAccess=all
+ LimitNOFILE=8192
+ ExecStart=/usr/bin/salt-minion
++SELinuxContext=system_u:system_r:unconfined_t:s0
+
+ [Install]
+ WantedBy=multi-user.target
+diff --git a/pkg/old/deb/salt-minion.service b/pkg/old/deb/salt-minion.service
+index 7e6cf146549..b0ad82c1334 100644
+--- a/pkg/old/deb/salt-minion.service
++++ b/pkg/old/deb/salt-minion.service
+@@ -8,6 +8,7 @@ KillMode=process
+ NotifyAccess=all
+ LimitNOFILE=8192
+ ExecStart=/usr/bin/salt-minion
++SELinuxContext=system_u:system_r:unconfined_t:s0
+
+ [Install]
+ WantedBy=multi-user.target
+diff --git a/pkg/old/suse/salt-minion.service b/pkg/old/suse/salt-minion.service
+index 12f28314cb1..b99ef063522 100644
+--- a/pkg/old/suse/salt-minion.service
++++ b/pkg/old/suse/salt-minion.service
+@@ -10,6 +10,7 @@ ExecStart=/usr/bin/salt-minion
+ KillMode=process
+ Restart=on-failure
+ RestartSec=15
++SELinuxContext=system_u:system_r:unconfined_t:s0
+
+ [Install]
+ WantedBy=multi-user.target
+diff --git a/pkg/old/suse/salt-minion.service.rhel7 b/pkg/old/suse/salt-minion.service.rhel7
+index 69172677140..92cc66d32f4 100644
+--- a/pkg/old/suse/salt-minion.service.rhel7
++++ b/pkg/old/suse/salt-minion.service.rhel7
+@@ -9,6 +9,7 @@ ExecStart=/usr/bin/salt-minion
+ KillMode=process
+ Restart=on-failure
+ RestartSec=15
++SELinuxContext=system_u:system_r:unconfined_t:s0
+
+ [Install]
+ WantedBy=multi-user.target
+--
+2.46.0
+
+
diff --git a/fix-x509-test-fails-on-old-openssl-systems-682.patch b/fix-x509-test-fails-on-old-openssl-systems-682.patch
new file mode 100644
index 0000000..7e6ae6c
--- /dev/null
+++ b/fix-x509-test-fails-on-old-openssl-systems-682.patch
@@ -0,0 +1,261 @@
+From 7daf461528c90776b8f865cd58d20e23bd5b6f3f Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Wed, 2 Oct 2024 09:09:34 +0200
+Subject: [PATCH] Fix x509 test fails on old openssl systems (#682)
+
+---
+ .../functional/modules/test_x509_v2.py | 41 +++++++++++++----
+ .../pytests/functional/states/test_x509_v2.py | 44 +++++++++++++++----
+ .../scenarios/performance/test_performance.py | 8 +++-
+ 3 files changed, 75 insertions(+), 18 deletions(-)
+
+diff --git a/tests/pytests/functional/modules/test_x509_v2.py b/tests/pytests/functional/modules/test_x509_v2.py
+index 2e8152d04a..7de8f3b01f 100644
+--- a/tests/pytests/functional/modules/test_x509_v2.py
++++ b/tests/pytests/functional/modules/test_x509_v2.py
+@@ -681,8 +681,13 @@ def test_create_certificate_self_signed(x509, algo, request):
+ privkey = request.getfixturevalue(f"{algo}_privkey")
+ try:
+ res = x509.create_certificate(signing_private_key=privkey, CN="success")
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ except salt.exceptions.CommandExecutionError as e:
++ if "Could not load PEM-encoded" in e.error:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ else:
++ raise e
+ assert res.startswith("-----BEGIN CERTIFICATE-----")
+ cert = _get_cert(res)
+ assert cert.subject.rfc4514_string() == "CN=success"
+@@ -754,8 +759,13 @@ def test_create_certificate_from_privkey(x509, ca_key, ca_cert, algo, request):
+ private_key=privkey,
+ CN="success",
+ )
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ except salt.exceptions.CommandExecutionError as e:
++ if "Could not load PEM-encoded" in e.error:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ else:
++ raise e
+ assert res.startswith("-----BEGIN CERTIFICATE-----")
+ cert = _get_cert(res)
+ assert cert.subject.rfc4514_string() == "CN=success"
+@@ -802,8 +812,13 @@ def test_create_certificate_from_pubkey(x509, ca_key, ca_cert, algo, request):
+ public_key=pubkey,
+ CN="success",
+ )
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ except salt.exceptions.CommandExecutionError as e:
++ if "Could not load PEM-encoded" in e.error:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ else:
++ raise e
+ assert res.startswith("-----BEGIN CERTIFICATE-----")
+ cert = _get_cert(res)
+ assert cert.subject.rfc4514_string() == "CN=success"
+@@ -1341,8 +1356,13 @@ def test_create_csr(x509, algo, request):
+ privkey = request.getfixturevalue(f"{algo}_privkey")
+ try:
+ res = x509.create_csr(private_key=privkey)
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ except salt.exceptions.CommandExecutionError as e:
++ if "Could not load PEM-encoded" in e.error:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ else:
++ raise e
+ assert res.startswith("-----BEGIN CERTIFICATE REQUEST-----")
+
+
+@@ -1402,7 +1422,7 @@ def test_create_csr_raw(x509, rsa_privkey):
+ def test_create_private_key(x509, algo):
+ try:
+ res = x509.create_private_key(algo=algo)
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ assert res.startswith("-----BEGIN PRIVATE KEY-----")
+
+@@ -1413,7 +1433,7 @@ def test_create_private_key_with_passphrase(x509, algo):
+ passphrase = "hunter2"
+ try:
+ res = x509.create_private_key(algo=algo, passphrase=passphrase)
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ assert res.startswith("-----BEGIN ENCRYPTED PRIVATE KEY-----")
+ # ensure it can be loaded
+@@ -1465,8 +1485,13 @@ def test_get_private_key_size(x509, algo, expected, request):
+ privkey = request.getfixturevalue(f"{algo}_privkey")
+ try:
+ res = x509.get_private_key_size(privkey)
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ except salt.exceptions.CommandExecutionError as e:
++ if "Could not load PEM-encoded" in e.error:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
++ else:
++ raise e
+ assert res == expected
+
+
+@@ -1612,7 +1637,7 @@ def test_verify_signature(x509, algo, request):
+ wrong_privkey = request.getfixturevalue(f"{algo}_privkey")
+ try:
+ privkey = x509.create_private_key(algo=algo)
+- except UnsupportedAlgorithm:
++ except (UnsupportedAlgorithm, NotImplementedError):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ cert = x509.create_certificate(signing_private_key=privkey)
+ assert x509.verify_signature(cert, privkey)
+diff --git a/tests/pytests/functional/states/test_x509_v2.py b/tests/pytests/functional/states/test_x509_v2.py
+index 47a1c555f8..139f7b1906 100644
+--- a/tests/pytests/functional/states/test_x509_v2.py
++++ b/tests/pytests/functional/states/test_x509_v2.py
+@@ -574,9 +574,9 @@ def existing_cert(x509, cert_args, ca_key, rsa_privkey, request):
+ ca_key,
+ encoding=cert_args.get("encoding", "pem"),
+ passphrase=cert_args.get("pkcs12_passphrase"),
+- subject=subject
+- if "signing_policy" not in cert_args
+- else "CN=from_signing_policy",
++ subject=(
++ subject if "signing_policy" not in cert_args else "CN=from_signing_policy"
++ ),
+ )
+ yield cert_args["name"]
+
+@@ -694,8 +694,12 @@ def existing_csr_exts(x509, csr_args, csr_args_exts, ca_key, rsa_privkey, reques
+ def existing_pk(x509, pk_args, request):
+ pk_args.update(request.param)
+ ret = x509.private_key_managed(**pk_args)
+- if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
+- pytest.skip(f"Algorithm '{pk_args['algo']}' is not supported on this OpenSSL version")
++ if ret.result == False and (
++ "UnsupportedAlgorithm" in ret.comment or "NotImplementedError" in ret.comment
++ ):
++ pytest.skip(
++ f"Algorithm '{pk_args['algo']}' is not supported on this OpenSSL version"
++ )
+ _assert_pk_basic(
+ ret,
+ pk_args.get("algo", "rsa"),
+@@ -1054,6 +1058,8 @@ def test_certificate_managed_days_valid_does_not_override_days_remaining(
+ def test_certificate_managed_privkey_change(x509, cert_args, ec_privkey, ca_key):
+ cert_args["private_key"] = ec_privkey
+ ret = x509.certificate_managed(**cert_args)
++ if ret.result == False and "NotImplementedError" in ret.comment:
++ pytest.skip("Current OpenSSL does not support 'ec' algorithm")
+ _assert_cert_basic(ret, cert_args["name"], ec_privkey, ca_key)
+ assert ret.changes["private_key"]
+
+@@ -1237,6 +1243,8 @@ def test_certificate_managed_wrong_ca_key(
+ cert_args["private_key"] = ec_privkey
+ cert_args["signing_private_key"] = rsa_privkey
+ ret = x509.certificate_managed(**cert_args)
++ if ret.result == False and "NotImplementedError" in ret.comment:
++ pytest.skip("Current OpenSSL does not support 'ec' algorithm")
+ assert ret.result is False
+ assert not ret.changes
+ assert "Signing private key does not match the certificate" in ret.comment
+@@ -1917,6 +1925,8 @@ def test_csr_managed_existing_invalid_version(x509, csr_args, rsa_privkey):
+ def test_csr_managed_privkey_change(x509, csr_args, ec_privkey):
+ csr_args["private_key"] = ec_privkey
+ ret = x509.csr_managed(**csr_args)
++ if ret.result == False and "NotImplementedError" in ret.comment:
++ pytest.skip("Current OpenSSL does not support 'ec' algorithm")
+ _assert_csr_basic(ret, ec_privkey)
+ assert ret.changes["private_key"]
+
+@@ -2141,11 +2151,14 @@ def test_private_key_managed(x509, pk_args, algo, encoding, passphrase):
+ pytest.skip(
+ "PKCS12 serialization of Edwards-curve keys requires cryptography v37"
+ )
++
+ pk_args["algo"] = algo
+ pk_args["encoding"] = encoding
+ pk_args["passphrase"] = passphrase
+ ret = x509.private_key_managed(**pk_args)
+- if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
++ if ret.result == False and (
++ "UnsupportedAlgorithm" in ret.comment or "NotImplementedError" in ret.comment
++ ):
+ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ _assert_pk_basic(ret, algo, encoding, passphrase)
+
+@@ -2155,6 +2168,8 @@ def test_private_key_managed_keysize(x509, pk_args, algo, keysize):
+ pk_args["algo"] = algo
+ pk_args["keysize"] = keysize
+ ret = x509.private_key_managed(**pk_args)
++ if ret.result == False and "NotImplementedError" in ret.comment:
++ pytest.skip("Current OpenSSL does not support 'ec' algorithm")
+ pk = _assert_pk_basic(ret, algo)
+ assert pk.key_size == keysize
+
+@@ -2174,8 +2189,12 @@ def test_private_key_managed_keysize(x509, pk_args, algo, keysize):
+ )
+ def test_private_key_managed_existing(x509, pk_args):
+ ret = x509.private_key_managed(**pk_args)
+- if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
+- pytest.skip(f"Algorithm '{pk_args['algo']}' is not supported on this OpenSSL version")
++ if ret.result == False and (
++ "UnsupportedAlgorithm" in ret.comment or "NotImplementedError" in ret.comment
++ ):
++ pytest.skip(
++ f"Algorithm '{pk_args['algo']}' is not supported on this OpenSSL version"
++ )
+ _assert_not_changed(ret)
+
+
+@@ -2382,6 +2401,8 @@ def test_private_key_managed_follow_symlinks_changes(
+ pk_args["encoding"] = encoding
+ pk_args["algo"] = "ec"
+ ret = x509.private_key_managed(**pk_args)
++ if ret.result == False and "NotImplementedError" in ret.comment:
++ pytest.skip("Current OpenSSL does not support 'ec' algorithm")
+ assert ret.changes
+ assert Path(ret.name).is_symlink() == follow
+
+@@ -2722,7 +2743,12 @@ def _get_cert(cert, encoding="pem", passphrase=None):
+ def _belongs_to(cert_or_pubkey, privkey):
+ if isinstance(cert_or_pubkey, cx509.Certificate):
+ cert_or_pubkey = cert_or_pubkey.public_key()
+- return x509util.is_pair(cert_or_pubkey, x509util.load_privkey(privkey))
++ try:
++ return x509util.is_pair(cert_or_pubkey, x509util.load_privkey(privkey))
++ except NotImplementedError:
++ pytest.skip(
++ "This OpenSSL version does not support current cryptographic algorithm"
++ )
+
+
+ def _signed_by(cert, privkey):
+diff --git a/tests/pytests/scenarios/performance/test_performance.py b/tests/pytests/scenarios/performance/test_performance.py
+index 85b92ed986..6319e26ce1 100644
+--- a/tests/pytests/scenarios/performance/test_performance.py
++++ b/tests/pytests/scenarios/performance/test_performance.py
+@@ -10,7 +10,13 @@ from saltfactories.utils import random_string
+
+ from salt.version import SaltVersionsInfo, __version__
+
+-pytestmark = [pytest.mark.skip_if_binaries_missing("docker")]
++pytestmark = [
++ pytest.mark.skip_if_binaries_missing("docker"),
++ pytest.mark.skipif(
++ os.environ.get("GITHUB_ACTIONS", "") == "true",
++ reason="Cannot spawn containers in GH actions run",
++ ),
++]
+
+
+ class ContainerMaster(SaltDaemon, master.SaltMaster):
+--
+2.46.1
+
diff --git a/improve-error-handling-with-different-openssl-versio.patch b/improve-error-handling-with-different-openssl-versio.patch
new file mode 100644
index 0000000..4d16812
--- /dev/null
+++ b/improve-error-handling-with-different-openssl-versio.patch
@@ -0,0 +1,98 @@
+From 4e226426d0897f2d9dc64891ced78487b181d40e Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Fri, 30 Aug 2024 14:33:51 +0200
+Subject: [PATCH] Improve error handling with different OpenSSL
+ versions
+
+* Make error checking of x509 more flexible
+
+for most recent cryptography and openSSL versions
+
+* Add test for different exception value on loading private key
+
+* Add fix for test_privkey_new_with_prereq on old OpenSSL
+---
+ salt/utils/x509.py | 3 +-
+ .../pytests/functional/states/test_x509_v2.py | 29 +++++++++++++++++++
+ .../integration/states/test_x509_v2.py | 7 +++++
+ 3 files changed, 38 insertions(+), 1 deletion(-)
+
+diff --git a/salt/utils/x509.py b/salt/utils/x509.py
+index 5b2ae15882..f9fdca64d9 100644
+--- a/salt/utils/x509.py
++++ b/salt/utils/x509.py
+@@ -695,7 +695,8 @@ def load_privkey(pk, passphrase=None, get_encoding=False):
+ return pk, "pem", None
+ return pk
+ except ValueError as err:
+- if "Bad decrypt" in str(err):
++ str_err = str(err)
++ if "Bad decrypt" in str_err or "Could not deserialize key data" in str_err:
+ raise SaltInvocationError(
+ "Bad decrypt - is the password correct?"
+ ) from err
+diff --git a/tests/pytests/functional/states/test_x509_v2.py b/tests/pytests/functional/states/test_x509_v2.py
+index 929be014cd..47a1c555f8 100644
+--- a/tests/pytests/functional/states/test_x509_v2.py
++++ b/tests/pytests/functional/states/test_x509_v2.py
+@@ -3,6 +3,8 @@ from pathlib import Path
+
+ import pytest
+
++from tests.support.mock import patch
++
+ try:
+ import cryptography
+ import cryptography.x509 as cx509
+@@ -2826,3 +2828,30 @@ def _get_privkey(pk, encoding="pem", passphrase=None):
+ pk = base64.b64decode(pk)
+ return pkcs12.load_pkcs12(pk, passphrase).key
+ raise ValueError("Need correct encoding")
++
++
++@pytest.mark.usefixtures("existing_pk")
++@pytest.mark.parametrize("existing_pk", [{"passphrase": "password"}], indirect=True)
++def test_exceptions_on_calling_load_pem_private_key(x509, pk_args):
++ pk_args["passphrase"] = "hunter1"
++ pk_args["overwrite"] = True
++
++ with patch(
++ "cryptography.hazmat.primitives.serialization.load_pem_private_key",
++ side_effect=ValueError("Bad decrypt. Incorrect password?"),
++ ):
++ ret = x509.private_key_managed(**pk_args)
++ _assert_pk_basic(ret, "rsa", passphrase="hunter1")
++
++ with patch(
++ "cryptography.hazmat.primitives.serialization.load_pem_private_key",
++ side_effect=ValueError(
++ "Could not deserialize key data. The data may be in an incorrect format, "
++ "the provided password may be incorrect, "
++ "it may be encrypted with an unsupported algorithm, "
++ "or it may be an unsupported key type "
++ "(e.g. EC curves with explicit parameters)."
++ ),
++ ):
++ ret = x509.private_key_managed(**pk_args)
++ _assert_pk_basic(ret, "rsa", passphrase="hunter1")
+diff --git a/tests/pytests/integration/states/test_x509_v2.py b/tests/pytests/integration/states/test_x509_v2.py
+index 4f94341295..ad8d904c92 100644
+--- a/tests/pytests/integration/states/test_x509_v2.py
++++ b/tests/pytests/integration/states/test_x509_v2.py
+@@ -195,6 +195,13 @@ Certificate:
+ """
+ with x509_salt_master.state_tree.base.temp_file("manage_cert.sls", state):
+ ret = x509_salt_call_cli.run("state.apply", "manage_cert")
++ if (
++ ret.returncode == 1
++ and "NotImplementedError: ECDSA keys with unnamed curves" in ret.stdout
++ ):
++ pytest.skip(
++ "The version of OpenSSL doesn't support ECDSA keys with unnamed curves"
++ )
+ assert ret.returncode == 0
+ assert ret.data[next(iter(ret.data))]["changes"]
+ assert (tmp_path / "priv.key").exists()
+--
+2.46.0
+
diff --git a/join-masters-if-it-is-a-list-671.patch b/join-masters-if-it-is-a-list-671.patch
new file mode 100644
index 0000000..2f384ab
--- /dev/null
+++ b/join-masters-if-it-is-a-list-671.patch
@@ -0,0 +1,105 @@
+From 94973ee85d766d7e98d02d89f4c81e59b36cb716 Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Thu, 29 Aug 2024 10:01:12 +0200
+Subject: [PATCH] Join masters if it is a list (#671)
+
+Co-authored-by: Twangboy
+---
+ changelog/64170.fixed.md | 2 +
+ salt/utils/cloud.py | 10 +++++
+ tests/pytests/unit/utils/test_cloud.py | 52 ++++++++++++++++++++++++++
+ 3 files changed, 64 insertions(+)
+ create mode 100644 changelog/64170.fixed.md
+
+diff --git a/changelog/64170.fixed.md b/changelog/64170.fixed.md
+new file mode 100644
+index 0000000000..1d20355bf1
+--- /dev/null
++++ b/changelog/64170.fixed.md
+@@ -0,0 +1,2 @@
++Fixed issue in salt-cloud so that multiple masters specified in the cloud
++are written to the minion config properly
+diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py
+index b7208dc4a6..a084313059 100644
+--- a/salt/utils/cloud.py
++++ b/salt/utils/cloud.py
+@@ -1202,6 +1202,16 @@ def wait_for_passwd(
+ time.sleep(trysleep)
+
+
++def _format_master_param(master):
++ """
++ If the master is a list, we need to convert it to a comma delimited string
++ Otherwise, we just return master
++ """
++ if isinstance(master, list):
++ return ",".join(master)
++ return master
++
++
+ def deploy_windows(
+ host,
+ port=445,
+diff --git a/tests/pytests/unit/utils/test_cloud.py b/tests/pytests/unit/utils/test_cloud.py
+index 550b63c974..db9d258d39 100644
+--- a/tests/pytests/unit/utils/test_cloud.py
++++ b/tests/pytests/unit/utils/test_cloud.py
+@@ -605,3 +605,55 @@ def test_deploy_script_ssh_timeout():
+ ssh_kwargs = root_cmd.call_args.kwargs
+ assert "ssh_timeout" in ssh_kwargs
+ assert ssh_kwargs["ssh_timeout"] == 34
++
++
++@pytest.mark.parametrize(
++ "master,expected",
++ [
++ (None, None),
++ ("single_master", "single_master"),
++ (["master1", "master2", "master3"], "master1,master2,master3"),
++ ],
++)
++def test__format_master_param(master, expected):
++ result = cloud._format_master_param(master)
++ assert result == expected
++
++
++@pytest.mark.skip_unless_on_windows(reason="Only applicable for Windows.")
++@pytest.mark.parametrize(
++ "master,expected",
++ [
++ (None, None),
++ ("single_master", "single_master"),
++ (["master1", "master2", "master3"], "master1,master2,master3"),
++ ],
++)
++def test_deploy_windows_master(master, expected):
++ """
++ Test deploy_windows with master parameter
++ """
++ mock_true = MagicMock(return_value=True)
++ mock_tuple = MagicMock(return_value=(0, 0, 0))
++ with patch("salt.utils.smb.get_conn", MagicMock()), patch(
++ "salt.utils.smb.mkdirs", MagicMock()
++ ), patch("salt.utils.smb.put_file", MagicMock()), patch(
++ "salt.utils.smb.delete_file", MagicMock()
++ ), patch(
++ "salt.utils.smb.delete_directory", MagicMock()
++ ), patch(
++ "time.sleep", MagicMock()
++ ), patch.object(
++ cloud, "wait_for_port", mock_true
++ ), patch.object(
++ cloud, "fire_event", MagicMock()
++ ), patch.object(
++ cloud, "wait_for_psexecsvc", mock_true
++ ), patch.object(
++ cloud, "run_psexec_command", mock_tuple
++ ) as mock:
++ cloud.deploy_windows(host="test", win_installer="install.exe", master=master)
++ expected_cmd = "c:\\salttemp\\install.exe"
++ expected_args = "/S /master={} /minion-name=None".format(expected)
++ assert mock.call_args_list[0].args[0] == expected_cmd
++ assert mock.call_args_list[0].args[1] == expected_args
+--
+2.44.0
+
diff --git a/make-tests-compatible-with-venv-bundle.patch b/make-tests-compatible-with-venv-bundle.patch
new file mode 100644
index 0000000..10e4022
--- /dev/null
+++ b/make-tests-compatible-with-venv-bundle.patch
@@ -0,0 +1,883 @@
+From 25c3df7713bd2a19a0980358fa72c1c48a08a1f4 Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Wed, 7 Aug 2024 10:28:07 +0200
+Subject: [PATCH] Make tests compatible with venv bundle
+
+Co-authored-by: cmcmarrow
+---
+ tests/pytests/functional/modules/test_sdb.py | 1 +
+ tests/pytests/functional/modules/test_yaml.py | 2 +-
+ .../rthooks/test_salt_utils_vt_terminal.py | 22 +++++--
+ .../pyinstaller/rthooks/test_subprocess.py | 22 +++++--
+ .../utils/yamllint/test_yamllint.py | 2 +-
+ tests/pytests/unit/modules/test_pip.py | 63 +++++++++++++------
+ .../unit/modules/test_transactional_update.py | 13 ++--
+ tests/pytests/unit/states/test_pkgrepo.py | 3 +-
+ tests/pytests/unit/test_fileserver.py | 8 +--
+ tests/pytests/unit/utils/test_gitfs.py | 18 ++++++
+ tests/pytests/unit/utils/test_msgpack.py | 2 +-
+ tests/pytests/unit/utils/test_pycrypto.py | 25 ++++----
+ tests/unit/test_config.py | 20 +++++-
+ tests/unit/utils/test_sdb.py | 2 +-
+ tests/unit/utils/test_templates.py | 34 ++++++++++
+ 15 files changed, 177 insertions(+), 60 deletions(-)
+
+diff --git a/tests/pytests/functional/modules/test_sdb.py b/tests/pytests/functional/modules/test_sdb.py
+index 5519bf8ab57..837e7515d30 100644
+--- a/tests/pytests/functional/modules/test_sdb.py
++++ b/tests/pytests/functional/modules/test_sdb.py
+@@ -16,6 +16,7 @@ def minion_config_overrides():
+ }
+
+
++@pytest.mark.skip("Great module migration")
+ @pytest.mark.parametrize(
+ "expected_value",
+ (
+diff --git a/tests/pytests/functional/modules/test_yaml.py b/tests/pytests/functional/modules/test_yaml.py
+index 2a8fbc113ff..9aad0dfdc8c 100644
+--- a/tests/pytests/functional/modules/test_yaml.py
++++ b/tests/pytests/functional/modules/test_yaml.py
+@@ -13,7 +13,7 @@ try:
+ import salt.modules.yaml
+ import salt.utils.yamllint
+
+- YAMLLINT_AVAILABLE = True
++ YAMLLINT_AVAILABLE = salt.utils.yamllint.has_yamllint()
+ except ImportError:
+ YAMLLINT_AVAILABLE = False
+
+diff --git a/tests/pytests/functional/utils/pyinstaller/rthooks/test_salt_utils_vt_terminal.py b/tests/pytests/functional/utils/pyinstaller/rthooks/test_salt_utils_vt_terminal.py
+index c45b5730a8e..ea687c0776d 100644
+--- a/tests/pytests/functional/utils/pyinstaller/rthooks/test_salt_utils_vt_terminal.py
++++ b/tests/pytests/functional/utils/pyinstaller/rthooks/test_salt_utils_vt_terminal.py
+@@ -8,6 +8,9 @@ import salt.utils.pyinstaller.rthooks._overrides as overrides
+ from tests.support import mock
+ from tests.support.helpers import PatchedEnviron
+
++LD_LIBRARY_PATH = ""
++if os.environ.get('VIRTUAL_ENV'):
++ LD_LIBRARY_PATH = f"{os.environ.get('VIRTUAL_ENV')}/lib"
+
+ @pytest.fixture(params=("LD_LIBRARY_PATH", "LIBPATH"))
+ def envvar(request):
+@@ -17,9 +20,14 @@ def envvar(request):
+ @pytest.fixture
+ def meipass(envvar):
+ with mock.patch("salt.utils.pyinstaller.rthooks._overrides.sys") as patched_sys:
+- patched_sys._MEIPASS = "{}_VALUE".format(envvar)
+- assert overrides.sys._MEIPASS == "{}_VALUE".format(envvar)
+- yield "{}_VALUE".format(envvar)
++ ld_path_mock_val = f"{envvar}_VALUE"
++ if envvar == "LD_LIBRARY_PATH" and LD_LIBRARY_PATH:
++ # venv-minion python wrapper hardcodes LD_LIB_PATH that
++ # we cannot overwrite from the testsuite
++ ld_path_mock_val = LD_LIBRARY_PATH
++ patched_sys._MEIPASS = ld_path_mock_val
++ assert overrides.sys._MEIPASS == ld_path_mock_val
++ yield ld_path_mock_val
+ assert not hasattr(sys, "_MEIPASS")
+ assert not hasattr(overrides.sys, "_MEIPASS")
+
+@@ -111,7 +119,8 @@ def test_vt_terminal_environ_cleanup(envvar, meipass):
+ returned_env = json.loads(buffer_o)
+ assert returned_env != original_env
+ assert envvar in returned_env
+- assert returned_env[envvar] == ""
++ envvar_value = LD_LIBRARY_PATH if envvar == "LD_LIBRARY_PATH" else ""
++ assert returned_env[envvar] == envvar_value
+
+
+ def test_vt_terminal_environ_cleanup_passed_directly_not_removed(envvar, meipass):
+@@ -139,4 +148,7 @@ def test_vt_terminal_environ_cleanup_passed_directly_not_removed(envvar, meipass
+ returned_env = json.loads(buffer_o)
+ assert returned_env != original_env
+ assert envvar in returned_env
+- assert returned_env[envvar] == envvar
++ envvar_val = envvar
++ if LD_LIBRARY_PATH and envvar == "LD_LIBRARY_PATH":
++ envvar_val = LD_LIBRARY_PATH
++ assert returned_env[envvar] == envvar_val
+diff --git a/tests/pytests/functional/utils/pyinstaller/rthooks/test_subprocess.py b/tests/pytests/functional/utils/pyinstaller/rthooks/test_subprocess.py
+index 836e392d016..e4b5420d5e3 100644
+--- a/tests/pytests/functional/utils/pyinstaller/rthooks/test_subprocess.py
++++ b/tests/pytests/functional/utils/pyinstaller/rthooks/test_subprocess.py
+@@ -9,6 +9,9 @@ import salt.utils.pyinstaller.rthooks._overrides as overrides
+ from tests.support import mock
+ from tests.support.helpers import PatchedEnviron
+
++LD_LIBRARY_PATH = ""
++if os.environ.get('VIRTUAL_ENV'):
++ LD_LIBRARY_PATH = f"{os.environ.get('VIRTUAL_ENV')}/lib"
+
+ @pytest.fixture(params=("LD_LIBRARY_PATH", "LIBPATH"))
+ def envvar(request):
+@@ -18,9 +21,14 @@ def envvar(request):
+ @pytest.fixture
+ def meipass(envvar):
+ with mock.patch("salt.utils.pyinstaller.rthooks._overrides.sys") as patched_sys:
+- patched_sys._MEIPASS = "{}_VALUE".format(envvar)
+- assert overrides.sys._MEIPASS == "{}_VALUE".format(envvar)
+- yield "{}_VALUE".format(envvar)
++ ld_path_mock_val = f"{envvar}_VALUE"
++ if envvar == "LD_LIBRARY_PATH" and LD_LIBRARY_PATH:
++ # venv-minion python wrapper hardcodes LD_LIB_PATH that
++ # we cannot overwrite from the testsuite
++ ld_path_mock_val = LD_LIBRARY_PATH
++ patched_sys._MEIPASS = ld_path_mock_val
++ assert overrides.sys._MEIPASS == ld_path_mock_val
++ yield ld_path_mock_val
+ assert not hasattr(sys, "_MEIPASS")
+ assert not hasattr(overrides.sys, "_MEIPASS")
+
+@@ -88,7 +96,8 @@ def test_subprocess_popen_environ_cleanup(envvar, meipass):
+ returned_env = json.loads(stdout)
+ assert returned_env != original_env
+ assert envvar in returned_env
+- assert returned_env[envvar] == ""
++ envvar_value = LD_LIBRARY_PATH if envvar == "LD_LIBRARY_PATH" else ""
++ assert returned_env[envvar] == envvar_value
+
+
+ def test_subprocess_popen_environ_cleanup_passed_directly_not_removed(envvar, meipass):
+@@ -108,4 +117,7 @@ def test_subprocess_popen_environ_cleanup_passed_directly_not_removed(envvar, me
+ returned_env = json.loads(stdout)
+ assert returned_env != original_env
+ assert envvar in returned_env
+- assert returned_env[envvar] == envvar
++ envvar_val = envvar
++ if LD_LIBRARY_PATH and envvar == "LD_LIBRARY_PATH":
++ envvar_val = LD_LIBRARY_PATH
++ assert returned_env[envvar] == envvar_val
+diff --git a/tests/pytests/functional/utils/yamllint/test_yamllint.py b/tests/pytests/functional/utils/yamllint/test_yamllint.py
+index 403c6fc610e..3c730523c4d 100644
+--- a/tests/pytests/functional/utils/yamllint/test_yamllint.py
++++ b/tests/pytests/functional/utils/yamllint/test_yamllint.py
+@@ -7,7 +7,7 @@ import salt.utils.versions as versions
+ try:
+ import salt.utils.yamllint as yamllint
+
+- YAMLLINT_AVAILABLE = True
++ YAMLLINT_AVAILABLE = yamllint.has_yamllint()
+ except ImportError:
+ YAMLLINT_AVAILABLE = False
+
+diff --git a/tests/pytests/unit/modules/test_pip.py b/tests/pytests/unit/modules/test_pip.py
+index 4b2da77786b..fbe0dc5f1cf 100644
+--- a/tests/pytests/unit/modules/test_pip.py
++++ b/tests/pytests/unit/modules/test_pip.py
+@@ -15,6 +15,10 @@ MISSING_SETUP_PY_FILE = not os.path.exists(
+ os.path.join(RUNTIME_VARS.CODE_DIR, "setup.py")
+ )
+
++TARGET = []
++if os.environ.get('VENV_PIP_TARGET'):
++ TARGET = ["--target", os.environ.get('VENV_PIP_TARGET')]
++
+
+ class FakeFopen:
+ def __init__(self, filename):
+@@ -102,6 +106,7 @@ def test_install_frozen_app(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ pkg,
+ ]
+ mock.assert_called_with(
+@@ -123,6 +128,7 @@ def test_install_source_app(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ pkg,
+ ]
+ mock.assert_called_with(
+@@ -143,6 +149,7 @@ def test_fix4361(python_binary):
+ "install",
+ "--requirement",
+ "requirements.txt",
++ *TARGET,
+ ]
+ mock.assert_called_with(
+ expected_cmd,
+@@ -169,7 +176,7 @@ def test_install_multiple_editable(python_binary):
+ "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting",
+ ]
+
+- expected = [*python_binary, "install"]
++ expected = [*python_binary, "install", *TARGET]
+ for item in editables:
+ expected.extend(["--editable", item])
+
+@@ -205,7 +212,7 @@ def test_install_multiple_pkgs_and_editables(python_binary):
+ "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting",
+ ]
+
+- expected = [*python_binary, "install"]
++ expected = [*python_binary, "install", *TARGET]
+ expected.extend(pkgs)
+ for item in editables:
+ expected.extend(["--editable", item])
+@@ -241,6 +248,7 @@ def test_install_multiple_pkgs_and_editables(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ pkgs[0],
+ "--editable",
+ editables[0],
+@@ -268,7 +276,7 @@ def test_issue5940_install_multiple_pip_mirrors(python_binary):
+ expected = [*python_binary, "install", "--use-mirrors"]
+ for item in mirrors:
+ expected.extend(["--mirrors", item])
+- expected.append("pep8")
++ expected = [*expected, *TARGET, "pep8"]
+
+ # Passing mirrors as a list
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+@@ -300,6 +308,7 @@ def test_issue5940_install_multiple_pip_mirrors(python_binary):
+ "--use-mirrors",
+ "--mirrors",
+ mirrors[0],
++ *TARGET,
+ "pep8",
+ ]
+
+@@ -327,7 +336,7 @@ def test_install_with_multiple_find_links(python_binary):
+ expected = [*python_binary, "install"]
+ for item in find_links:
+ expected.extend(["--find-links", item])
+- expected.append(pkg)
++ expected = [*expected, *TARGET, pkg]
+
+ # Passing mirrors as a list
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+@@ -370,6 +379,7 @@ def test_install_with_multiple_find_links(python_binary):
+ "install",
+ "--find-links",
+ find_links[0],
++ *TARGET,
+ pkg,
+ ]
+
+@@ -435,6 +445,7 @@ def test_install_cached_requirements_used(python_binary):
+ "install",
+ "--requirement",
+ "my_cached_reqs",
++ *TARGET,
+ ]
+ mock.assert_called_with(
+ expected,
+@@ -491,6 +502,7 @@ def test_install_log_argument_in_resulting_command(python_binary):
+ "install",
+ "--log",
+ log_path,
++ *TARGET,
+ pkg,
+ ]
+ mock.assert_called_with(
+@@ -521,7 +533,7 @@ def test_install_timeout_argument_in_resulting_command(python_binary):
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, timeout=10)
+ mock.assert_called_with(
+- expected + [10, pkg],
++ expected + [10, *TARGET, pkg],
+ saltenv="base",
+ runas=None,
+ use_vt=False,
+@@ -533,7 +545,7 @@ def test_install_timeout_argument_in_resulting_command(python_binary):
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, timeout="10")
+ mock.assert_called_with(
+- expected + ["10", pkg],
++ expected + ["10", *TARGET, pkg],
+ saltenv="base",
+ runas=None,
+ use_vt=False,
+@@ -557,6 +569,7 @@ def test_install_index_url_argument_in_resulting_command(python_binary):
+ "install",
+ "--index-url",
+ index_url,
++ *TARGET,
+ pkg,
+ ]
+ mock.assert_called_with(
+@@ -579,6 +592,7 @@ def test_install_extra_index_url_argument_in_resulting_command(python_binary):
+ "install",
+ "--extra-index-url",
+ extra_index_url,
++ *TARGET,
+ pkg,
+ ]
+ mock.assert_called_with(
+@@ -595,7 +609,7 @@ def test_install_no_index_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, no_index=True)
+- expected = [*python_binary, "install", "--no-index", pkg]
++ expected = [*python_binary, "install", "--no-index", *TARGET, pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -611,7 +625,7 @@ def test_install_build_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, build=build)
+- expected = [*python_binary, "install", "--build", build, pkg]
++ expected = [*python_binary, "install", "--build", build, *TARGET, pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -646,6 +660,7 @@ def test_install_download_argument_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ "--download",
+ download,
+ pkg,
+@@ -664,7 +679,7 @@ def test_install_no_download_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, no_download=True)
+- expected = [*python_binary, "install", "--no-download", pkg]
++ expected = [*python_binary, "install", *TARGET, "--no-download", pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -691,6 +706,7 @@ def test_install_download_cache_dir_arguments_in_resulting_command(python_binary
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ cmd_arg,
+ download_cache,
+ pkg,
+@@ -720,7 +736,7 @@ def test_install_source_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, source=source)
+- expected = [*python_binary, "install", "--source", source, pkg]
++ expected = [*python_binary, "install", *TARGET, "--source", source, pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -739,6 +755,7 @@ def test_install_exists_action_argument_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ "--exists-action",
+ action,
+ pkg,
+@@ -761,7 +778,7 @@ def test_install_install_options_argument_in_resulting_command(python_binary):
+ install_options = ["--exec-prefix=/foo/bar", "--install-scripts=/foo/bar/bin"]
+ pkg = "pep8"
+
+- expected = [*python_binary, "install"]
++ expected = [*python_binary, "install", *TARGET]
+ for item in install_options:
+ expected.extend(["--install-option", item])
+ expected.append(pkg)
+@@ -797,6 +814,7 @@ def test_install_install_options_argument_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ "--install-option",
+ install_options[0],
+ pkg,
+@@ -814,7 +832,7 @@ def test_install_global_options_argument_in_resulting_command(python_binary):
+ global_options = ["--quiet", "--no-user-cfg"]
+ pkg = "pep8"
+
+- expected = [*python_binary, "install"]
++ expected = [*python_binary, "install", *TARGET]
+ for item in global_options:
+ expected.extend(["--global-option", item])
+ expected.append(pkg)
+@@ -850,6 +868,7 @@ def test_install_global_options_argument_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ "--global-option",
+ global_options[0],
+ pkg,
+@@ -868,7 +887,7 @@ def test_install_upgrade_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, upgrade=True)
+- expected = [*python_binary, "install", "--upgrade", pkg]
++ expected = [*python_binary, "install", *TARGET, "--upgrade", pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -886,6 +905,7 @@ def test_install_force_reinstall_argument_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ "--force-reinstall",
+ pkg,
+ ]
+@@ -906,6 +926,7 @@ def test_install_ignore_installed_argument_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ "--ignore-installed",
+ pkg,
+ ]
+@@ -923,7 +944,7 @@ def test_install_no_deps_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, no_deps=True)
+- expected = [*python_binary, "install", "--no-deps", pkg]
++ expected = [*python_binary, "install", *TARGET, "--no-deps", pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -938,7 +959,7 @@ def test_install_no_install_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, no_install=True)
+- expected = [*python_binary, "install", "--no-install", pkg]
++ expected = [*python_binary, "install", *TARGET, "--no-install", pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -954,7 +975,7 @@ def test_install_proxy_argument_in_resulting_command(python_binary):
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ pip.install(pkg, proxy=proxy)
+- expected = [*python_binary, "install", "--proxy", proxy, pkg]
++ expected = [*python_binary, "install", "--proxy", proxy, *TARGET, pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -981,7 +1002,7 @@ def test_install_proxy_false_argument_in_resulting_command(python_binary):
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ with patch.dict(pip.__opts__, config_mock):
+ pip.install(pkg, proxy=proxy)
+- expected = [*python_binary, "install", pkg]
++ expected = [*python_binary, "install", *TARGET, pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -1012,6 +1033,7 @@ def test_install_global_proxy_in_resulting_command(python_binary):
+ "install",
+ "--proxy",
+ proxy,
++ *TARGET,
+ pkg,
+ ]
+ mock.assert_called_with(
+@@ -1032,6 +1054,7 @@ def test_install_multiple_requirements_arguments_in_resulting_command(python_bin
+ expected = [*python_binary, "install"]
+ for item in cached_reqs:
+ expected.extend(["--requirement", item])
++ expected.extend(TARGET)
+
+ # Passing option as a list
+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""})
+@@ -1068,6 +1091,7 @@ def test_install_multiple_requirements_arguments_in_resulting_command(python_bin
+ "install",
+ "--requirement",
+ cached_reqs[0],
++ *TARGET,
+ ]
+ mock.assert_called_with(
+ expected,
+@@ -1088,6 +1112,7 @@ def test_install_extra_args_arguments_in_resulting_command(python_binary):
+ expected = [
+ *python_binary,
+ "install",
++ *TARGET,
+ pkg,
+ "--latest-pip-kwarg",
+ "param",
+@@ -1604,7 +1629,7 @@ def test_install_pre_argument_in_resulting_command(python_binary):
+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}):
+ with patch("salt.modules.pip.version", MagicMock(return_value="1.3")):
+ pip.install(pkg, pre_releases=True)
+- expected = [*python_binary, "install", pkg]
++ expected = [*python_binary, "install", *TARGET, pkg]
+ mock.assert_called_with(
+ expected,
+ saltenv="base",
+@@ -1620,7 +1645,7 @@ def test_install_pre_argument_in_resulting_command(python_binary):
+ ):
+ with patch("salt.modules.pip._get_pip_bin", MagicMock(return_value=["pip"])):
+ pip.install(pkg, pre_releases=True)
+- expected = ["pip", "install", "--pre", pkg]
++ expected = ["pip", "install", *TARGET, "--pre", pkg]
+ mock_run_all.assert_called_with(
+ expected,
+ saltenv="base",
+diff --git a/tests/pytests/unit/modules/test_transactional_update.py b/tests/pytests/unit/modules/test_transactional_update.py
+index dbd72fd74bf..e0ef2abd0f3 100644
+--- a/tests/pytests/unit/modules/test_transactional_update.py
++++ b/tests/pytests/unit/modules/test_transactional_update.py
+@@ -1,3 +1,4 @@
++import os
+ import pytest
+
+ import salt.loader.context
+@@ -10,6 +11,10 @@ pytestmark = [
+ pytest.mark.skip_on_windows(reason="Not supported on Windows"),
+ ]
+
++SALT_CALL_BINARY = "salt-call"
++if os.environ.get('VIRTUAL_ENV'):
++ SALT_CALL_BINARY = f"{os.environ.get('VIRTUAL_ENV')}/bin/salt-call"
++
+
+ @pytest.fixture
+ def configure_loader_modules():
+@@ -379,7 +384,7 @@ def test_call_fails_function():
+ "--continue",
+ "--quiet",
+ "run",
+- "salt-call",
++ SALT_CALL_BINARY,
+ "--out",
+ "json",
+ "-l",
+@@ -411,7 +416,7 @@ def test_call_success_no_reboot():
+ "--continue",
+ "--quiet",
+ "run",
+- "salt-call",
++ SALT_CALL_BINARY,
+ "--out",
+ "json",
+ "-l",
+@@ -454,7 +459,7 @@ def test_call_success_reboot():
+ "--continue",
+ "--quiet",
+ "run",
+- "salt-call",
++ SALT_CALL_BINARY,
+ "--out",
+ "json",
+ "-l",
+@@ -488,7 +493,7 @@ def test_call_success_parameters():
+ "--continue",
+ "--quiet",
+ "run",
+- "salt-call",
++ SALT_CALL_BINARY,
+ "--out",
+ "json",
+ "-l",
+diff --git a/tests/pytests/unit/states/test_pkgrepo.py b/tests/pytests/unit/states/test_pkgrepo.py
+index 5f540bd2454..14d17ad3f9f 100644
+--- a/tests/pytests/unit/states/test_pkgrepo.py
++++ b/tests/pytests/unit/states/test_pkgrepo.py
+@@ -1,7 +1,6 @@
+ """
+ :codeauthor: Tyler Johnson
+ """
+-
+ import pytest
+
+ import salt.states.pkgrepo as pkgrepo
+@@ -390,7 +389,7 @@ def test_migrated_wrong_method():
+ with patch.dict(pkgrepo.__grains__, grains), patch.dict(
+ pkgrepo.__salt__, salt_mock
+ ):
+- assert pkgrepo.migrated("/mnt", method_="magic") == {
++ assert pkgrepo.migrated("/mnt", method="magic") == {
+ "name": "/mnt",
+ "result": False,
+ "changes": {},
+diff --git a/tests/pytests/unit/test_fileserver.py b/tests/pytests/unit/test_fileserver.py
+index 8dd3ea0a27d..49be3967dc4 100644
+--- a/tests/pytests/unit/test_fileserver.py
++++ b/tests/pytests/unit/test_fileserver.py
+@@ -75,9 +75,7 @@ def test_file_server_url_escape(tmp_path):
+ opts = {
+ "fileserver_backend": ["roots"],
+ "extension_modules": "",
+- "optimization_order": [
+- 0,
+- ],
++ "optimization_order": [0, 1],
+ "file_roots": {
+ "base": [fileroot],
+ },
+@@ -102,9 +100,7 @@ def test_file_server_serve_url_escape(tmp_path):
+ opts = {
+ "fileserver_backend": ["roots"],
+ "extension_modules": "",
+- "optimization_order": [
+- 0,
+- ],
++ "optimization_order": [0, 1],
+ "file_roots": {
+ "base": [fileroot],
+ },
+diff --git a/tests/pytests/unit/utils/test_gitfs.py b/tests/pytests/unit/utils/test_gitfs.py
+index 2bf627049f9..bd7d74cb2b2 100644
+--- a/tests/pytests/unit/utils/test_gitfs.py
++++ b/tests/pytests/unit/utils/test_gitfs.py
+@@ -3,6 +3,7 @@ import time
+
+ import pytest
+
++import salt.config
+ import salt.fileserver.gitfs
+ import salt.utils.gitfs
+ from salt.exceptions import FileserverConfigError
+@@ -24,6 +25,23 @@ if HAS_PYGIT2:
+ import pygit2
+
+
++@pytest.fixture
++def minion_opts(tmp_path):
++ """
++ Default minion configuration with relative temporary paths to not require root permissions.
++ """
++ root_dir = tmp_path / "minion"
++ opts = salt.config.DEFAULT_MINION_OPTS.copy()
++ opts["__role"] = "minion"
++ opts["root_dir"] = str(root_dir)
++ for name in ("cachedir", "pki_dir", "sock_dir", "conf_dir"):
++ dirpath = root_dir / name
++ dirpath.mkdir(parents=True)
++ opts[name] = str(dirpath)
++ opts["log_file"] = "logs/minion.log"
++ return opts
++
++
+ @pytest.mark.parametrize(
+ "role_name,role_class",
+ (
+diff --git a/tests/pytests/unit/utils/test_msgpack.py b/tests/pytests/unit/utils/test_msgpack.py
+index a09b6e5b8b1..3d0b9d7fc8c 100644
+--- a/tests/pytests/unit/utils/test_msgpack.py
++++ b/tests/pytests/unit/utils/test_msgpack.py
+@@ -3,7 +3,7 @@ import pytest
+ import salt.utils.msgpack
+ from tests.support.mock import MagicMock, patch
+
+-
++@pytest.mark.skipif(salt.utils.msgpack.version < (1, 0, 0), reason="Test requires msgpack version >= 1.0.0")
+ def test_load_encoding(tmp_path):
+ """
+ test when using msgpack version >= 1.0.0 we
+diff --git a/tests/pytests/unit/utils/test_pycrypto.py b/tests/pytests/unit/utils/test_pycrypto.py
+index 693ad10e240..9e0b58d1b35 100644
+--- a/tests/pytests/unit/utils/test_pycrypto.py
++++ b/tests/pytests/unit/utils/test_pycrypto.py
+@@ -57,21 +57,20 @@ def test_gen_hash_crypt(algorithm, expected):
+ """
+ Test gen_hash with crypt library
+ """
+- with patch("salt.utils.pycrypto.methods", {}):
+- ret = salt.utils.pycrypto.gen_hash(
+- crypt_salt=expected["salt"], password=passwd, algorithm=algorithm
+- )
+- assert ret == expected["hashed"]
++ ret = salt.utils.pycrypto.gen_hash(
++ crypt_salt=expected["salt"], password=passwd, algorithm=algorithm
++ )
++ assert ret == expected["hashed"]
+
+- ret = salt.utils.pycrypto.gen_hash(
+- crypt_salt=expected["badsalt"], password=passwd, algorithm=algorithm
+- )
+- assert ret != expected["hashed"]
++ ret = salt.utils.pycrypto.gen_hash(
++ crypt_salt=expected["badsalt"], password=passwd, algorithm=algorithm
++ )
++ assert ret != expected["hashed"]
+
+- ret = salt.utils.pycrypto.gen_hash(
+- crypt_salt=None, password=passwd, algorithm=algorithm
+- )
+- assert ret != expected["hashed"]
++ ret = salt.utils.pycrypto.gen_hash(
++ crypt_salt=None, password=passwd, algorithm=algorithm
++ )
++ assert ret != expected["hashed"]
+
+
+ @pytest.mark.skipif(not salt.utils.pycrypto.HAS_CRYPT, reason="crypt not available")
+diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
+index 5cc58c273d0..6995b01c892 100644
+--- a/tests/unit/test_config.py
++++ b/tests/unit/test_config.py
+@@ -83,9 +83,12 @@ class SampleConfTest(DefaultConfigsBase, TestCase):
+ """
+ master_config = SAMPLE_CONF_DIR + "master"
+ ret = salt.config._read_conf_file(master_config)
++ # openSUSE modified the default config in
++ # https://github.com/opensuse/salt/commit/6ffbf7fcc178f32c670b177b25ed64658c59f1bf
++ expected_config = {"user": "salt", "syndic_user": "salt"}
+ self.assertEqual(
+ ret,
+- {},
++ expected_config,
+ "Sample config file '{}' must be commented out.".format(master_config),
+ )
+
+@@ -347,7 +350,10 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
+
+ with patched_environ(SALT_MINION_CONFIG=env_fpath):
+ # Should load from env variable, not the default configuration file
+- config = salt.config.minion_config("{}/minion".format(CONFIG_DIR))
++ # Override defaults from venv-minion conf
++ defaults = salt.config.DEFAULT_MINION_OPTS.copy()
++ defaults["default_include"] = ""
++ config = salt.config.minion_config("{}/minion".format(CONFIG_DIR), defaults=defaults)
+ self.assertEqual(config["log_file"], env_fpath)
+
+ root_dir = os.path.join(tempdir, "foo", "bar")
+@@ -1946,6 +1952,11 @@ class APIConfigTestCase(DefaultConfigsBase, TestCase):
+ if salt.utils.platform.is_windows():
+ expected = "{}\\var\\log\\salt\\api".format(RUNTIME_VARS.TMP_ROOT_DIR)
+
++ if os.environ.get("VIRTUAL_ENV"):
++ # venv bundle configures --salt-logs-dir=%{_localstatedir}/log
++ # in the RPM spec file
++ expected = expected.replace("/salt/api", "/api")
++
+ ret = salt.config.api_config("/some/fake/path")
+ self.assertEqual(ret["log_file"], expected)
+
+@@ -2017,6 +2028,11 @@ class APIConfigTestCase(DefaultConfigsBase, TestCase):
+ mock_pid = "c:\\mock\\root\\var\\run\\salt-api.pid"
+ mock_master_config["root_dir"] = "c:\\mock\\root"
+
++ if os.environ.get("VIRTUAL_ENV"):
++ # venv bundle configures --salt-logs-dir=%{_localstatedir}/log
++ # in the RPM spec file
++ mock_log = mock_log.replace("/salt", "")
++
+ with patch(
+ "salt.config.client_config", MagicMock(return_value=mock_master_config)
+ ):
+diff --git a/tests/unit/utils/test_sdb.py b/tests/unit/utils/test_sdb.py
+index 87886cbc521..69cbda07beb 100644
+--- a/tests/unit/utils/test_sdb.py
++++ b/tests/unit/utils/test_sdb.py
+@@ -49,7 +49,7 @@ class SdbTestCase(TestCase, LoaderModuleMockMixin):
+ # test with SQLite database write and read
+
+ def test_sqlite_get_found(self):
+- expected = {b"name": b"testone", b"number": 46}
++ expected = {"name": "testone", "number": 46}
+ sdb.sdb_set("sdb://test_sdb_data/test1", expected, self.sdb_opts)
+ resp = sdb.sdb_get("sdb://test_sdb_data/test1", self.sdb_opts)
+ self.assertEqual(resp, expected)
+diff --git a/tests/unit/utils/test_templates.py b/tests/unit/utils/test_templates.py
+index 264b4ae801d..604395f5e08 100644
+--- a/tests/unit/utils/test_templates.py
++++ b/tests/unit/utils/test_templates.py
+@@ -1,6 +1,7 @@
+ """
+ Unit tests for salt.utils.templates.py
+ """
++
+ import logging
+ import os
+ import sys
+@@ -22,6 +23,20 @@ try:
+ except ImportError:
+ HAS_CHEETAH = False
+
++try:
++ import genshi as _
++
++ HAS_GENSHI = True
++except ImportError:
++ HAS_GENSHI = False
++
++try:
++ import mako as _
++
++ HAS_MAKO = True
++except ImportError:
++ HAS_MAKO = False
++
+ log = logging.getLogger(__name__)
+
+
+@@ -83,16 +98,19 @@ class RenderTestCase(TestCase):
+ assert res == expected
+
+ ### Tests for mako template
++ @pytest.mark.skipif(not HAS_MAKO, reason="Mako module not available for testing")
+ def test_render_mako_sanity(self):
+ tmpl = """OK"""
+ res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context))
+ self.assertEqual(res, "OK")
+
++ @pytest.mark.skipif(not HAS_MAKO, reason="Mako module not available for testing")
+ def test_render_mako_evaluate(self):
+ tmpl = """${ "OK" }"""
+ res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context))
+ self.assertEqual(res, "OK")
+
++ @pytest.mark.skipif(not HAS_MAKO, reason="Mako module not available for testing")
+ def test_render_mako_evaluate_multi(self):
+ tmpl = """
+ % if 1:
+@@ -103,6 +121,7 @@ class RenderTestCase(TestCase):
+ stripped = res.strip()
+ self.assertEqual(stripped, "OK")
+
++ @pytest.mark.skipif(not HAS_MAKO, reason="Mako module not available for testing")
+ def test_render_mako_variable(self):
+ tmpl = """${ var }"""
+
+@@ -152,21 +171,33 @@ class RenderTestCase(TestCase):
+ self.assertEqual(res, "OK")
+
+ ### Tests for genshi template (xml-based)
++ @pytest.mark.skipif(
++ not HAS_GENSHI, reason="Genshi module not available for testing"
++ )
+ def test_render_genshi_sanity(self):
+ tmpl = """OK"""
+ res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context))
+ self.assertEqual(res, "OK")
+
++ @pytest.mark.skipif(
++ not HAS_GENSHI, reason="Genshi module not available for testing"
++ )
+ def test_render_genshi_evaluate(self):
+ tmpl = """${ "OK" }"""
+ res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context))
+ self.assertEqual(res, "OK")
+
++ @pytest.mark.skipif(
++ not HAS_GENSHI, reason="Genshi module not available for testing"
++ )
+ def test_render_genshi_evaluate_condition(self):
+ tmpl = """OK"""
+ res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context))
+ self.assertEqual(res, "OK")
+
++ @pytest.mark.skipif(
++ not HAS_GENSHI, reason="Genshi module not available for testing"
++ )
+ def test_render_genshi_variable(self):
+ tmpl = """$var"""
+
+@@ -175,6 +206,9 @@ class RenderTestCase(TestCase):
+ res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx)
+ self.assertEqual(res, "OK")
+
++ @pytest.mark.skipif(
++ not HAS_GENSHI, reason="Genshi module not available for testing"
++ )
+ def test_render_genshi_variable_replace(self):
+ tmpl = """not ok"""
+
+--
+2.46.0
+
diff --git a/prevent-using-syncwrapper-with-no-reason.patch b/prevent-using-syncwrapper-with-no-reason.patch
new file mode 100644
index 0000000..a2db38f
--- /dev/null
+++ b/prevent-using-syncwrapper-with-no-reason.patch
@@ -0,0 +1,25 @@
+From 936c298c177a50783b080c445745bedf77050cb0 Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Wed, 25 Sep 2024 14:04:40 +0300
+Subject: [PATCH] Prevent using SyncWrapper with no reason
+
+---
+ salt/channel/server.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/salt/channel/server.py b/salt/channel/server.py
+index b6d51fef08..f1b6f701a9 100644
+--- a/salt/channel/server.py
++++ b/salt/channel/server.py
+@@ -86,7 +86,7 @@ class ReqServerChannel:
+ # other things needed for _auth
+ # Create the event manager
+ self.event = salt.utils.event.get_master_event(
+- self.opts, self.opts["sock_dir"], listen=False
++ self.opts, self.opts["sock_dir"], listen=False, io_loop=io_loop
+ )
+ self.auto_key = salt.daemons.masterapi.AutoKey(self.opts)
+ # only create a con_cache-client if the con_cache is active
+--
+2.46.1
+
diff --git a/remove-redundant-run_func-from-salt.master.mworker._.patch b/remove-redundant-run_func-from-salt.master.mworker._.patch
new file mode 100644
index 0000000..5fd4641
--- /dev/null
+++ b/remove-redundant-run_func-from-salt.master.mworker._.patch
@@ -0,0 +1,224 @@
+From ff789d88541954e4fc1678dff728bc6a3ea7472e Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Fri, 30 Aug 2024 14:30:27 +0200
+Subject: [PATCH] Remove redundant run_func from
+ salt.master.MWorker._handle_aes
+
+* New request context
+
+* Fix docs
+
+* Remove redundant run_func from salt.master.MWorker._handle_aes
+
+* Get rid of run_func in salt.Minion._target
+
+---------
+
+Co-authored-by: Daniel A. Wozniak
+---
+ doc/topics/releases/3007.0.rst | 0
+ salt/_logging/impl.py | 15 ++++++---
+ salt/master.py | 12 ++-----
+ salt/minion.py | 10 ++----
+ salt/utils/ctx.py | 60 +++++++++++-----------------------
+ 5 files changed, 34 insertions(+), 63 deletions(-)
+ create mode 100644 doc/topics/releases/3007.0.rst
+
+diff --git a/doc/topics/releases/3007.0.rst b/doc/topics/releases/3007.0.rst
+new file mode 100644
+index 0000000000..e69de29bb2
+diff --git a/salt/_logging/impl.py b/salt/_logging/impl.py
+index 1d71cb8be8..4d1ebd2495 100644
+--- a/salt/_logging/impl.py
++++ b/salt/_logging/impl.py
+@@ -26,6 +26,8 @@ GARBAGE = logging.GARBAGE = 1
+ QUIET = logging.QUIET = 1000
+
+ import salt.defaults.exitcodes # isort:skip pylint: disable=unused-import
++import salt.utils.ctx
++
+ from salt._logging.handlers import DeferredStreamHandler # isort:skip
+ from salt._logging.handlers import RotatingFileHandler # isort:skip
+ from salt._logging.handlers import StreamHandler # isort:skip
+@@ -33,7 +35,6 @@ from salt._logging.handlers import SysLogHandler # isort:skip
+ from salt._logging.handlers import WatchedFileHandler # isort:skip
+ from salt._logging.mixins import LoggingMixinMeta # isort:skip
+ from salt.exceptions import LoggingRuntimeError # isort:skip
+-from salt.utils.ctx import RequestContext # isort:skip
+ from salt.utils.immutabletypes import freeze, ImmutableDict # isort:skip
+ from salt.utils.textformat import TextFormat # isort:skip
+
+@@ -242,10 +243,14 @@ class SaltLoggingClass(LOGGING_LOGGER_CLASS, metaclass=LoggingMixinMeta):
+ if extra is None:
+ extra = {}
+
+- # pylint: disable=no-member
+- current_jid = RequestContext.current.get("data", {}).get("jid", None)
+- log_fmt_jid = RequestContext.current.get("opts", {}).get("log_fmt_jid", None)
+- # pylint: enable=no-member
++ current_jid = (
++ salt.utils.ctx.get_request_context().get("data", {}).get("jid", None)
++ )
++ log_fmt_jid = (
++ salt.utils.ctx.get_request_context()
++ .get("opts", {})
++ .get("log_fmt_jid", None)
++ )
+
+ if current_jid is not None:
+ extra["jid"] = current_jid
+diff --git a/salt/master.py b/salt/master.py
+index d7182d10b5..49cfb68860 100644
+--- a/salt/master.py
++++ b/salt/master.py
+@@ -38,6 +38,7 @@ import salt.state
+ import salt.utils.args
+ import salt.utils.atomicfile
+ import salt.utils.crypt
++import salt.utils.ctx
+ import salt.utils.event
+ import salt.utils.files
+ import salt.utils.gitfs
+@@ -58,10 +59,8 @@ import salt.wheel
+ from salt.cli.batch_async import BatchAsync, batch_async_required
+ from salt.config import DEFAULT_INTERVAL
+ from salt.defaults import DEFAULT_TARGET_DELIM
+-from salt.ext.tornado.stack_context import StackContext
+ from salt.transport import TRANSPORTS
+ from salt.utils.channel import iter_transport_opts
+-from salt.utils.ctx import RequestContext
+ from salt.utils.debug import (
+ enable_sigusr1_handler,
+ enable_sigusr2_handler,
+@@ -1108,13 +1107,8 @@ class MWorker(salt.utils.process.SignalHandlingProcess):
+ start = time.time()
+ self.stats[cmd]["runs"] += 1
+
+- def run_func(data):
+- return self.aes_funcs.run_func(data["cmd"], data)
+-
+- with StackContext(
+- functools.partial(RequestContext, {"data": data, "opts": self.opts})
+- ):
+- ret = run_func(data)
++ with salt.utils.ctx.request_context({"data": data, "opts": self.opts}):
++ ret = self.aes_funcs.run_func(data["cmd"], data)
+
+ if self.opts["master_stats"]:
+ self._post_stats(start, cmd)
+diff --git a/salt/minion.py b/salt/minion.py
+index 2ccd0cd5a9..e21a017cfd 100644
+--- a/salt/minion.py
++++ b/salt/minion.py
+@@ -39,6 +39,7 @@ import salt.transport
+ import salt.utils.args
+ import salt.utils.context
+ import salt.utils.crypt
++import salt.utils.ctx
+ import salt.utils.data
+ import salt.utils.dictdiffer
+ import salt.utils.dictupdate
+@@ -70,7 +71,6 @@ from salt.exceptions import (
+ SaltSystemExit,
+ )
+ from salt.template import SLS_ENCODING
+-from salt.utils.ctx import RequestContext
+ from salt.utils.debug import enable_sigusr1_handler
+ from salt.utils.event import tagify
+ from salt.utils.network import parse_host_port
+@@ -1805,18 +1805,12 @@ class Minion(MinionBase):
+ uid = salt.utils.user.get_uid(user=opts.get("user", None))
+ minion_instance.proc_dir = get_proc_dir(opts["cachedir"], uid=uid)
+
+- def run_func(minion_instance, opts, data):
++ with salt.utils.ctx.request_context({"data": data, "opts": opts}):
+ if isinstance(data["fun"], tuple) or isinstance(data["fun"], list):
+ return Minion._thread_multi_return(minion_instance, opts, data)
+ else:
+ return Minion._thread_return(minion_instance, opts, data)
+
+- with salt.ext.tornado.stack_context.StackContext(
+- functools.partial(RequestContext, {"data": data, "opts": opts})
+- ):
+- with salt.ext.tornado.stack_context.StackContext(minion_instance.ctx):
+- run_func(minion_instance, opts, data)
+-
+ def _execute_job_function(
+ self, function_name, function_args, executors, opts, data
+ ):
+diff --git a/salt/utils/ctx.py b/salt/utils/ctx.py
+index a9c0931bd8..2f4b5b4c9b 100644
+--- a/salt/utils/ctx.py
++++ b/salt/utils/ctx.py
+@@ -1,49 +1,27 @@
+-import threading
++import contextlib
+
++try:
++ # Try the stdlib C extension first
++ import _contextvars as contextvars
++except ImportError:
++ # Py<3.7
++ import contextvars
+
+-class ClassProperty(property):
+- """
+- Use a classmethod as a property
+- http://stackoverflow.com/a/1383402/1258307
+- """
++DEFAULT_CTX_VAR = "request_ctxvar"
++request_ctxvar = contextvars.ContextVar(DEFAULT_CTX_VAR)
+
+- def __get__(self, cls, owner):
+- return self.fget.__get__(None, owner)() # pylint: disable=no-member
+
+-
+-class RequestContext:
++@contextlib.contextmanager
++def request_context(data):
+ """
+- A context manager that saves some per-thread state globally.
+- Intended for use with Tornado's StackContext.
+- https://gist.github.com/simon-weber/7755289
+- Simply import this class into any module and access the current request handler by this
+- class's class method property 'current'. If it returns None, there's no active request.
+- .. code:: python
+- from raas.utils.ctx import RequestContext
+- current_request_handler = RequestContext.current
++ A context manager that sets and un-sets the loader context
+ """
++ tok = request_ctxvar.set(data)
++ try:
++ yield
++ finally:
++ request_ctxvar.reset(tok)
+
+- _state = threading.local()
+- _state.current_request = {}
+-
+- def __init__(self, current_request):
+- self._current_request = current_request
+-
+- @ClassProperty
+- @classmethod
+- def current(cls):
+- if not hasattr(cls._state, "current_request"):
+- return {}
+- return cls._state.current_request
+-
+- def __enter__(self):
+- self._prev_request = self.__class__.current
+- self.__class__._state.current_request = self._current_request
+-
+- def __exit__(self, *exc):
+- self.__class__._state.current_request = self._prev_request
+- del self._prev_request
+- return False
+
+- def __call__(self):
+- return self
++def get_request_context():
++ return request_ctxvar.get({})
+--
+2.46.0
+
diff --git a/replace-use-of-pygit2-deprecated-and-removed-1.15.0-.patch b/replace-use-of-pygit2-deprecated-and-removed-1.15.0-.patch
new file mode 100644
index 0000000..789a7ea
--- /dev/null
+++ b/replace-use-of-pygit2-deprecated-and-removed-1.15.0-.patch
@@ -0,0 +1,122 @@
+From 3f3c8d80427c9d90bea5fbca785b210260d33a0f Mon Sep 17 00:00:00 2001
+From: Marek Czernek
+Date: Wed, 21 Aug 2024 16:15:02 +0200
+Subject: [PATCH] Replace use of pygit2 deprecated and removed (1.15.0)
+ oid with id (#673)
+
+Co-authored-by: David Murphy
+---
+ salt/utils/gitfs.py | 31 ++++++++++++++++---------------
+ 1 file changed, 16 insertions(+), 15 deletions(-)
+
+diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py
+index 061647edaca..f3902c1f19a 100644
+--- a/salt/utils/gitfs.py
++++ b/salt/utils/gitfs.py
+@@ -1683,7 +1683,7 @@ class Pygit2(GitProvider):
+ # remote ref.
+ self.repo.checkout(checkout_ref)
+ if branch:
+- self.repo.reset(oid, pygit2.GIT_RESET_HARD)
++ self.repo.reset(pygit2_id, pygit2.GIT_RESET_HARD)
+ return True
+ except GitLockError as exc:
+ if exc.errno == errno.EEXIST:
+@@ -1714,11 +1714,11 @@ class Pygit2(GitProvider):
+ tag_ref = "refs/tags/" + tgt_ref
+ if remote_ref in refs:
+ # Get commit id for the remote ref
+- oid = self.peel(self.repo.lookup_reference(remote_ref)).id
++ pygit2_id = self.peel(self.repo.lookup_reference(remote_ref)).id
+ if local_ref not in refs:
+ # No local branch for this remote, so create one and point
+ # it at the commit id of the remote ref
+- self.repo.create_reference(local_ref, oid)
++ self.repo.create_reference(local_ref, pygit2_id)
+
+ try:
+ target_sha = self.peel(self.repo.lookup_reference(remote_ref)).hex
+@@ -1749,7 +1749,8 @@ class Pygit2(GitProvider):
+ # cachedir).
+ head_ref = local_head.target
+ # If head_ref is not a string, it will point to a
+- # pygit2.Oid object and we are in detached HEAD mode.
++ # pygit2.id object (oid is deprecated and removed) and
++ # we are in detached HEAD mode.
+ # Therefore, there is no need to add a local reference. If
+ # head_ref == local_ref, then the local reference for HEAD
+ # in refs/heads/ already exists and again, no need to add.
+@@ -1918,10 +1919,10 @@ class Pygit2(GitProvider):
+ the empty directories within it in the "blobs" list
+ """
+ for entry in iter(tree):
+- if entry.oid not in self.repo:
++ if entry.id not in self.repo:
+ # Entry is a submodule, skip it
+ continue
+- blob = self.repo[entry.oid]
++ blob = self.repo[entry.id]
+ if not isinstance(blob, pygit2.Tree):
+ continue
+ blobs.append(
+@@ -1940,8 +1941,8 @@ class Pygit2(GitProvider):
+ return ret
+ if self.root(tgt_env):
+ try:
+- oid = tree[self.root(tgt_env)].oid
+- tree = self.repo[oid]
++ pygit2_id = tree[self.root(tgt_env)].id
++ tree = self.repo[pygit2_id]
+ except KeyError:
+ return ret
+ if not isinstance(tree, pygit2.Tree):
+@@ -2056,17 +2057,17 @@ class Pygit2(GitProvider):
+ the file paths and symlink info in the "blobs" dict
+ """
+ for entry in iter(tree):
+- if entry.oid not in self.repo:
++ if entry.id not in self.repo:
+ # Entry is a submodule, skip it
+ continue
+- obj = self.repo[entry.oid]
++ obj = self.repo[entry.id]
+ if isinstance(obj, pygit2.Blob):
+ repo_path = salt.utils.path.join(
+ prefix, entry.name, use_posixpath=True
+ )
+ blobs.setdefault("files", []).append(repo_path)
+ if stat.S_ISLNK(tree[entry.name].filemode):
+- link_tgt = self.repo[tree[entry.name].oid].data
++ link_tgt = self.repo[tree[entry.name].id].data
+ blobs.setdefault("symlinks", {})[repo_path] = link_tgt
+ elif isinstance(obj, pygit2.Tree):
+ _traverse(
+@@ -2085,8 +2086,8 @@ class Pygit2(GitProvider):
+ try:
+ # This might need to be changed to account for a root that
+ # spans more than one directory
+- oid = tree[self.root(tgt_env)].oid
+- tree = self.repo[oid]
++ pygit2_id = tree[self.root(tgt_env)].id
++ tree = self.repo[pygit2_id]
+ except KeyError:
+ return files, symlinks
+ if not isinstance(tree, pygit2.Tree):
+@@ -2130,12 +2131,12 @@ class Pygit2(GitProvider):
+ # path's object ID will be the target of the symlink. Follow
+ # the symlink and set path to the location indicated
+ # in the blob data.
+- link_tgt = self.repo[entry.oid].data
++ link_tgt = self.repo[entry.id].data
+ path = salt.utils.path.join(
+ os.path.dirname(path), link_tgt, use_posixpath=True
+ )
+ else:
+- blob = self.repo[entry.oid]
++ blob = self.repo[entry.id]
+ if isinstance(blob, pygit2.Tree):
+ # Path is a directory, not a file.
+ blob = None
+--
+2.46.0
+
diff --git a/revert-the-change-making-reactor-less-blocking-bsc-1.patch b/revert-the-change-making-reactor-less-blocking-bsc-1.patch
new file mode 100644
index 0000000..3212dca
--- /dev/null
+++ b/revert-the-change-making-reactor-less-blocking-bsc-1.patch
@@ -0,0 +1,106 @@
+From c00801d2f9807e49769d0e0d848ec12be555dbc1 Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Wed, 25 Sep 2024 14:07:05 +0300
+Subject: [PATCH] Revert the change making reactor less blocking
+ (bsc#1230322)
+
+This reverts commit 0d35f09288700f5c961567442c3fcc25838b8de4.
+---
+ salt/utils/reactor.py | 45 ++++++++++++++++---------------------------
+ 1 file changed, 17 insertions(+), 28 deletions(-)
+
+diff --git a/salt/utils/reactor.py b/salt/utils/reactor.py
+index 78adad34da..19420a51cf 100644
+--- a/salt/utils/reactor.py
++++ b/salt/utils/reactor.py
+@@ -1,12 +1,10 @@
+ """
+ Functions which implement running reactor jobs
+ """
+-
+ import fnmatch
+ import glob
+ import logging
+ import os
+-from threading import Lock
+
+ import salt.client
+ import salt.defaults.exitcodes
+@@ -196,6 +194,13 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler):
+ self.resolve_aliases(chunks)
+ return chunks
+
++ def call_reactions(self, chunks):
++ """
++ Execute the reaction state
++ """
++ for chunk in chunks:
++ self.wrap.run(chunk)
++
+ def run(self):
+ """
+ Enter into the server loop
+@@ -213,7 +218,7 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler):
+ ) as event:
+ self.wrap = ReactWrap(self.opts)
+
+- for data in event.iter_events(full=True, auto_reconnect=True):
++ for data in event.iter_events(full=True):
+ # skip all events fired by ourselves
+ if data["data"].get("user") == self.wrap.event_user:
+ continue
+@@ -263,9 +268,15 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler):
+ if not self.is_leader:
+ continue
+ else:
+- self.wrap.call_reactions(
+- data, self.list_reactors, self.reactions
+- )
++ reactors = self.list_reactors(data["tag"])
++ if not reactors:
++ continue
++ chunks = self.reactions(data["tag"], data["data"], reactors)
++ if chunks:
++ try:
++ self.call_reactions(chunks)
++ except SystemExit:
++ log.warning("Exit ignored by reactor")
+
+
+ class ReactWrap:
+@@ -286,7 +297,6 @@ class ReactWrap:
+
+ def __init__(self, opts):
+ self.opts = opts
+- self._run_lock = Lock()
+ if ReactWrap.client_cache is None:
+ ReactWrap.client_cache = salt.utils.cache.CacheDict(
+ opts["reactor_refresh_interval"]
+@@ -470,24 +480,3 @@ class ReactWrap:
+ Wrap LocalCaller to execute remote exec functions locally on the Minion
+ """
+ self.client_cache["caller"].cmd(fun, *kwargs["arg"], **kwargs["kwarg"])
+-
+- def _call_reactions(self, data, list_reactors, get_reactions):
+- reactors = list_reactors(data["tag"])
+- if not reactors:
+- return
+- chunks = get_reactions(data["tag"], data["data"], reactors)
+- if not chunks:
+- return
+- with self._run_lock:
+- try:
+- for chunk in chunks:
+- self.run(chunk)
+- except Exception as exc: # pylint: disable=broad-except
+- log.error(
+- "Exception while calling the reactions: %s", exc, exc_info=True
+- )
+-
+- def call_reactions(self, data, list_reactors, get_reactions):
+- return self.pool.fire_async(
+- self._call_reactions, args=(data, list_reactors, get_reactions)
+- )
+--
+2.46.1
+
diff --git a/salt.changes b/salt.changes
index 7ac6ade..1506adb 100644
--- a/salt.changes
+++ b/salt.changes
@@ -1,3 +1,44 @@
+-------------------------------------------------------------------
+Wed Oct 2 12:09:33 UTC 2024 - Yeray Gutiérrez Cedrés
+
+- Fix failing x509 tests with OpenSSL < 1.1
+- Avoid explicit reading of /etc/salt/minion (bsc#1220357)
+- Allow NamedLoaderContexts to be returned from loader
+- Revert the change making reactor less blocking (bsc#1230322)
+- Use --cachedir for extension_modules in salt-call (bsc#1226141)
+- Prevent using SyncWrapper with no reason
+- Fix the SELinux context for Salt Minion service (bsc#1219041)
+- Set contextvars as a build requirement for package
+- Increase warn_until_date date for code we still support
+- The test_debian test now uses port 80 for ubuntu keyserver
+- Fix too frequent systemd service restart in test_system test
+- Avoid crash on wrong output of systemctl version (bsc#1229539)
+- Improve error handling with different OpenSSL versions
+- Remove redundant run_func from salt.master.MWorker._handle_aes
+- Fix cloud minion configuration for multiple masters (bsc#1229109)
+- Use Pygit2 id instead of deprecated oid in gitfs
+- Fix few failing tests to work with both Salt and Salt bundle
+- Skip testing unsupported OpenSSL crypto algorithms
+
+- Added:
+ * skip-more-tests-related-to-old-openssl-algorithms.patch
+ * fix-the-selinux-context-for-salt-minion-service-bsc-.patch
+ * allow-namedloadercontexts-to-be-returned-from-loader.patch
+ * join-masters-if-it-is-a-list-671.patch
+ * remove-redundant-run_func-from-salt.master.mworker._.patch
+ * prevent-using-syncwrapper-with-no-reason.patch
+ * fix-test_debian-to-work-in-our-infrastructure-676.patch
+ * fix-x509-test-fails-on-old-openssl-systems-682.patch
+ * make-tests-compatible-with-venv-bundle.patch
+ * avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch
+ * revert-the-change-making-reactor-less-blocking-bsc-1.patch
+ * avoid-explicit-reading-of-etc-salt-minion-bsc-122035.patch
+ * improve-error-handling-with-different-openssl-versio.patch
+ * replace-use-of-pygit2-deprecated-and-removed-1.15.0-.patch
+ * fix-test_system-flaky-setup_teardown-fn.patch
+ * use-cachedir-for-extension_modules-in-salt-call-bsc-.patch
+ * fix-deprecated-code-677.patch
+
-------------------------------------------------------------------
Fri Aug 2 09:00:07 UTC 2024 - Yeray Gutiérrez Cedrés
diff --git a/salt.spec b/salt.spec
index 374f1f8..ebeeae4 100644
--- a/salt.spec
+++ b/salt.spec
@@ -410,6 +410,44 @@ Patch124: some-more-small-tests-fixes-enhancements-661.patch
Patch125: test_vultrpy-adjust-test-expectation-to-prevent-fail.patch
# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/66698
Patch126: firewalld-normalize-new-rich-rules-before-comparing-.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/669
+Patch127: skip-more-tests-related-to-old-openssl-algorithms.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/662
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66730
+Patch128: make-tests-compatible-with-venv-bundle.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66743
+Patch129: replace-use-of-pygit2-deprecated-and-removed-1.15.0-.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/671
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/64173
+Patch130: join-masters-if-it-is-a-list-671.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66509
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/0c3ebc0795f9c2adec90118281343cae3070e0f6
+Patch131: remove-redundant-run_func-from-salt.master.mworker._.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66818
+Patch132: improve-error-handling-with-different-openssl-versio.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66856
+Patch133: avoid-crash-on-wrong-output-of-systemctl-version-bsc.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66861
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/675
+Patch134: fix-test_system-flaky-setup_teardown-fn.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/676
+Patch135: fix-test_debian-to-work-in-our-infrastructure-676.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/677
+Patch136: fix-deprecated-code-677.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66780
+Patch137: fix-the-selinux-context-for-salt-minion-service-bsc-.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66510
+Patch138: prevent-using-syncwrapper-with-no-reason.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66742
+Patch139: use-cachedir-for-extension_modules-in-salt-call-bsc-.patch
+# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/680
+Patch140: revert-the-change-making-reactor-less-blocking-bsc-1.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66649
+Patch141: allow-namedloadercontexts-to-be-returned-from-loader.patch
+# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/d54407ba6dc664e5e5f3f613e27ae24f828c9648
+Patch142: avoid-explicit-reading-of-etc-salt-minion-bsc-122035.patch
+# PATCH-FIX_UPSTREAM: https://github.com/openSUSE/salt/pull/682
+Patch143: fix-x509-test-fails-on-old-openssl-systems-682.patch
### IMPORTANT: The line below is used as a snippet marker. Do not touch it.
### SALT PATCHES LIST END
@@ -528,6 +566,7 @@ BuildRequires: python3-requests >= 1.0.0
BuildRequires: python3-distro
BuildRequires: python3-looseversion
BuildRequires: python3-packaging
+BuildRequires: python3-contextvars
# requirements/zeromq.txt
%if %{with test}
diff --git a/skip-more-tests-related-to-old-openssl-algorithms.patch b/skip-more-tests-related-to-old-openssl-algorithms.patch
new file mode 100644
index 0000000..485db8a
--- /dev/null
+++ b/skip-more-tests-related-to-old-openssl-algorithms.patch
@@ -0,0 +1,97 @@
+From 63ff8ce775eec43b2f768b72fba4154c7832b1f7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Yeray=20Guti=C3=A9rrez=20Cedr=C3=A9s?=
+
+Date: Wed, 7 Aug 2024 08:54:24 +0100
+Subject: [PATCH] Skip more tests related to old OpenSSL algorithms
+
+* Skip more tests related to old OpenSSL algorithms
+
+* Check the comment from state apply ret instead of exception
+
+---------
+
+Co-authored-by: vzhestkov
+---
+ tests/pytests/functional/modules/test_x509_v2.py | 10 ++++++++--
+ tests/pytests/functional/states/test_x509_v2.py | 9 +++++++++
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/tests/pytests/functional/modules/test_x509_v2.py b/tests/pytests/functional/modules/test_x509_v2.py
+index c060ad2971c..2e8152d04a3 100644
+--- a/tests/pytests/functional/modules/test_x509_v2.py
++++ b/tests/pytests/functional/modules/test_x509_v2.py
+@@ -1400,7 +1400,10 @@ def test_create_csr_raw(x509, rsa_privkey):
+ @pytest.mark.slow_test
+ @pytest.mark.parametrize("algo", ["rsa", "ec", "ed25519", "ed448"])
+ def test_create_private_key(x509, algo):
+- res = x509.create_private_key(algo=algo)
++ try:
++ res = x509.create_private_key(algo=algo)
++ except UnsupportedAlgorithm:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ assert res.startswith("-----BEGIN PRIVATE KEY-----")
+
+
+@@ -1408,7 +1411,10 @@ def test_create_private_key(x509, algo):
+ @pytest.mark.parametrize("algo", ["rsa", "ec", "ed25519", "ed448"])
+ def test_create_private_key_with_passphrase(x509, algo):
+ passphrase = "hunter2"
+- res = x509.create_private_key(algo=algo, passphrase=passphrase)
++ try:
++ res = x509.create_private_key(algo=algo, passphrase=passphrase)
++ except UnsupportedAlgorithm:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ assert res.startswith("-----BEGIN ENCRYPTED PRIVATE KEY-----")
+ # ensure it can be loaded
+ x509.get_private_key_size(res, passphrase=passphrase)
+diff --git a/tests/pytests/functional/states/test_x509_v2.py b/tests/pytests/functional/states/test_x509_v2.py
+index e74bdd73f37..929be014cdb 100644
+--- a/tests/pytests/functional/states/test_x509_v2.py
++++ b/tests/pytests/functional/states/test_x509_v2.py
+@@ -6,6 +6,7 @@ import pytest
+ try:
+ import cryptography
+ import cryptography.x509 as cx509
++ from cryptography.exceptions import UnsupportedAlgorithm
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.hazmat.primitives.asymmetric import ec, ed448, ed25519, rsa
+ from cryptography.hazmat.primitives.serialization import (
+@@ -691,6 +692,8 @@ def existing_csr_exts(x509, csr_args, csr_args_exts, ca_key, rsa_privkey, reques
+ def existing_pk(x509, pk_args, request):
+ pk_args.update(request.param)
+ ret = x509.private_key_managed(**pk_args)
++ if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
++ pytest.skip(f"Algorithm '{pk_args['algo']}' is not supported on this OpenSSL version")
+ _assert_pk_basic(
+ ret,
+ pk_args.get("algo", "rsa"),
+@@ -2140,6 +2143,8 @@ def test_private_key_managed(x509, pk_args, algo, encoding, passphrase):
+ pk_args["encoding"] = encoding
+ pk_args["passphrase"] = passphrase
+ ret = x509.private_key_managed(**pk_args)
++ if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
++ pytest.skip(f"Algorithm '{algo}' is not supported on this OpenSSL version")
+ _assert_pk_basic(ret, algo, encoding, passphrase)
+
+
+@@ -2167,6 +2172,8 @@ def test_private_key_managed_keysize(x509, pk_args, algo, keysize):
+ )
+ def test_private_key_managed_existing(x509, pk_args):
+ ret = x509.private_key_managed(**pk_args)
++ if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
++ pytest.skip(f"Algorithm '{pk_args['algo']}' is not supported on this OpenSSL version")
+ _assert_not_changed(ret)
+
+
+@@ -2194,6 +2201,8 @@ def test_private_key_managed_existing_new_with_passphrase_change(x509, pk_args):
+ def test_private_key_managed_algo_change(x509, pk_args):
+ pk_args["algo"] = "ed25519"
+ ret = x509.private_key_managed(**pk_args)
++ if ret.result == False and "UnsupportedAlgorithm" in ret.comment:
++ pytest.skip("Algorithm 'ed25519' is not supported on this OpenSSL version")
+ _assert_pk_basic(ret, "ed25519")
+
+
+--
+2.46.0
+
diff --git a/use-cachedir-for-extension_modules-in-salt-call-bsc-.patch b/use-cachedir-for-extension_modules-in-salt-call-bsc-.patch
new file mode 100644
index 0000000..859a926
--- /dev/null
+++ b/use-cachedir-for-extension_modules-in-salt-call-bsc-.patch
@@ -0,0 +1,110 @@
+From 2fb453d04b8abe765a964174ae77d57398f2877a Mon Sep 17 00:00:00 2001
+From: Victor Zhestkov
+Date: Wed, 25 Sep 2024 14:06:19 +0300
+Subject: [PATCH] Use --cachedir for extension_modules in salt-call
+ (bsc#1226141)
+
+* Use --cachedir parameter for extension_modules with salt-call
+
+* Add test to check extension_modules value alignment
+---
+ salt/cli/call.py | 11 +++++++-
+ salt/config/__init__.py | 6 +++++
+ tests/pytests/unit/cli/test_salt_call.py | 32 ++++++++++++++++++++++++
+ 3 files changed, 48 insertions(+), 1 deletion(-)
+ create mode 100644 tests/pytests/unit/cli/test_salt_call.py
+
+diff --git a/salt/cli/call.py b/salt/cli/call.py
+index 932dc61681..be3ded77e6 100644
+--- a/salt/cli/call.py
++++ b/salt/cli/call.py
+@@ -3,7 +3,7 @@ import os
+ import salt.cli.caller
+ import salt.defaults.exitcodes
+ import salt.utils.parsers
+-from salt.config import _expand_glob_path
++from salt.config import _expand_glob_path, prepend_root_dir
+
+
+ class SaltCall(salt.utils.parsers.SaltCallOptionParser):
+@@ -37,6 +37,15 @@ class SaltCall(salt.utils.parsers.SaltCallOptionParser):
+ if self.options.master:
+ self.config["master"] = self.options.master
+
++ if self.options.cachedir and self.config.get(
++ "extension_modules"
++ ) == os.path.join(self.config.get("__cachedir"), "extmods"):
++ # Override `extension_modules`, but only in case if it was autogenerated
++ cache_dir = os.path.abspath(self.options.cachedir)
++ self.config["cachedir"] = cache_dir
++ self.config["extension_modules"] = os.path.join(cache_dir, "extmods")
++ prepend_root_dir(self.config, ["cachedir", "extension_modules"])
++
+ caller = salt.cli.caller.Caller.factory(self.config)
+
+ if self.options.doc:
+diff --git a/salt/config/__init__.py b/salt/config/__init__.py
+index 68f2b0f674..b3cd5d85ae 100644
+--- a/salt/config/__init__.py
++++ b/salt/config/__init__.py
+@@ -1,6 +1,7 @@
+ """
+ All salt configuration loading and defaults should be in this module
+ """
++
+ import codecs
+ import glob
+ import logging
+@@ -3841,6 +3842,11 @@ def apply_minion_config(
+ _update_ssl_config(opts)
+ _update_discovery_config(opts)
+
++ # Store original `cachedir` value, before overriding,
++ # to make overriding more accurate.
++ if "__cachedir" not in opts:
++ opts["__cachedir"] = opts["cachedir"]
++
+ return opts
+
+
+diff --git a/tests/pytests/unit/cli/test_salt_call.py b/tests/pytests/unit/cli/test_salt_call.py
+new file mode 100644
+index 0000000000..078f2af70d
+--- /dev/null
++++ b/tests/pytests/unit/cli/test_salt_call.py
+@@ -0,0 +1,32 @@
++import os
++
++from salt.cli.call import SaltCall
++from tests.support.mock import MagicMock, patch
++
++
++def test_passing_cachedir_to_extension_modules(temp_salt_minion):
++ """
++ Test passing `cachedir` CLI parameter to `extension_modules` opts
++ """
++ test_cache_dir = os.path.join(temp_salt_minion.config["root_dir"], "new_cache_tmp")
++ with patch(
++ "sys.argv",
++ [
++ "salt-call",
++ "--local",
++ "--config-dir",
++ temp_salt_minion.config["root_dir"],
++ "--cachedir",
++ test_cache_dir,
++ "test.true",
++ ],
++ ), patch("salt.utils.verify.verify_files", MagicMock()), patch(
++ "salt._logging.impl.setup_logfile_handler", MagicMock()
++ ):
++ salt_call = SaltCall()
++ with patch("salt.cli.caller.Caller.factory", MagicMock()) as caller_mock:
++ salt_call.run()
++ assert salt_call.config["cachedir"] == test_cache_dir
++ assert salt_call.config["extension_modules"] == os.path.join(
++ test_cache_dir, "extmods"
++ )
+--
+2.46.1
+