From ab9c251387f7cef2301fcee2243cd29152a11e3a105c12d3d66bc65d1513a094 Mon Sep 17 00:00:00 2001 From: Alexander Graul Date: Fri, 5 May 2023 09:46:40 +0000 Subject: [PATCH] Accepting request 1085018 from home:agraul:branches:systemsmanagement:saltstack remove leftover patches OBS-URL: https://build.opensuse.org/request/show/1085018 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=211 --- ...etection-for-virtual-grains-bsc-1195.patch | 224 ---- ...name-pkgs-and-diff_attr-parameters-t.patch | 1056 ----------------- ...-nitro-grains-with-upstream-pr-bsc-1.patch | 124 -- ...-compatibility-for-importlib-metadat.patch | 151 --- ...g.installed-pkg_verify-documentation.patch | 51 - detect-module.run-syntax.patch | 28 - ...file.managed-for-follow_symlinks-tru.patch | 50 - ...in-test-mode-with-file-state-module-.patch | 183 --- fix-test_ipc-unit-tests.patch | 37 - ...n_cmp-on-openeuler-systems-and-a-few.patch | 103 -- ...nd-bad-buffering-for-binary-mode-563.patch | 105 -- ...reading-license-files-with-dpkg_lowp.patch | 56 - ...declarations-from-excluded-sls-files.patch | 250 ---- ...characters-while-reading-files-with-.patch | 214 ---- ...t-in-error-message-for-zypperpkg-559.patch | 63 - ...enderer-configurable-other-fixes-532.patch | 414 ------- ...cheloader-use-correct-fileclient-519.patch | 30 - ...e-names-once-with-pkg.installed-remo.patch | 296 ----- ...-lock-is-temporarily-unavailable-547.patch | 297 ----- ...et-for-pip-from-venv_pip_target-envi.patch | 87 -- ...don-t-check-for-cached-pillar-errors.patch | 362 ------ ...e_single-does-not-pass-pillar-none-4.patch | 105 -- 22 files changed, 4286 deletions(-) delete mode 100644 add-amazon-ec2-detection-for-virtual-grains-bsc-1195.patch delete mode 100644 add-support-for-name-pkgs-and-diff_attr-parameters-t.patch delete mode 100644 align-amazon-ec2-nitro-grains-with-upstream-pr-bsc-1.patch delete mode 100644 allow-entrypoint-compatibility-for-importlib-metadat.patch delete mode 100644 clarify-pkg.installed-pkg_verify-documentation.patch delete mode 100644 detect-module.run-syntax.patch delete mode 100644 fix-salt.states.file.managed-for-follow_symlinks-tru.patch delete mode 100644 fix-state.apply-in-test-mode-with-file-state-module-.patch delete mode 100644 fix-test_ipc-unit-tests.patch delete mode 100644 fixes-pkg.version_cmp-on-openeuler-systems-and-a-few.patch delete mode 100644 fopen-workaround-bad-buffering-for-binary-mode-563.patch delete mode 100644 ignore-erros-on-reading-license-files-with-dpkg_lowp.patch delete mode 100644 ignore-extend-declarations-from-excluded-sls-files.patch delete mode 100644 ignore-non-utf8-characters-while-reading-files-with-.patch delete mode 100644 include-stdout-in-error-message-for-zypperpkg-559.patch delete mode 100644 make-pass-renderer-configurable-other-fixes-532.patch delete mode 100644 make-sure-saltcacheloader-use-correct-fileclient-519.patch delete mode 100644 normalize-package-names-once-with-pkg.installed-remo.patch delete mode 100644 retry-if-rpm-lock-is-temporarily-unavailable-547.patch delete mode 100644 set-default-target-for-pip-from-venv_pip_target-envi.patch delete mode 100644 state.apply-don-t-check-for-cached-pillar-errors.patch delete mode 100644 state.orchestrate_single-does-not-pass-pillar-none-4.patch diff --git a/add-amazon-ec2-detection-for-virtual-grains-bsc-1195.patch b/add-amazon-ec2-detection-for-virtual-grains-bsc-1195.patch deleted file mode 100644 index b599881..0000000 --- a/add-amazon-ec2-detection-for-virtual-grains-bsc-1195.patch +++ /dev/null @@ -1,224 +0,0 @@ -From 1434a128559df8183c032af722dc3d187bda148a Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Thu, 1 Sep 2022 14:46:24 +0300 -Subject: [PATCH] Add Amazon EC2 detection for virtual grains - (bsc#1195624) - -* Add ignore_retcode to quiet run functions - -* Implement Amazon EC2 detection for virtual grains - -* Add test for virtual grain detection of Amazon EC2 - -* Also detect the product of Amazon EC2 instance - -* Add changelog entry ---- - changelog/62539.added | 1 + - salt/grains/core.py | 18 ++++ - salt/modules/cmdmod.py | 4 + - tests/pytests/unit/grains/test_core.py | 117 +++++++++++++++++++++++++ - 4 files changed, 140 insertions(+) - create mode 100644 changelog/62539.added - -diff --git a/changelog/62539.added b/changelog/62539.added -new file mode 100644 -index 0000000000..5f402d61c2 ---- /dev/null -+++ b/changelog/62539.added -@@ -0,0 +1 @@ -+Implementation of Amazon EC2 instance detection and setting `virtual_subtype` grain accordingly including the product if possible to identify. -diff --git a/salt/grains/core.py b/salt/grains/core.py -index 23d8b8ea42..047c33ffd3 100644 ---- a/salt/grains/core.py -+++ b/salt/grains/core.py -@@ -1171,6 +1171,24 @@ def _virtual(osdata): - if grains.get("virtual_subtype") and grains["virtual"] == "physical": - grains["virtual"] = "virtual" - -+ # Try to detect if the instance is running on Amazon EC2 -+ if grains["virtual"] in ("qemu", "kvm", "xen"): -+ dmidecode = salt.utils.path.which("dmidecode") -+ if dmidecode: -+ ret = __salt__["cmd.run_all"]( -+ [dmidecode, "-t", "system"], ignore_retcode=True -+ ) -+ output = ret["stdout"] -+ if "Manufacturer: Amazon EC2" in output: -+ grains["virtual_subtype"] = "Amazon EC2" -+ product = re.match( -+ r".*Product Name: ([^\r\n]*).*", output, flags=re.DOTALL -+ ) -+ if product: -+ grains["virtual_subtype"] = "Amazon EC2 ({})".format(product[1]) -+ elif re.match(r".*Version: [^\r\n]+\.amazon.*", output, flags=re.DOTALL): -+ grains["virtual_subtype"] = "Amazon EC2" -+ - for command in failed_commands: - log.info( - "Although '%s' was found in path, the current user " -diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py -index a26220718a..07b6e100d2 100644 ---- a/salt/modules/cmdmod.py -+++ b/salt/modules/cmdmod.py -@@ -932,6 +932,7 @@ def _run_quiet( - success_retcodes=None, - success_stdout=None, - success_stderr=None, -+ ignore_retcode=None, - ): - """ - Helper for running commands quietly for minion startup -@@ -958,6 +959,7 @@ def _run_quiet( - success_retcodes=success_retcodes, - success_stdout=success_stdout, - success_stderr=success_stderr, -+ ignore_retcode=ignore_retcode, - )["stdout"] - - -@@ -980,6 +982,7 @@ def _run_all_quiet( - success_retcodes=None, - success_stdout=None, - success_stderr=None, -+ ignore_retcode=None, - ): - - """ -@@ -1012,6 +1015,7 @@ def _run_all_quiet( - success_retcodes=success_retcodes, - success_stdout=success_stdout, - success_stderr=success_stderr, -+ ignore_retcode=ignore_retcode, - ) - - -diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py -index 5c43dbdb09..7c4ea1f17f 100644 ---- a/tests/pytests/unit/grains/test_core.py -+++ b/tests/pytests/unit/grains/test_core.py -@@ -2823,3 +2823,120 @@ def test_get_server_id(): - - with patch.dict(core.__opts__, {"id": "otherid"}): - assert core.get_server_id() != expected -+ -+ -+@pytest.mark.skip_unless_on_linux -+def test_virtual_set_virtual_ec2(): -+ osdata = {} -+ -+ ( -+ osdata["kernel"], -+ osdata["nodename"], -+ osdata["kernelrelease"], -+ osdata["kernelversion"], -+ osdata["cpuarch"], -+ _, -+ ) = platform.uname() -+ -+ which_mock = MagicMock( -+ side_effect=[ -+ # Check with virt-what -+ "/usr/sbin/virt-what", -+ "/usr/sbin/virt-what", -+ None, -+ "/usr/sbin/dmidecode", -+ # Check with systemd-detect-virt -+ None, -+ "/usr/bin/systemd-detect-virt", -+ None, -+ "/usr/sbin/dmidecode", -+ # Check with systemd-detect-virt when no dmidecode available -+ None, -+ "/usr/bin/systemd-detect-virt", -+ None, -+ None, -+ ] -+ ) -+ cmd_run_all_mock = MagicMock( -+ side_effect=[ -+ # Check with virt-what -+ {"retcode": 0, "stderr": "", "stdout": "xen"}, -+ { -+ "retcode": 0, -+ "stderr": "", -+ "stdout": "\n".join( -+ [ -+ "dmidecode 3.2", -+ "Getting SMBIOS data from sysfs.", -+ "SMBIOS 2.7 present.", -+ "", -+ "Handle 0x0100, DMI type 1, 27 bytes", -+ "System Information", -+ " Manufacturer: Xen", -+ " Product Name: HVM domU", -+ " Version: 4.11.amazon", -+ " Serial Number: 12345678-abcd-4321-dcba-0123456789ab", -+ " UUID: 01234567-dcba-1234-abcd-abcdef012345", -+ " Wake-up Type: Power Switch", -+ " SKU Number: Not Specified", -+ " Family: Not Specified", -+ "", -+ "Handle 0x2000, DMI type 32, 11 bytes", -+ "System Boot Information", -+ " Status: No errors detected", -+ ] -+ ), -+ }, -+ # Check with systemd-detect-virt -+ {"retcode": 0, "stderr": "", "stdout": "kvm"}, -+ { -+ "retcode": 0, -+ "stderr": "", -+ "stdout": "\n".join( -+ [ -+ "dmidecode 3.2", -+ "Getting SMBIOS data from sysfs.", -+ "SMBIOS 2.7 present.", -+ "", -+ "Handle 0x0001, DMI type 1, 27 bytes", -+ "System Information", -+ " Manufacturer: Amazon EC2", -+ " Product Name: m5.large", -+ " Version: Not Specified", -+ " Serial Number: 01234567-dcba-1234-abcd-abcdef012345", -+ " UUID: 12345678-abcd-4321-dcba-0123456789ab", -+ " Wake-up Type: Power Switch", -+ " SKU Number: Not Specified", -+ " Family: Not Specified", -+ ] -+ ), -+ }, -+ # Check with systemd-detect-virt when no dmidecode available -+ {"retcode": 0, "stderr": "", "stdout": "kvm"}, -+ ] -+ ) -+ -+ with patch("salt.utils.path.which", which_mock), patch.dict( -+ core.__salt__, -+ { -+ "cmd.run": salt.modules.cmdmod.run, -+ "cmd.run_all": cmd_run_all_mock, -+ "cmd.retcode": salt.modules.cmdmod.retcode, -+ "smbios.get": salt.modules.smbios.get, -+ }, -+ ): -+ -+ virtual_grains = core._virtual(osdata.copy()) -+ -+ assert virtual_grains["virtual"] == "xen" -+ assert virtual_grains["virtual_subtype"] == "Amazon EC2" -+ -+ virtual_grains = core._virtual(osdata.copy()) -+ -+ assert virtual_grains["virtual"] == "kvm" -+ assert virtual_grains["virtual_subtype"] == "Amazon EC2 (m5.large)" -+ -+ virtual_grains = core._virtual(osdata.copy()) -+ -+ assert virtual_grains["virtual"] == "kvm" -+ assert "virtual_subtype" not in virtual_grains --- -2.37.3 - - diff --git a/add-support-for-name-pkgs-and-diff_attr-parameters-t.patch b/add-support-for-name-pkgs-and-diff_attr-parameters-t.patch deleted file mode 100644 index f6d9d17..0000000 --- a/add-support-for-name-pkgs-and-diff_attr-parameters-t.patch +++ /dev/null @@ -1,1056 +0,0 @@ -From 0165c300ec0f7ac70b274b81a8857c2e6d71552d Mon Sep 17 00:00:00 2001 -From: Alexander Graul -Date: Thu, 7 Jul 2022 11:26:34 +0200 -Subject: [PATCH] Add support for name, pkgs and diff_attr parameters to - zypperpkg.upgrade()/yumpkg.upgrade() - backport 3004 (#538) - -* Migrate zypper.upgrade tests to pytest - -(cherry picked from commit ecce005b543f66198c7ac118966254dd3d60682f) - -* Add names and pkgs parameters to zypper.upgrade - -Fixes https://github.com/saltstack/salt/issues/62030 - -(cherry picked from commit 19ebb40dc4538c983721a8746a201b7f1300c2f7) - -* Don't turn attr="all" into a list - -pkg_resource.format_pkg_list expects its `attr` argument to be either a -list of attributes or the string "all" to indicate all available -attributes should be used for formatting. - -Fixes: https://github.com/saltstack/salt/issues/62032 -(cherry picked from commit 05482da89b91442235d3cc2889e59ac3722a7fae) - -* Add diff_attr parameter to zypper/yum upgrade - -diff_attr works just like it does for pkg.install. Having the -option to return additional attributes can remove the need for a -follow-up list_pkgs call. - -Fixes: https://github.com/saltstack/salt/issues/62031 -(cherry picked from commit 20ffffe3be6c7d94e9cc3338a57bbf5014f33d93) ---- - changelog/62030.fixed | 1 + - changelog/62031.added | 1 + - changelog/62032.fixed | 1 + - salt/modules/yumpkg.py | 7 +- - salt/modules/zypperpkg.py | 76 ++- - tests/pytests/unit/modules/test_zypperpkg.py | 277 ++++++++++- - tests/unit/modules/test_zypperpkg.py | 482 ------------------- - 7 files changed, 355 insertions(+), 490 deletions(-) - create mode 100644 changelog/62030.fixed - create mode 100644 changelog/62031.added - create mode 100644 changelog/62032.fixed - -diff --git a/changelog/62030.fixed b/changelog/62030.fixed -new file mode 100644 -index 0000000000..bd60463606 ---- /dev/null -+++ b/changelog/62030.fixed -@@ -0,0 +1 @@ -+Fix inconsitency regarding name and pkgs parameters between zypperpkg.upgrade() and yumpkg.upgrade() -diff --git a/changelog/62031.added b/changelog/62031.added -new file mode 100644 -index 0000000000..f0b66ff96f ---- /dev/null -+++ b/changelog/62031.added -@@ -0,0 +1 @@ -+Add `diff_attr` parameter to pkg.upgrade() (zypper/yum). -diff --git a/changelog/62032.fixed b/changelog/62032.fixed -new file mode 100644 -index 0000000000..ceb3cc89b9 ---- /dev/null -+++ b/changelog/62032.fixed -@@ -0,0 +1 @@ -+Fix attr=all handling in pkg.list_pkgs() (yum/zypper). -diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py -index f52e084346..fcfc5d4045 100644 ---- a/salt/modules/yumpkg.py -+++ b/salt/modules/yumpkg.py -@@ -735,7 +735,7 @@ def list_pkgs(versions_as_list=False, **kwargs): - return {} - - attr = kwargs.get("attr") -- if attr is not None: -+ if attr is not None and attr != "all": - attr = salt.utils.args.split_input(attr) - - contextkey = "pkg.list_pkgs" -@@ -1844,6 +1844,7 @@ def upgrade( - normalize=True, - minimal=False, - obsoletes=True, -+ diff_attr=None, - **kwargs - ): - """ -@@ -2000,7 +2001,7 @@ def upgrade( - if salt.utils.data.is_true(refresh): - refresh_db(**kwargs) - -- old = list_pkgs() -+ old = list_pkgs(attr=diff_attr) - - targets = [] - if name or pkgs: -@@ -2032,7 +2033,7 @@ def upgrade( - cmd.extend(targets) - result = _call_yum(cmd) - __context__.pop("pkg.list_pkgs", None) -- new = list_pkgs() -+ new = list_pkgs(attr=diff_attr) - ret = salt.utils.data.compare_dicts(old, new) - - if result["retcode"] != 0: -diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py -index 4e3006a8cd..cbfbc4b78d 100644 ---- a/salt/modules/zypperpkg.py -+++ b/salt/modules/zypperpkg.py -@@ -939,7 +939,7 @@ def list_pkgs(versions_as_list=False, root=None, includes=None, **kwargs): - return {} - - attr = kwargs.get("attr") -- if attr is not None: -+ if attr is not None and attr != "all": - attr = salt.utils.args.split_input(attr) - - includes = includes if includes else [] -@@ -1791,6 +1791,8 @@ def install( - - - def upgrade( -+ name=None, -+ pkgs=None, - refresh=True, - dryrun=False, - dist_upgrade=False, -@@ -1800,6 +1802,7 @@ def upgrade( - skip_verify=False, - no_recommends=False, - root=None, -+ diff_attr=None, - **kwargs - ): # pylint: disable=unused-argument - """ -@@ -1819,6 +1822,27 @@ def upgrade( - - Run a full system upgrade, a zypper upgrade - -+ name -+ The name of the package to be installed. Note that this parameter is -+ ignored if ``pkgs`` is passed or if ``dryrun`` is set to True. -+ -+ CLI Example: -+ -+ .. code-block:: bash -+ -+ salt '*' pkg.install name= -+ -+ pkgs -+ A list of packages to install from a software repository. Must be -+ passed as a python list. Note that this parameter is ignored if -+ ``dryrun`` is set to True. -+ -+ CLI Examples: -+ -+ .. code-block:: bash -+ -+ salt '*' pkg.install pkgs='["foo", "bar"]' -+ - refresh - force a refresh if set to True (default). - If set to False it depends on zypper if a refresh is -@@ -1850,6 +1874,24 @@ def upgrade( - root - Operate on a different root directory. - -+ diff_attr: -+ If a list of package attributes is specified, returned value will -+ contain them, eg.:: -+ -+ {'': { -+ 'old': { -+ 'version': '', -+ 'arch': ''}, -+ -+ 'new': { -+ 'version': '', -+ 'arch': ''}}} -+ -+ Valid attributes are: ``epoch``, ``version``, ``release``, ``arch``, -+ ``install_date``, ``install_date_time_t``. -+ -+ If ``all`` is specified, all valid attributes will be returned. -+ - Returns a dictionary containing the changes: - - .. code-block:: python -@@ -1857,11 +1899,27 @@ def upgrade( - {'': {'old': '', - 'new': ''}} - -+ If an attribute list is specified in ``diff_attr``, the dict will also contain -+ any specified attribute, eg.:: -+ -+ .. code-block:: python -+ -+ {'': { -+ 'old': { -+ 'version': '', -+ 'arch': ''}, -+ -+ 'new': { -+ 'version': '', -+ 'arch': ''}}} -+ - CLI Example: - - .. code-block:: bash - - salt '*' pkg.upgrade -+ salt '*' pkg.upgrade name=mypackage -+ salt '*' pkg.upgrade pkgs='["package1", "package2"]' - salt '*' pkg.upgrade dist_upgrade=True fromrepo='["MyRepoName"]' novendorchange=True - salt '*' pkg.upgrade dist_upgrade=True dryrun=True - """ -@@ -1897,12 +1955,24 @@ def upgrade( - allowvendorchange, novendorchange - ).noraise.call(*cmd_update + ["--debug-solver"]) - -- old = list_pkgs(root=root) -+ if not dist_upgrade: -+ if name or pkgs: -+ try: -+ (pkg_params, _) = __salt__["pkg_resource.parse_targets"]( -+ name=name, pkgs=pkgs, sources=None, **kwargs -+ ) -+ if pkg_params: -+ cmd_update.extend(pkg_params.keys()) -+ -+ except MinionError as exc: -+ raise CommandExecutionError(exc) -+ -+ old = list_pkgs(root=root, attr=diff_attr) - __zypper__(systemd_scope=_systemd_scope(), root=root).allow_vendor_change( - allowvendorchange, novendorchange - ).noraise.call(*cmd_update) - _clean_cache() -- new = list_pkgs(root=root) -+ new = list_pkgs(root=root, attr=diff_attr) - ret = salt.utils.data.compare_dicts(old, new) - - if __zypper__.exit_code not in __zypper__.SUCCESS_EXIT_CODES: -diff --git a/tests/pytests/unit/modules/test_zypperpkg.py b/tests/pytests/unit/modules/test_zypperpkg.py -index 351a173b81..84dc7a10b4 100644 ---- a/tests/pytests/unit/modules/test_zypperpkg.py -+++ b/tests/pytests/unit/modules/test_zypperpkg.py -@@ -4,17 +4,31 @@ - - - import os -+import textwrap - - import pytest - import salt.modules.pkg_resource as pkg_resource - import salt.modules.zypperpkg as zypper --from salt.exceptions import SaltInvocationError -+from salt.exceptions import CommandExecutionError, SaltInvocationError - from tests.support.mock import MagicMock, mock_open, patch - - - @pytest.fixture - def configure_loader_modules(): -- return {zypper: {"rpm": None}, pkg_resource: {}} -+ return { -+ zypper: { -+ "rpm": None, -+ "_systemd_scope": MagicMock(return_value=False), -+ "osrelease_info": [15, 3], -+ "__salt__": {"pkg_resource.parse_targets": pkg_resource.parse_targets}, -+ }, -+ pkg_resource: {"__grains__": {"os": "SUSE"}}, -+ } -+ -+ -+@pytest.fixture(autouse=True) -+def fresh_zypper_instance(): -+ zypper.__zypper__ = zypper._Zypper() - - - def test_list_pkgs_no_context(): -@@ -254,3 +268,262 @@ def test_del_repo_key(): - with patch.dict(zypper.__salt__, salt_mock): - assert zypper.del_repo_key(keyid="keyid", root="/mnt") - salt_mock["lowpkg.remove_gpg_key"].assert_called_once_with("keyid", "/mnt") -+ -+@pytest.mark.parametrize( -+ "zypper_version,lowpkg_version_cmp,expected_inst_avc,expected_dup_avc", -+ [ -+ ("0.5", [-1, -1], False, False), -+ ("1.11.34", [0, -1], False, True), -+ ("1.14.8", [0, 0], True, True), -+ ], -+) -+def test_refresh_zypper_flags( -+ zypper_version, lowpkg_version_cmp, expected_inst_avc, expected_dup_avc -+): -+ with patch( -+ "salt.modules.zypperpkg.version", MagicMock(return_value=zypper_version) -+ ), patch.dict( -+ zypper.__salt__, -+ {"lowpkg.version_cmp": MagicMock(side_effect=lowpkg_version_cmp)}, -+ ): -+ _zypper = zypper._Zypper() -+ _zypper.refresh_zypper_flags() -+ assert _zypper.inst_avc == expected_inst_avc -+ assert _zypper.dup_avc == expected_dup_avc -+ -+ -+@pytest.mark.parametrize( -+ "inst_avc,dup_avc,avc,allowvendorchange_param,novendorchange_param,expected", -+ [ -+ # inst_avc = True, dup_avc = True -+ (True, True, False, False, False, True), -+ (True, True, False, True, False, True), -+ (True, True, False, False, True, False), -+ (True, True, False, True, True, True), -+ # inst_avc = False, dup_avc = True -+ (False, True, False, False, False, True), -+ (False, True, False, True, False, True), -+ (False, True, False, False, True, False), -+ (False, True, False, True, True, True), -+ # inst_avc = False, dup_avc = False -+ (False, False, False, False, False, False), -+ (False, False, False, True, False, False), -+ (False, False, False, False, True, False), -+ (False, False, False, True, True, False), -+ ], -+) -+@patch("salt.modules.zypperpkg._Zypper.refresh_zypper_flags", MagicMock()) -+def test_allow_vendor_change( -+ inst_avc, -+ dup_avc, -+ avc, -+ allowvendorchange_param, -+ novendorchange_param, -+ expected, -+): -+ _zypper = zypper._Zypper() -+ _zypper.inst_avc = inst_avc -+ _zypper.dup_avc = dup_avc -+ _zypper.avc = avc -+ _zypper.allow_vendor_change(allowvendorchange_param, novendorchange_param) -+ assert _zypper.avc == expected -+ -+ -+@pytest.mark.parametrize( -+ "package,pre_version,post_version,fromrepo_param,name_param,pkgs_param,diff_attr_param", -+ [ -+ ("vim", "1.1", "1.2", [], "", [], "all"), -+ ("kernel-default", "1.1", "1.1,1.2", ["dummy", "dummy2"], "", [], None), -+ ("vim", "1.1", "1.2", [], "vim", [], None), -+ ], -+) -+@patch.object(zypper, "refresh_db", MagicMock(return_value=True)) -+def test_upgrade( -+ package, -+ pre_version, -+ post_version, -+ fromrepo_param, -+ name_param, -+ pkgs_param, -+ diff_attr_param, -+): -+ with patch( -+ "salt.modules.zypperpkg.__zypper__.noraise.call" -+ ) as zypper_mock, patch.object( -+ zypper, -+ "list_pkgs", -+ MagicMock(side_effect=[{package: pre_version}, {package: post_version}]), -+ ) as list_pkgs_mock: -+ expected_call = ["update", "--auto-agree-with-licenses"] -+ for repo in fromrepo_param: -+ expected_call.extend(["--repo", repo]) -+ -+ if pkgs_param: -+ expected_call.extend(pkgs_param) -+ elif name_param: -+ expected_call.append(name_param) -+ -+ result = zypper.upgrade( -+ name=name_param, -+ pkgs=pkgs_param, -+ fromrepo=fromrepo_param, -+ diff_attr=diff_attr_param, -+ ) -+ zypper_mock.assert_any_call(*expected_call) -+ assert result == {package: {"old": pre_version, "new": post_version}} -+ list_pkgs_mock.assert_any_call(root=None, attr=diff_attr_param) -+ -+ -+@pytest.mark.parametrize( -+ "package,pre_version,post_version,fromrepo_param", -+ [ -+ ("vim", "1.1", "1.2", []), -+ ("emacs", "1.1", "1.2", ["Dummy", "Dummy2"]), -+ ], -+) -+@patch.object(zypper, "refresh_db", MagicMock(return_value=True)) -+def test_dist_upgrade(package, pre_version, post_version, fromrepo_param): -+ with patch( -+ "salt.modules.zypperpkg.__zypper__.noraise.call" -+ ) as zypper_mock, patch.object( -+ zypper, -+ "list_pkgs", -+ MagicMock(side_effect=[{package: pre_version}, {package: post_version}]), -+ ): -+ expected_call = ["dist-upgrade", "--auto-agree-with-licenses"] -+ -+ for repo in fromrepo_param: -+ expected_call.extend(["--from", repo]) -+ -+ result = zypper.upgrade(dist_upgrade=True, fromrepo=fromrepo_param) -+ zypper_mock.assert_any_call(*expected_call) -+ assert result == {package: {"old": pre_version, "new": post_version}} -+ -+ -+@pytest.mark.parametrize( -+ "package,pre_version,post_version,dup_avc,novendorchange_param,allowvendorchange_param,vendor_change", -+ [ -+ # dup_avc = True, both params = default -> no vendor change -+ ("vim", "1.1", "1.2", True, True, False, False), -+ # dup_avc = True, allowvendorchange = True -> vendor change -+ ( -+ "emacs", -+ "1.1", -+ "1.2", -+ True, -+ True, -+ True, -+ True, -+ ), -+ # dup_avc = True, novendorchange = False -> vendor change -+ ("joe", "1.1", "1.2", True, False, False, True), -+ # dup_avc = True, both params = toggled -> vendor change -+ ("kate", "1.1", "1.2", True, False, True, True), -+ # dup_avc = False -> no vendor change -+ ( -+ "gedit", -+ "1.1", -+ "1.2", -+ False, -+ False, -+ True, -+ False -+ ), -+ ], -+) -+@patch.object(zypper, "refresh_db", MagicMock(return_value=True)) -+def test_dist_upgrade_vendorchange( -+ package, -+ pre_version, -+ post_version, -+ dup_avc, -+ novendorchange_param, -+ allowvendorchange_param, -+ vendor_change -+): -+ cmd_run_mock = MagicMock(return_value={"retcode": 0, "stdout": None}) -+ with patch.object( -+ zypper, -+ "list_pkgs", -+ MagicMock(side_effect=[{package: pre_version}, {package: post_version}]), -+ ), patch("salt.modules.zypperpkg.__zypper__.refresh_zypper_flags",), patch.dict( -+ zypper.__salt__, {"cmd.run_all": cmd_run_mock} -+ ): -+ expected_cmd = ["zypper", "--non-interactive", "--no-refresh", "dist-upgrade"] -+ # --allow-vendor-change is injected right after "dist-upgrade" -+ if vendor_change: -+ expected_cmd.append("--allow-vendor-change") -+ expected_cmd.append("--auto-agree-with-licenses") -+ -+ zypper.__zypper__.dup_avc = dup_avc -+ zypper.upgrade( -+ dist_upgrade=True, -+ allowvendorchange=allowvendorchange_param, -+ novendorchange=novendorchange_param, -+ ) -+ cmd_run_mock.assert_any_call( -+ expected_cmd, output_loglevel="trace", python_shell=False, env={} -+ ) -+ -+ -+@pytest.mark.parametrize( -+ "package,pre_version,post_version,fromrepo_param", -+ [ -+ ("vim", "1.1", "1.1", []), -+ ("emacs", "1.1", "1.1", ["Dummy", "Dummy2"]), -+ ], -+) -+@patch.object(zypper, "refresh_db", MagicMock(return_value=True)) -+def test_dist_upgrade_dry_run(package, pre_version, post_version, fromrepo_param): -+ with patch( -+ "salt.modules.zypperpkg.__zypper__.noraise.call" -+ ) as zypper_mock, patch.object( -+ zypper, -+ "list_pkgs", -+ MagicMock(side_effect=[{package: pre_version}, {package: post_version}]), -+ ): -+ expected_call = ["dist-upgrade", "--auto-agree-with-licenses", "--dry-run"] -+ -+ for repo in fromrepo_param: -+ expected_call.extend(["--from", repo]) -+ -+ zypper.upgrade(dist_upgrade=True, dryrun=True, fromrepo=fromrepo_param) -+ zypper_mock.assert_any_call(*expected_call) -+ # dryrun=True causes two calls, one with a trailing --debug-solver flag -+ expected_call.append("--debug-solver") -+ zypper_mock.assert_any_call(*expected_call) -+ -+ -+@patch.object(zypper, "refresh_db", MagicMock(return_value=True)) -+def test_dist_upgrade_failure(): -+ zypper_output = textwrap.dedent( -+ """\ -+ Loading repository data... -+ Reading installed packages... -+ Computing distribution upgrade... -+ Use 'zypper repos' to get the list of defined repositories. -+ Repository 'DUMMY' not found by its alias, number, or URI. -+ """ -+ ) -+ call_spy = MagicMock() -+ zypper_mock = MagicMock() -+ zypper_mock.stdout = zypper_output -+ zypper_mock.stderr = "" -+ zypper_mock.exit_code = 3 -+ zypper_mock.noraise.call = call_spy -+ with patch("salt.modules.zypperpkg.__zypper__", zypper_mock), patch.object( -+ zypper, "list_pkgs", MagicMock(side_effect=[{"vim": 1.1}, {"vim": 1.1}]) -+ ): -+ expected_call = [ -+ "dist-upgrade", -+ "--auto-agree-with-licenses", -+ "--from", -+ "Dummy", -+ ] -+ -+ with pytest.raises(CommandExecutionError) as exc: -+ zypper.upgrade(dist_upgrade=True, fromrepo=["Dummy"]) -+ call_spy.assert_called_with(*expected_call) -+ -+ assert exc.exception.info["changes"] == {} -+ assert exc.exception.info["result"]["stdout"] == zypper_output -diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py -index 39f28f2198..fea6eeb004 100644 ---- a/tests/unit/modules/test_zypperpkg.py -+++ b/tests/unit/modules/test_zypperpkg.py -@@ -610,151 +610,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - {"vim": "7.4.326-2.62", "fakepkg": ""}, - ) - -- def test_upgrade_without_vendor_change(self): -- """ -- Dist-upgrade without vendor change option. -- """ -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), -- ): -- ret = zypper.upgrade(dist_upgrade=True) -- self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}}) -- zypper_mock.assert_any_call( -- "dist-upgrade", "--auto-agree-with-licenses", -- ) -- -- def test_refresh_zypper_flags(self): -- zypper.__zypper__._reset() -- with patch( -- "salt.modules.zypperpkg.version", MagicMock(return_value="0.5") -- ), patch.dict( -- zypper.__salt__, {"lowpkg.version_cmp": MagicMock(side_effect=[-1, -1])} -- ): -- zypper.__zypper__.refresh_zypper_flags() -- assert zypper.__zypper__.inst_avc == False -- assert zypper.__zypper__.dup_avc == False -- with patch( -- "salt.modules.zypperpkg.version", MagicMock(return_value="1.11.34") -- ), patch.dict( -- zypper.__salt__, {"lowpkg.version_cmp": MagicMock(side_effect=[0, -1])} -- ): -- zypper.__zypper__.refresh_zypper_flags() -- assert zypper.__zypper__.inst_avc == False -- assert zypper.__zypper__.dup_avc == True -- with patch( -- "salt.modules.zypperpkg.version", MagicMock(return_value="1.14.8") -- ), patch.dict( -- zypper.__salt__, {"lowpkg.version_cmp": MagicMock(side_effect=[0, 0])} -- ): -- zypper.__zypper__.refresh_zypper_flags() -- assert zypper.__zypper__.inst_avc == True -- assert zypper.__zypper__.dup_avc == True -- -- @patch("salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock()) -- def test_allow_vendor_change_function(self): -- zypper.__zypper__._reset() -- zypper.__zypper__.inst_avc = True -- zypper.__zypper__.dup_avc = True -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(False, False) -- assert zypper.__zypper__.avc == True -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(True, False) -- assert zypper.__zypper__.avc == True -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(False, True) -- assert zypper.__zypper__.avc == False -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(True, True) -- assert zypper.__zypper__.avc == True -- -- zypper.__zypper__._reset() -- zypper.__zypper__.inst_avc = False -- zypper.__zypper__.dup_avc = True -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(False, False) -- assert zypper.__zypper__.avc == True -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(True, False) -- assert zypper.__zypper__.avc == True -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(False, True) -- assert zypper.__zypper__.avc == False -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(True, True) -- assert zypper.__zypper__.avc == True -- -- zypper.__zypper__._reset() -- zypper.__zypper__.inst_avc = False -- zypper.__zypper__.dup_avc = False -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(False, False) -- assert zypper.__zypper__.avc == False -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(True, False) -- assert zypper.__zypper__.avc == False -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(False, True) -- assert zypper.__zypper__.avc == False -- zypper.__zypper__.avc = False -- zypper.__zypper__.allow_vendor_change(True, True) -- assert zypper.__zypper__.avc == False -- -- @patch( -- "salt.utils.environment.get_module_environment", -- MagicMock(return_value={"SALT_RUNNING": "1"}), -- ) -- def test_zypper_call_dist_upgrade_with_avc_true(self): -- cmd_run_mock = MagicMock(return_value={"retcode": 0, "stdout": None}) -- zypper.__zypper__._reset() -- with patch.dict(zypper.__salt__, {"cmd.run_all": cmd_run_mock}), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ), patch("salt.modules.zypperpkg.__zypper__._reset", MagicMock()): -- zypper.__zypper__.dup_avc = True -- zypper.__zypper__.avc = True -- zypper.__zypper__.call("dist-upgrade") -- cmd_run_mock.assert_any_call( -- [ -- "zypper", -- "--non-interactive", -- "--no-refresh", -- "dist-upgrade", -- "--allow-vendor-change", -- ], -- output_loglevel="trace", -- python_shell=False, -- env={"SALT_RUNNING": "1"}, -- ) -- -- @patch( -- "salt.utils.environment.get_module_environment", -- MagicMock(return_value={"SALT_RUNNING": "1"}), -- ) -- def test_zypper_call_dist_upgrade_with_avc_false(self): -- cmd_run_mock = MagicMock(return_value={"retcode": 0, "stdout": None}) -- zypper.__zypper__._reset() -- with patch.dict(zypper.__salt__, {"cmd.run_all": cmd_run_mock}), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ), patch("salt.modules.zypperpkg.__zypper__._reset", MagicMock()): -- zypper.__zypper__.dup_avc = False -- zypper.__zypper__.avc = False -- zypper.__zypper__.call("dist-upgrade") -- cmd_run_mock.assert_any_call( -- ["zypper", "--non-interactive", "--no-refresh", "dist-upgrade",], -- output_loglevel="trace", -- python_shell=False, -- env={"SALT_RUNNING": "1"}, -- ) -- - @patch( - "salt.utils.environment.get_module_environment", - MagicMock(return_value={"SALT_RUNNING": "1"}), -@@ -802,296 +657,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - env={"SALT_RUNNING": "1"}, - ) - -- def test_upgrade_with_novendorchange_true(self): -- """ -- Dist-upgrade without vendor change option. -- """ -- zypper.__zypper__._reset() -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ) as refresh_flags_mock, patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), -- ): -- ret = zypper.upgrade(dist_upgrade=True, novendorchange=True) -- refresh_flags_mock.assert_not_called() -- zypper_mock.assert_any_call( -- "dist-upgrade", "--auto-agree-with-licenses", -- ) -- -- def test_upgrade_with_novendorchange_false(self): -- """ -- Perform dist-upgrade with novendorchange set to False. -- """ -- zypper.__zypper__._reset() -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ), patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- zypper.__zypper__.inst_avc = True -- zypper.__zypper__.dup_avc = True -- with patch.dict( -- zypper.__salt__, -- { -- "pkg_resource.version": MagicMock(return_value="1.15"), -- "lowpkg.version_cmp": MagicMock(return_value=1), -- }, -- ): -- ret = zypper.upgrade( -- dist_upgrade=True, -- dryrun=True, -- fromrepo=["Dummy", "Dummy2"], -- novendorchange=False, -- ) -- assert zypper.__zypper__.avc == True -- -- def test_upgrade_with_allowvendorchange_true(self): -- """ -- Perform dist-upgrade with allowvendorchange set to True. -- """ -- zypper.__zypper__._reset() -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ), patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- with patch.dict( -- zypper.__salt__, -- { -- "pkg_resource.version": MagicMock(return_value="1.15"), -- "lowpkg.version_cmp": MagicMock(return_value=1), -- }, -- ): -- -- zypper.__zypper__.inst_avc = True -- zypper.__zypper__.dup_avc = True -- ret = zypper.upgrade( -- dist_upgrade=True, -- dryrun=True, -- fromrepo=["Dummy", "Dummy2"], -- allowvendorchange=True, -- ) -- assert zypper.__zypper__.avc == True -- -- def test_upgrade_with_allowvendorchange_false(self): -- """ -- Perform dist-upgrade with allowvendorchange set to False. -- """ -- zypper.__zypper__._reset() -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ), patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- with patch.dict( -- zypper.__salt__, -- { -- "pkg_resource.version": MagicMock(return_value="1.15"), -- "lowpkg.version_cmp": MagicMock(return_value=1), -- }, -- ): -- -- zypper.__zypper__.inst_avc = True -- zypper.__zypper__.dup_avc = True -- ret = zypper.upgrade( -- dist_upgrade=True, -- dryrun=True, -- fromrepo=["Dummy", "Dummy2"], -- allowvendorchange=False, -- ) -- assert zypper.__zypper__.avc == False -- -- def test_upgrade_old_zypper(self): -- zypper.__zypper__._reset() -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg.__zypper__.refresh_zypper_flags", MagicMock() -- ) as refresh_flags_mock, patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- with patch.dict( -- zypper.__salt__, -- { -- "pkg_resource.version": MagicMock(return_value="1.11"), -- "lowpkg.version_cmp": MagicMock(return_value=-1), -- }, -- ): -- zypper.__zypper__.inst_avc = False -- zypper.__zypper__.dup_avc = False -- ret = zypper.upgrade( -- dist_upgrade=True, -- dryrun=True, -- fromrepo=["Dummy", "Dummy2"], -- novendorchange=False, -- ) -- zypper.__zypper__.avc = False -- -- def test_upgrade_success(self): -- """ -- Test system upgrade and dist-upgrade success. -- -- :return: -- """ -- with patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- with patch( -- "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() -- ) as zypper_mock: -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), -- ): -- ret = zypper.upgrade() -- self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}}) -- zypper_mock.assert_any_call("update", "--auto-agree-with-licenses") -- -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock( -- side_effect=[ -- {"kernel-default": "1.1"}, -- {"kernel-default": "1.1,1.2"}, -- ] -- ), -- ): -- ret = zypper.upgrade() -- self.assertDictEqual( -- ret, {"kernel-default": {"old": "1.1", "new": "1.1,1.2"}} -- ) -- zypper_mock.assert_any_call("update", "--auto-agree-with-licenses") -- -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1,1.2"}]), -- ): -- ret = zypper.upgrade() -- self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.1,1.2"}}) -- zypper_mock.assert_any_call("update", "--auto-agree-with-licenses") -- -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- ret = zypper.upgrade(dist_upgrade=True, dryrun=True) -- zypper_mock.assert_any_call( -- "dist-upgrade", "--auto-agree-with-licenses", "--dry-run" -- ) -- zypper_mock.assert_any_call( -- "dist-upgrade", -- "--auto-agree-with-licenses", -- "--dry-run", -- "--debug-solver", -- ) -- -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- ret = zypper.upgrade( -- dist_upgrade=False, fromrepo=["Dummy", "Dummy2"], dryrun=False -- ) -- zypper_mock.assert_any_call( -- "update", -- "--auto-agree-with-licenses", -- "--repo", -- "Dummy", -- "--repo", -- "Dummy2", -- ) -- -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- ret = zypper.upgrade( -- dist_upgrade=True, -- dryrun=True, -- fromrepo=["Dummy", "Dummy2"], -- novendorchange=True, -- ) -- zypper_mock.assert_any_call( -- "dist-upgrade", -- "--auto-agree-with-licenses", -- "--dry-run", -- "--from", -- "Dummy", -- "--from", -- "Dummy2", -- ) -- zypper_mock.assert_any_call( -- "dist-upgrade", -- "--auto-agree-with-licenses", -- "--dry-run", -- "--from", -- "Dummy", -- "--from", -- "Dummy2", -- "--debug-solver", -- ) -- -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- ret = zypper.upgrade( -- dist_upgrade=False, fromrepo=["Dummy", "Dummy2"], dryrun=False -- ) -- zypper_mock.assert_any_call( -- "update", -- "--auto-agree-with-licenses", -- "--repo", -- "Dummy", -- "--repo", -- "Dummy2", -- ) -- - def test_upgrade_kernel(self): - """ - Test kernel package upgrade success. -@@ -1136,53 +701,6 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - }, - ) - -- def test_upgrade_failure(self): -- """ -- Test system upgrade failure. -- -- :return: -- """ -- zypper_out = """ --Loading repository data... --Reading installed packages... --Computing distribution upgrade... --Use 'zypper repos' to get the list of defined repositories. --Repository 'DUMMY' not found by its alias, number, or URI. --""" -- -- class FailingZypperDummy: -- def __init__(self): -- self.stdout = zypper_out -- self.stderr = "" -- self.pid = 1234 -- self.exit_code = 555 -- self.noraise = MagicMock() -- self.allow_vendor_change = self -- self.SUCCESS_EXIT_CODES = [0] -- -- def __call__(self, *args, **kwargs): -- return self -- -- with patch( -- "salt.modules.zypperpkg.__zypper__", FailingZypperDummy() -- ) as zypper_mock, patch( -- "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) -- ), patch( -- "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) -- ): -- zypper_mock.noraise.call = MagicMock() -- with patch( -- "salt.modules.zypperpkg.list_pkgs", -- MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), -- ): -- with self.assertRaises(CommandExecutionError) as cmd_exc: -- ret = zypper.upgrade(dist_upgrade=True, fromrepo=["DUMMY"]) -- self.assertEqual(cmd_exc.exception.info["changes"], {}) -- self.assertEqual(cmd_exc.exception.info["result"]["stdout"], zypper_out) -- zypper_mock.noraise.call.assert_called_with( -- "dist-upgrade", "--auto-agree-with-licenses", "--from", "DUMMY", -- ) -- - def test_upgrade_available(self): - """ - Test whether or not an upgrade is available for a given package. --- -2.37.3 - - diff --git a/align-amazon-ec2-nitro-grains-with-upstream-pr-bsc-1.patch b/align-amazon-ec2-nitro-grains-with-upstream-pr-bsc-1.patch deleted file mode 100644 index a56ff4e..0000000 --- a/align-amazon-ec2-nitro-grains-with-upstream-pr-bsc-1.patch +++ /dev/null @@ -1,124 +0,0 @@ -From d1e9af256fa67cd792ce11e6e9c1e24a1fe2054f Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Fri, 28 Oct 2022 13:19:46 +0300 -Subject: [PATCH] Align Amazon EC2 (Nitro) grains with upstream PR - (bsc#1203685) - -* Set virtual to Nitro for Amazon EC2 kvm instances - -* Add few mocks to prevent false failing - -possible in some specific environments - -* Add one more possible test case returning Nitro ---- - salt/grains/core.py | 8 +++++++- - tests/pytests/unit/grains/test_core.py | 27 +++++++++++++++++++++++++- - 2 files changed, 33 insertions(+), 2 deletions(-) - -diff --git a/salt/grains/core.py b/salt/grains/core.py -index 76f3767ddf..f359c07432 100644 ---- a/salt/grains/core.py -+++ b/salt/grains/core.py -@@ -860,6 +860,10 @@ def _virtual(osdata): - grains["virtual"] = "container" - grains["virtual_subtype"] = "LXC" - break -+ elif "amazon" in output: -+ grains["virtual"] = "Nitro" -+ grains["virtual_subtype"] = "Amazon EC2" -+ break - elif command == "virt-what": - for line in output.splitlines(): - if line in ("kvm", "qemu", "uml", "xen"): -@@ -1174,7 +1178,7 @@ def _virtual(osdata): - grains["virtual"] = "virtual" - - # Try to detect if the instance is running on Amazon EC2 -- if grains["virtual"] in ("qemu", "kvm", "xen"): -+ if grains["virtual"] in ("qemu", "kvm", "xen", "amazon"): - dmidecode = salt.utils.path.which("dmidecode") - if dmidecode: - ret = __salt__["cmd.run_all"]( -@@ -1182,6 +1186,8 @@ def _virtual(osdata): - ) - output = ret["stdout"] - if "Manufacturer: Amazon EC2" in output: -+ if grains["virtual"] != "xen": -+ grains["virtual"] = "Nitro" - grains["virtual_subtype"] = "Amazon EC2" - product = re.match( - r".*Product Name: ([^\r\n]*).*", output, flags=re.DOTALL -diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py -index c06cdb2db0..6f3bef69f2 100644 ---- a/tests/pytests/unit/grains/test_core.py -+++ b/tests/pytests/unit/grains/test_core.py -@@ -2888,6 +2888,11 @@ def test_virtual_set_virtual_ec2(): - "/usr/bin/systemd-detect-virt", - None, - None, -+ # Check with systemd-detect-virt returning amazon and no dmidecode available -+ None, -+ "/usr/bin/systemd-detect-virt", -+ None, -+ None, - ] - ) - cmd_run_all_mock = MagicMock( -@@ -2946,9 +2951,22 @@ def test_virtual_set_virtual_ec2(): - }, - # Check with systemd-detect-virt when no dmidecode available - {"retcode": 0, "stderr": "", "stdout": "kvm"}, -+ # Check with systemd-detect-virt returning amazon and no dmidecode available -+ {"retcode": 0, "stderr": "", "stdout": "amazon"}, - ] - ) - -+ def _mock_is_file(filename): -+ if filename in ( -+ "/proc/1/cgroup", -+ "/proc/cpuinfo", -+ "/sys/devices/virtual/dmi/id/product_name", -+ "/proc/xen/xsd_kva", -+ "/proc/xen/capabilities", -+ ): -+ return False -+ return True -+ - with patch("salt.utils.path.which", which_mock), patch.dict( - core.__salt__, - { -@@ -2957,6 +2975,8 @@ def test_virtual_set_virtual_ec2(): - "cmd.retcode": salt.modules.cmdmod.retcode, - "smbios.get": salt.modules.smbios.get, - }, -+ ), patch("os.path.isfile", _mock_is_file), patch( -+ "os.path.isdir", return_value=False - ): - - virtual_grains = core._virtual(osdata.copy()) -@@ -2966,7 +2986,7 @@ def test_virtual_set_virtual_ec2(): - - virtual_grains = core._virtual(osdata.copy()) - -- assert virtual_grains["virtual"] == "kvm" -+ assert virtual_grains["virtual"] == "Nitro" - assert virtual_grains["virtual_subtype"] == "Amazon EC2 (m5.large)" - - virtual_grains = core._virtual(osdata.copy()) -@@ -2974,6 +2994,11 @@ def test_virtual_set_virtual_ec2(): - assert virtual_grains["virtual"] == "kvm" - assert "virtual_subtype" not in virtual_grains - -+ virtual_grains = core._virtual(osdata.copy()) -+ -+ assert virtual_grains["virtual"] == "Nitro" -+ assert virtual_grains["virtual_subtype"] == "Amazon EC2" -+ - - @pytest.mark.skip_on_windows - def test_linux_proc_files_with_non_utf8_chars(): --- -2.37.3 - - diff --git a/allow-entrypoint-compatibility-for-importlib-metadat.patch b/allow-entrypoint-compatibility-for-importlib-metadat.patch deleted file mode 100644 index b4546de..0000000 --- a/allow-entrypoint-compatibility-for-importlib-metadat.patch +++ /dev/null @@ -1,151 +0,0 @@ -From 6dc653b0cf8e6e043e13bea7009ded604ceb7b71 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Thu, 12 Jan 2023 15:43:56 +0000 -Subject: [PATCH] Allow entrypoint compatibility for - importlib-metadata>=5.0.0 (#572) - -add tests and make sure the compat code is in an else :) - -changelog - -switch to try/except - -Co-authored-by: MKLeb ---- - changelog/62854.fixed | 1 + - salt/utils/entrypoints.py | 15 +++-- - .../pytests/functional/loader/test_loader.py | 67 +++++++++++++++++-- - 3 files changed, 74 insertions(+), 9 deletions(-) - create mode 100644 changelog/62854.fixed - -diff --git a/changelog/62854.fixed b/changelog/62854.fixed -new file mode 100644 -index 0000000000..13e6df4fe3 ---- /dev/null -+++ b/changelog/62854.fixed -@@ -0,0 +1 @@ -+Use select instead of iterating over entrypoints as a dictionary for importlib_metadata>=5.0.0 -diff --git a/salt/utils/entrypoints.py b/salt/utils/entrypoints.py -index 3effa0b494..9452878ade 100644 ---- a/salt/utils/entrypoints.py -+++ b/salt/utils/entrypoints.py -@@ -38,13 +38,20 @@ def iter_entry_points(group, name=None): - entry_points_listing = [] - entry_points = importlib_metadata.entry_points() - -- for entry_point_group, entry_points_list in entry_points.items(): -- if entry_point_group != group: -- continue -- for entry_point in entry_points_list: -+ try: -+ for entry_point in entry_points.select(group=group): - if name is not None and entry_point.name != name: - continue - entry_points_listing.append(entry_point) -+ except AttributeError: -+ # importlib-metadata<5.0.0 -+ for entry_point_group, entry_points_list in entry_points.items(): -+ if entry_point_group != group: -+ continue -+ for entry_point in entry_points_list: -+ if name is not None and entry_point.name != name: -+ continue -+ entry_points_listing.append(entry_point) - - return entry_points_listing - -diff --git a/tests/pytests/functional/loader/test_loader.py b/tests/pytests/functional/loader/test_loader.py -index 6dfd97b0e6..a13d90d5eb 100644 ---- a/tests/pytests/functional/loader/test_loader.py -+++ b/tests/pytests/functional/loader/test_loader.py -@@ -1,5 +1,4 @@ - import json --import sys - - import pytest - import salt.utils.versions -@@ -143,10 +142,6 @@ def test_utils_loader_does_not_load_extensions( - assert "foobar.echo" not in loader_functions - - --@pytest.mark.skipif( -- sys.version_info < (3, 6), -- reason="importlib-metadata>=3.3.0 does not exist for Py3.5", --) - def test_extension_discovery_without_reload_with_importlib_metadata_installed( - venv, salt_extension, salt_minion_factory - ): -@@ -209,6 +204,68 @@ def test_extension_discovery_without_reload_with_importlib_metadata_installed( - assert "foobar.echo2" in loader_functions - - -+def test_extension_discovery_without_reload_with_importlib_metadata_5_installed( -+ venv, salt_extension, salt_minion_factory -+): -+ # Install our extension into the virtualenv -+ installed_packages = venv.get_installed_packages() -+ assert salt_extension.name not in installed_packages -+ venv.install("importlib-metadata>=3.3.0") -+ code = """ -+ import sys -+ import json -+ import subprocess -+ import salt._logging -+ import salt.loader -+ -+ extension_path = "{}" -+ -+ minion_config = json.loads(sys.stdin.read()) -+ salt._logging.set_logging_options_dict(minion_config) -+ salt._logging.setup_logging() -+ loader = salt.loader.minion_mods(minion_config) -+ -+ if "foobar.echo1" in loader: -+ sys.exit(1) -+ -+ # Install the extension -+ proc = subprocess.run( -+ [sys.executable, "-m", "pip", "install", extension_path], -+ check=False, -+ shell=False, -+ stdout=subprocess.PIPE, -+ ) -+ if proc.returncode != 0: -+ sys.exit(2) -+ -+ loader = salt.loader.minion_mods(minion_config) -+ if "foobar.echo1" not in loader: -+ sys.exit(3) -+ -+ print(json.dumps(list(loader))) -+ """.format( -+ salt_extension.srcdir -+ ) -+ ret = venv.run_code( -+ code, input=json.dumps(salt_minion_factory.config.copy()), check=False -+ ) -+ # Exitcode 1 - Extension was already installed -+ # Exitcode 2 - Failed to install the extension -+ # Exitcode 3 - Extension was not found within the same python process after being installed -+ assert ret.returncode == 0 -+ installed_packages = venv.get_installed_packages() -+ assert salt_extension.name in installed_packages -+ -+ loader_functions = json.loads(ret.stdout) -+ -+ # A non existing module should not appear in the loader -+ assert "monty.python" not in loader_functions -+ -+ # But our extension's modules should appear on the loader -+ assert "foobar.echo1" in loader_functions -+ assert "foobar.echo2" in loader_functions -+ -+ - def test_extension_discovery_without_reload_with_bundled_importlib_metadata( - venv, salt_extension, salt_minion_factory - ): --- -2.37.3 - - diff --git a/clarify-pkg.installed-pkg_verify-documentation.patch b/clarify-pkg.installed-pkg_verify-documentation.patch deleted file mode 100644 index 90f9a24..0000000 --- a/clarify-pkg.installed-pkg_verify-documentation.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 5ed2295489fc13e48b981c323c846bde927cb800 Mon Sep 17 00:00:00 2001 -From: Alexander Graul -Date: Fri, 21 Oct 2022 14:39:21 +0200 -Subject: [PATCH] Clarify pkg.installed pkg_verify documentation - -There have been misunderstandings what the pkg_verify parameter does and -bug reports that it does not work, based on the wrong assumption that -this parameter changes the installation of new packages. The docstring -also stated that it was only provided by `yum`, but `zypper` also -provides this feature (actually it is `rpm` itself in both cases that -does the verification check) - -Related issue: https://github.com/saltstack/salt/issues/44878 - -(cherry picked from commit 2ed5f3c29d3b4313d904b7c081e5a29bf5e309c7) ---- - salt/states/pkg.py | 17 +++++++++-------- - 1 file changed, 9 insertions(+), 8 deletions(-) - -diff --git a/salt/states/pkg.py b/salt/states/pkg.py -index cda966a1e8..13532521d5 100644 ---- a/salt/states/pkg.py -+++ b/salt/states/pkg.py -@@ -1277,14 +1277,15 @@ def installed( - - .. versionadded:: 2014.7.0 - -- For requested packages that are already installed and would not be -- targeted for upgrade or downgrade, use pkg.verify to determine if any -- of the files installed by the package have been altered. If files have -- been altered, the reinstall option of pkg.install is used to force a -- reinstall. Types to ignore can be passed to pkg.verify. Additionally, -- ``verify_options`` can be used to modify further the behavior of -- pkg.verify. See examples below. Currently, this option is supported -- for the following pkg providers: :mod:`yumpkg `. -+ Use pkg.verify to check if already installed packages require -+ reinstallion. Requested packages that are already installed and not -+ targeted for up- or downgrade are verified with pkg.verify to determine -+ if any file installed by the package have been modified or if package -+ dependencies are not fulfilled. ``ignore_types`` and ``verify_options`` -+ can be passed to pkg.verify. See examples below. Currently, this option -+ is supported for the following pkg providers: -+ :mod:`yum `, -+ :mod:`zypperpkg `. - - Examples: - --- -2.37.3 - - diff --git a/detect-module.run-syntax.patch b/detect-module.run-syntax.patch deleted file mode 100644 index e7f58ae..0000000 --- a/detect-module.run-syntax.patch +++ /dev/null @@ -1,28 +0,0 @@ -From dd147ab110e71ea0f1091923c9230ade01f226d4 Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Fri, 28 Oct 2022 13:19:23 +0300 -Subject: [PATCH] Detect module.run syntax - -* Detect module run syntax version - -* Update module.run docs and add changelog - -* Add test for module.run without any args - -Co-authored-by: Daniel A. Wozniak ---- - changelog/58763.fixed | 1 + - 1 file changed, 1 insertion(+) - create mode 100644 changelog/58763.fixed - -diff --git a/changelog/58763.fixed b/changelog/58763.fixed -new file mode 100644 -index 0000000000..53ee8304c0 ---- /dev/null -+++ b/changelog/58763.fixed -@@ -0,0 +1 @@ -+Detect new and legacy styles of calling module.run and support them both. --- -2.37.3 - - diff --git a/fix-salt.states.file.managed-for-follow_symlinks-tru.patch b/fix-salt.states.file.managed-for-follow_symlinks-tru.patch deleted file mode 100644 index 661c70c..0000000 --- a/fix-salt.states.file.managed-for-follow_symlinks-tru.patch +++ /dev/null @@ -1,50 +0,0 @@ -From e328d2029c93153c519e10e9596c635f6f3febcf Mon Sep 17 00:00:00 2001 -From: Petr Pavlu <31453820+petrpavlu@users.noreply.github.com> -Date: Fri, 8 Jul 2022 10:11:52 +0200 -Subject: [PATCH] Fix salt.states.file.managed() for follow_symlinks=True - and test=True (bsc#1199372) (#535) - -When managing file /etc/test as follows: -> file /etc/test: -> file.managed: -> - name: /etc/test -> - source: salt://config/test -> - mode: 644 -> - follow_symlinks: True - -and with /etc/test being a symlink to a different file, an invocation of -"salt-call '*' state.apply test=True" can report that the file should be -updated even when a subsequent run of the same command without the test -parameter makes no changes. - -The problem is that the test code path doesn't take correctly into -account the follow_symlinks=True setting and ends up comparing -permissions of the symlink instead of its target file. - -The patch addresses the problem by extending functions -salt.modules.file.check_managed(), check_managed_changes() and -check_file_meta() to have the follow_symlinks parameter which gets -propagated to the salt.modules.file.stats() call and by updating -salt.states.file.managed() to forward the same parameter to -salt.modules.file.check_managed_changes(). - -Fixes #62066. - -[Cherry-picked from upstream commit -95bfbe31a2dc54723af3f1783d40de152760fe1a.] ---- - changelog/62066.fixed | 1 + - 1 file changed, 1 insertion(+) - create mode 100644 changelog/62066.fixed - -diff --git a/changelog/62066.fixed b/changelog/62066.fixed -new file mode 100644 -index 0000000000..68216a03c1 ---- /dev/null -+++ b/changelog/62066.fixed -@@ -0,0 +1 @@ -+Fixed salt.states.file.managed() for follow_symlinks=True and test=True --- -2.37.3 - - diff --git a/fix-state.apply-in-test-mode-with-file-state-module-.patch b/fix-state.apply-in-test-mode-with-file-state-module-.patch deleted file mode 100644 index 85a7e84..0000000 --- a/fix-state.apply-in-test-mode-with-file-state-module-.patch +++ /dev/null @@ -1,183 +0,0 @@ -From 58317cda7a347581b495ab7fd71ce75f0740d8d6 Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Thu, 1 Sep 2022 14:44:26 +0300 -Subject: [PATCH] Fix state.apply in test mode with file state module on - user/group checking (bsc#1202167) - -* Do not fail on checking user/group in test mode - -* fixes saltstack/salt#61846 reporting of errors in test mode - -Co-authored-by: nicholasmhughes - -* Add tests for _check_user usage - -Co-authored-by: nicholasmhughes ---- - changelog/61846.fixed | 1 + - salt/states/file.py | 5 +++ - tests/pytests/unit/states/file/test_copy.py | 35 ++++++++++++++++ - .../unit/states/file/test_filestate.py | 42 +++++++++++++++++++ - .../pytests/unit/states/file/test_managed.py | 31 ++++++++++++++ - 5 files changed, 114 insertions(+) - create mode 100644 changelog/61846.fixed - -diff --git a/changelog/61846.fixed b/changelog/61846.fixed -new file mode 100644 -index 0000000000..c4024efe9f ---- /dev/null -+++ b/changelog/61846.fixed -@@ -0,0 +1 @@ -+Fix the reporting of errors for file.directory in test mode -diff --git a/salt/states/file.py b/salt/states/file.py -index 1083bb46d6..5cb58f5454 100644 ---- a/salt/states/file.py -+++ b/salt/states/file.py -@@ -379,6 +379,11 @@ def _check_user(user, group): - gid = __salt__["file.group_to_gid"](group) - if gid == "": - err += "Group {} is not available".format(group) -+ if err and __opts__["test"]: -+ # Write the warning with error message, but prevent failing, -+ # in case of applying the state in test mode. -+ log.warning(err) -+ return "" - return err - - -diff --git a/tests/pytests/unit/states/file/test_copy.py b/tests/pytests/unit/states/file/test_copy.py -index ce7161f02d..a11adf5ae0 100644 ---- a/tests/pytests/unit/states/file/test_copy.py -+++ b/tests/pytests/unit/states/file/test_copy.py -@@ -205,3 +205,38 @@ def test_copy(tmp_path): - ) - res = filestate.copy_(name, source, group=group, preserve=False) - assert res == ret -+ -+ -+def test_copy_test_mode_user_group_not_present(): -+ """ -+ Test file copy in test mode with no user or group existing -+ """ -+ source = "/tmp/src_copy_no_user_group_test_mode" -+ filename = "/tmp/copy_no_user_group_test_mode" -+ with patch.dict( -+ filestate.__salt__, -+ { -+ "file.group_to_gid": MagicMock(side_effect=["1234", "", ""]), -+ "file.user_to_uid": MagicMock(side_effect=["", "4321", ""]), -+ "file.get_mode": MagicMock(return_value="0644"), -+ }, -+ ), patch.dict(filestate.__opts__, {"test": True}), patch.object( -+ os.path, "exists", return_value=True -+ ): -+ ret = filestate.copy_( -+ source, filename, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -+ -+ ret = filestate.copy_( -+ source, filename, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -+ -+ ret = filestate.copy_( -+ source, filename, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -diff --git a/tests/pytests/unit/states/file/test_filestate.py b/tests/pytests/unit/states/file/test_filestate.py -index 2f9f369fb2..c373cb3449 100644 ---- a/tests/pytests/unit/states/file/test_filestate.py -+++ b/tests/pytests/unit/states/file/test_filestate.py -@@ -577,3 +577,45 @@ def test_mod_run_check_cmd(): - assert filestate.mod_run_check_cmd(cmd, filename) == ret - - assert filestate.mod_run_check_cmd(cmd, filename) -+ -+ -+def test_recurse_test_mode_user_group_not_present(): -+ """ -+ Test file recurse in test mode with no user or group existing -+ """ -+ filename = "/tmp/recurse_no_user_group_test_mode" -+ source = "salt://tmp/src_recurse_no_user_group_test_mode" -+ mock_l = MagicMock(return_value=[]) -+ mock_emt = MagicMock(return_value=["tmp/src_recurse_no_user_group_test_mode"]) -+ with patch.dict( -+ filestate.__salt__, -+ { -+ "file.group_to_gid": MagicMock(side_effect=["1234", "", ""]), -+ "file.user_to_uid": MagicMock(side_effect=["", "4321", ""]), -+ "file.get_mode": MagicMock(return_value="0644"), -+ "file.source_list": MagicMock(return_value=[source, ""]), -+ "cp.list_master_dirs": mock_emt, -+ "cp.list_master": mock_l, -+ }, -+ ), patch.dict(filestate.__opts__, {"test": True}), patch.object( -+ os.path, "exists", return_value=True -+ ), patch.object( -+ os.path, "isdir", return_value=True -+ ): -+ ret = filestate.recurse( -+ filename, source, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -+ -+ ret = filestate.recurse( -+ filename, source, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -+ -+ ret = filestate.recurse( -+ filename, source, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -diff --git a/tests/pytests/unit/states/file/test_managed.py b/tests/pytests/unit/states/file/test_managed.py -index 9d9fb17717..0b341e09a9 100644 ---- a/tests/pytests/unit/states/file/test_managed.py -+++ b/tests/pytests/unit/states/file/test_managed.py -@@ -373,3 +373,34 @@ def test_managed(): - filestate.managed(name, user=user, group=group) - == ret - ) -+ -+ -+def test_managed_test_mode_user_group_not_present(): -+ """ -+ Test file managed in test mode with no user or group existing -+ """ -+ filename = "/tmp/managed_no_user_group_test_mode" -+ with patch.dict( -+ filestate.__salt__, -+ { -+ "file.group_to_gid": MagicMock(side_effect=["1234", "", ""]), -+ "file.user_to_uid": MagicMock(side_effect=["", "4321", ""]), -+ }, -+ ), patch.dict(filestate.__opts__, {"test": True}): -+ ret = filestate.managed( -+ filename, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -+ -+ ret = filestate.managed( -+ filename, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] -+ -+ ret = filestate.managed( -+ filename, group="nonexistinggroup", user="nonexistinguser" -+ ) -+ assert ret["result"] is not False -+ assert "is not available" not in ret["comment"] --- -2.37.3 - - diff --git a/fix-test_ipc-unit-tests.patch b/fix-test_ipc-unit-tests.patch deleted file mode 100644 index 5a24a11..0000000 --- a/fix-test_ipc-unit-tests.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 4cc528dadfbffdeb90df41bbd848d0c2c7efec78 Mon Sep 17 00:00:00 2001 -From: Alexander Graul -Date: Tue, 12 Jul 2022 14:02:58 +0200 -Subject: [PATCH] Fix test_ipc unit tests - ---- - tests/unit/transport/test_ipc.py | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/tests/unit/transport/test_ipc.py b/tests/unit/transport/test_ipc.py -index 4a0a7c29e2..af001d9650 100644 ---- a/tests/unit/transport/test_ipc.py -+++ b/tests/unit/transport/test_ipc.py -@@ -105,8 +105,8 @@ class IPCMessagePubSubCase(salt.ext.tornado.testing.AsyncTestCase): - self.stop() - - # Now let both waiting data at once -- client1.read_async(handler) -- client2.read_async(handler) -+ client1.read_async() -+ client2.read_async() - self.pub_channel.publish("TEST") - self.wait() - self.assertEqual(len(call_cnt), 2) -@@ -148,7 +148,7 @@ class IPCMessagePubSubCase(salt.ext.tornado.testing.AsyncTestCase): - pass - - try: -- ret1 = yield client1.read_async(handler) -+ ret1 = yield client1.read_async() - self.wait() - except StreamClosedError as ex: - assert False, "StreamClosedError was raised inside the Future" --- -2.37.3 - - diff --git a/fixes-pkg.version_cmp-on-openeuler-systems-and-a-few.patch b/fixes-pkg.version_cmp-on-openeuler-systems-and-a-few.patch deleted file mode 100644 index 3d66c74..0000000 --- a/fixes-pkg.version_cmp-on-openeuler-systems-and-a-few.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 62b3e3491f283a5b5ac243e1f5904ad17e0353bc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Ra=C3=BAl=20Osuna?= - <17827278+raulillo82@users.noreply.github.com> -Date: Tue, 31 Jan 2023 13:13:32 +0100 -Subject: [PATCH] Fixes pkg.version_cmp on openEuler systems and a few - other OS flavors (#576) - -* Fix bug/issue #540 - -* Fix bug/issue #540 - -* Add tests for __virtual__ function - -* Minor changes to align with upstream ---- - changelog/540.fixed | 1 + - salt/modules/rpm_lowpkg.py | 12 +++++-- - tests/pytests/unit/modules/test_rpm_lowpkg.py | 31 +++++++++++++++++++ - 3 files changed, 42 insertions(+), 2 deletions(-) - create mode 100644 changelog/540.fixed - -diff --git a/changelog/540.fixed b/changelog/540.fixed -new file mode 100644 -index 0000000000..50cb42bf40 ---- /dev/null -+++ b/changelog/540.fixed -@@ -0,0 +1 @@ -+Fix pkg.version_cmp on openEuler and a few other os flavors. -diff --git a/salt/modules/rpm_lowpkg.py b/salt/modules/rpm_lowpkg.py -index c8e984c021..01cd575bc7 100644 ---- a/salt/modules/rpm_lowpkg.py -+++ b/salt/modules/rpm_lowpkg.py -@@ -62,14 +62,22 @@ def __virtual__(): - " grains.", - ) - -- enabled = ("amazon", "xcp", "xenserver", "virtuozzolinux") -+ enabled = ( -+ "amazon", -+ "xcp", -+ "xenserver", -+ "virtuozzolinux", -+ "virtuozzo", -+ "issabel pbx", -+ "openeuler", -+ ) - - if os_family in ["redhat", "suse"] or os_grain in enabled: - return __virtualname__ - return ( - False, - "The rpm execution module failed to load: only available on redhat/suse type" -- " systems or amazon, xcp or xenserver.", -+ " systems or amazon, xcp, xenserver, virtuozzolinux, virtuozzo, issabel pbx or openeuler.", - ) - - -diff --git a/tests/pytests/unit/modules/test_rpm_lowpkg.py b/tests/pytests/unit/modules/test_rpm_lowpkg.py -index f19afa854e..e07f71eb61 100644 ---- a/tests/pytests/unit/modules/test_rpm_lowpkg.py -+++ b/tests/pytests/unit/modules/test_rpm_lowpkg.py -@@ -35,6 +35,37 @@ def _called_with_root(mock): - def configure_loader_modules(): - return {rpm: {"rpm": MagicMock(return_value=MagicMock)}} - -+def test___virtual___openeuler(): -+ patch_which = patch("salt.utils.path.which", return_value=True) -+ with patch.dict( -+ rpm.__grains__, {"os": "openEuler", "os_family": "openEuler"} -+ ), patch_which: -+ assert rpm.__virtual__() == "lowpkg" -+ -+ -+def test___virtual___issabel_pbx(): -+ patch_which = patch("salt.utils.path.which", return_value=True) -+ with patch.dict( -+ rpm.__grains__, {"os": "Issabel Pbx", "os_family": "IssabeL PBX"} -+ ), patch_which: -+ assert rpm.__virtual__() == "lowpkg" -+ -+ -+def test___virtual___virtuozzo(): -+ patch_which = patch("salt.utils.path.which", return_value=True) -+ with patch.dict( -+ rpm.__grains__, {"os": "virtuozzo", "os_family": "VirtuoZZO"} -+ ), patch_which: -+ assert rpm.__virtual__() == "lowpkg" -+ -+ -+def test___virtual___with_no_rpm(): -+ patch_which = patch("salt.utils.path.which", return_value=False) -+ ret = rpm.__virtual__() -+ assert isinstance(ret, tuple) -+ assert ret[0] is False -+ -+ - - # 'list_pkgs' function tests: 2 - --- -2.37.3 - - diff --git a/fopen-workaround-bad-buffering-for-binary-mode-563.patch b/fopen-workaround-bad-buffering-for-binary-mode-563.patch deleted file mode 100644 index e68ddd2..0000000 --- a/fopen-workaround-bad-buffering-for-binary-mode-563.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 7d5b1d2178d0573f137b9481ded85419a36998ff Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Thu, 6 Oct 2022 10:55:50 +0100 -Subject: [PATCH] fopen: Workaround bad buffering for binary mode (#563) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -A lot of code assumes Python 2.x behavior for buffering, in which 1 is a -special value meaning line buffered. - -Python 3 makes this value unusable, so fallback to the default buffering -size, and report these calls to be fixed. - -Fixes: https://github.com/saltstack/salt/issues/57584 - -Do not drop buffering from kwargs to avoid errors - -Add unit test around linebuffering in binary mode - -Add changelog file - -Co-authored-by: Pablo Suárez Hernández - -Co-authored-by: Ismael Luceno ---- - changelog/62817.fixed | 1 + - salt/utils/files.py | 8 ++++++++ - tests/pytests/unit/utils/test_files.py | 13 ++++++++++++- - 3 files changed, 21 insertions(+), 1 deletion(-) - create mode 100644 changelog/62817.fixed - -diff --git a/changelog/62817.fixed b/changelog/62817.fixed -new file mode 100644 -index 0000000000..ff335f2916 ---- /dev/null -+++ b/changelog/62817.fixed -@@ -0,0 +1 @@ -+Prevent annoying RuntimeWarning message about line buffering (buffering=1) not being supported in binary mode -diff --git a/salt/utils/files.py b/salt/utils/files.py -index 1cf636a753..3c57cce713 100644 ---- a/salt/utils/files.py -+++ b/salt/utils/files.py -@@ -6,6 +6,7 @@ Functions for working with files - import codecs - import contextlib - import errno -+import io - import logging - import os - import re -@@ -382,6 +383,13 @@ def fopen(*args, **kwargs): - if not binary and not kwargs.get("newline", None): - kwargs["newline"] = "" - -+ # Workaround callers with bad buffering setting for binary files -+ if kwargs.get("buffering") == 1 and "b" in kwargs.get("mode", ""): -+ log.debug( -+ "Line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used" -+ ) -+ kwargs["buffering"] = io.DEFAULT_BUFFER_SIZE -+ - f_handle = open(*args, **kwargs) # pylint: disable=resource-leakage - - if is_fcntl_available(): -diff --git a/tests/pytests/unit/utils/test_files.py b/tests/pytests/unit/utils/test_files.py -index fd88167b16..bd18bc5750 100644 ---- a/tests/pytests/unit/utils/test_files.py -+++ b/tests/pytests/unit/utils/test_files.py -@@ -4,11 +4,12 @@ Unit Tests for functions located in salt/utils/files.py - - - import copy -+import io - import os - - import pytest - import salt.utils.files --from tests.support.mock import patch -+from tests.support.mock import MagicMock, patch - - - def test_safe_rm(): -@@ -74,6 +75,16 @@ def test_fopen_with_disallowed_fds(): - ) - - -+def test_fopen_binary_line_buffering(tmp_path): -+ tmp_file = os.path.join(tmp_path, "foobar") -+ with patch("builtins.open") as open_mock, patch( -+ "salt.utils.files.is_fcntl_available", MagicMock(return_value=False) -+ ): -+ salt.utils.files.fopen(os.path.join(tmp_path, "foobar"), mode="b", buffering=1) -+ assert open_mock.called -+ assert open_mock.call_args[1]["buffering"] == io.DEFAULT_BUFFER_SIZE -+ -+ - def _create_temp_structure(temp_directory, structure): - for folder, files in structure.items(): - current_directory = os.path.join(temp_directory, folder) --- -2.37.3 - - diff --git a/ignore-erros-on-reading-license-files-with-dpkg_lowp.patch b/ignore-erros-on-reading-license-files-with-dpkg_lowp.patch deleted file mode 100644 index 3b9c808..0000000 --- a/ignore-erros-on-reading-license-files-with-dpkg_lowp.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 7cac5f67eb0d586314f9e7c987b8a620e28eeac3 Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Mon, 27 Jun 2022 17:55:49 +0300 -Subject: [PATCH] Ignore erros on reading license files with dpkg_lowpkg - (bsc#1197288) - -* Ignore erros on reading license files with dpkg_lowpkg (bsc#1197288) - -* Add test for license reading with dpkg_lowpkg ---- - salt/modules/dpkg_lowpkg.py | 2 +- - tests/pytests/unit/modules/test_dpkg_lowpkg.py | 18 ++++++++++++++++++ - 2 files changed, 19 insertions(+), 1 deletion(-) - create mode 100644 tests/pytests/unit/modules/test_dpkg_lowpkg.py - -diff --git a/salt/modules/dpkg_lowpkg.py b/salt/modules/dpkg_lowpkg.py -index afbd619490..2c25b1fb2a 100644 ---- a/salt/modules/dpkg_lowpkg.py -+++ b/salt/modules/dpkg_lowpkg.py -@@ -361,7 +361,7 @@ def _get_pkg_license(pkg): - licenses = set() - cpr = "/usr/share/doc/{}/copyright".format(pkg) - if os.path.exists(cpr): -- with salt.utils.files.fopen(cpr) as fp_: -+ with salt.utils.files.fopen(cpr, errors="ignore") as fp_: - for line in salt.utils.stringutils.to_unicode(fp_.read()).split(os.linesep): - if line.startswith("License:"): - licenses.add(line.split(":", 1)[1].strip()) -diff --git a/tests/pytests/unit/modules/test_dpkg_lowpkg.py b/tests/pytests/unit/modules/test_dpkg_lowpkg.py -new file mode 100644 -index 0000000000..1a89660c02 ---- /dev/null -+++ b/tests/pytests/unit/modules/test_dpkg_lowpkg.py -@@ -0,0 +1,18 @@ -+import os -+ -+import salt.modules.dpkg_lowpkg as dpkg -+from tests.support.mock import MagicMock, mock_open, patch -+ -+ -+def test_get_pkg_license(): -+ """ -+ Test _get_pkg_license for ignore errors on reading license from copyright files -+ """ -+ license_read_mock = mock_open(read_data="") -+ with patch.object(os.path, "exists", MagicMock(return_value=True)), patch( -+ "salt.utils.files.fopen", license_read_mock -+ ): -+ dpkg._get_pkg_license("bash") -+ -+ assert license_read_mock.calls[0].args[0] == "/usr/share/doc/bash/copyright" -+ assert license_read_mock.calls[0].kwargs["errors"] == "ignore" --- -2.37.3 - - diff --git a/ignore-extend-declarations-from-excluded-sls-files.patch b/ignore-extend-declarations-from-excluded-sls-files.patch deleted file mode 100644 index 619ac0a..0000000 --- a/ignore-extend-declarations-from-excluded-sls-files.patch +++ /dev/null @@ -1,250 +0,0 @@ -From e4aff9ca68ce142c87ec875846d8916b9df8e6c5 Mon Sep 17 00:00:00 2001 -From: Alexander Graul -Date: Fri, 21 Oct 2022 14:39:52 +0200 -Subject: [PATCH] Ignore extend declarations from excluded sls files - -* Use both test sls files - -(cherry picked from commit 3cb5f5a14ff68d0bd809a4adba7d820534d0f7c7) - -* Test that excluded sls files can't extend others - -(cherry picked from commit e91c1a608b3c016b2c30bf324e969cd097ddf776) - -* Ignore extend declarations from excluded sls files - -sls files that are excluded should not affect other sls files by -extending their states. Exclude statements are processed very late in -the state processing pipeline to ensure they are not overridden. By that -time, extend declarations are already processed. - -Luckily, it's not necessary to change much, during the extend -declarations processing it is easy to check if the sls file that -contains a given extend declaration is excluded. - -(cherry picked from commit 856b23c45dd3be78d8879a0b0c4aa6356afea3cf) ---- - changelog/62082.fixed | 1 + - salt/state.py | 19 +++ - .../unit/state/test_state_highstate.py | 152 +++++++++++++++++- - 3 files changed, 171 insertions(+), 1 deletion(-) - create mode 100644 changelog/62082.fixed - -diff --git a/changelog/62082.fixed b/changelog/62082.fixed -new file mode 100644 -index 0000000000..02e5f5ff40 ---- /dev/null -+++ b/changelog/62082.fixed -@@ -0,0 +1 @@ -+Ignore extend declarations in sls files that are excluded. -diff --git a/salt/state.py b/salt/state.py -index 316dcdec63..f5579fbb69 100644 ---- a/salt/state.py -+++ b/salt/state.py -@@ -1680,6 +1680,25 @@ class State: - else: - name = ids[0][0] - -+ sls_excludes = [] -+ # excluded sls are plain list items or dicts with an "sls" key -+ for exclude in high.get("__exclude__", []): -+ if isinstance(exclude, str): -+ sls_excludes.append(exclude) -+ elif exclude.get("sls"): -+ sls_excludes.append(exclude["sls"]) -+ -+ if body.get("__sls__") in sls_excludes: -+ log.debug( -+ "Cannot extend ID '%s' in '%s:%s' because '%s:%s' is excluded.", -+ name, -+ body.get("__env__", "base"), -+ body.get("__sls__", "base"), -+ body.get("__env__", "base"), -+ body.get("__sls__", "base"), -+ ) -+ continue -+ - for state, run in body.items(): - if state.startswith("__"): - continue -diff --git a/tests/pytests/unit/state/test_state_highstate.py b/tests/pytests/unit/state/test_state_highstate.py -index 059f83fd9f..7c72cc8e09 100644 ---- a/tests/pytests/unit/state/test_state_highstate.py -+++ b/tests/pytests/unit/state/test_state_highstate.py -@@ -3,9 +3,11 @@ - """ - - import logging -+import textwrap - - import pytest # pylint: disable=unused-import - import salt.state -+from salt.utils.odict import OrderedDict - - log = logging.getLogger(__name__) - -@@ -180,7 +182,7 @@ def test_find_sls_ids_with_exclude(highstate, state_tree_dir): - with pytest.helpers.temp_file( - "slsfile1.sls", slsfile1, sls_dir - ), pytest.helpers.temp_file( -- "slsfile1.sls", slsfile1, sls_dir -+ "slsfile2.sls", slsfile2, sls_dir - ), pytest.helpers.temp_file( - "stateB.sls", stateB, sls_dir - ), pytest.helpers.temp_file( -@@ -196,3 +198,151 @@ def test_find_sls_ids_with_exclude(highstate, state_tree_dir): - high, _ = highstate.render_highstate(matches) - ret = salt.state.find_sls_ids("issue-47182.stateA.newer", high) - assert ret == [("somestuff", "cmd")] -+ -+ -+def test_dont_extend_in_excluded_sls_file(highstate, state_tree_dir): -+ """ -+ See https://github.com/saltstack/salt/issues/62082#issuecomment-1245461333 -+ """ -+ top_sls = textwrap.dedent( -+ """\ -+ base: -+ '*': -+ - test1 -+ - exclude -+ """ -+ ) -+ exclude_sls = textwrap.dedent( -+ """\ -+ exclude: -+ - sls: test2 -+ """ -+ ) -+ test1_sls = textwrap.dedent( -+ """\ -+ include: -+ - test2 -+ -+ test1: -+ cmd.run: -+ - name: echo test1 -+ """ -+ ) -+ test2_sls = textwrap.dedent( -+ """\ -+ extend: -+ test1: -+ cmd.run: -+ - name: echo "override test1 in test2" -+ -+ test2-id: -+ cmd.run: -+ - name: echo test2 -+ """ -+ ) -+ sls_dir = str(state_tree_dir) -+ with pytest.helpers.temp_file( -+ "top.sls", top_sls, sls_dir -+ ), pytest.helpers.temp_file( -+ "test1.sls", test1_sls, sls_dir -+ ), pytest.helpers.temp_file( -+ "test2.sls", test2_sls, sls_dir -+ ), pytest.helpers.temp_file( -+ "exclude.sls", exclude_sls, sls_dir -+ ): -+ # manually compile the high data, error checking is not needed in this -+ # test case. -+ top = highstate.get_top() -+ matches = highstate.top_matches(top) -+ high, _ = highstate.render_highstate(matches) -+ -+ # high is mutated by call_high and the different "pipeline steps" -+ assert high == OrderedDict( -+ [ -+ ( -+ "__extend__", -+ [ -+ { -+ "test1": OrderedDict( -+ [ -+ ("__sls__", "test2"), -+ ("__env__", "base"), -+ ( -+ "cmd", -+ [ -+ OrderedDict( -+ [ -+ ( -+ "name", -+ 'echo "override test1 in test2"', -+ ) -+ ] -+ ), -+ "run", -+ ], -+ ), -+ ] -+ ) -+ } -+ ], -+ ), -+ ( -+ "test1", -+ OrderedDict( -+ [ -+ ( -+ "cmd", -+ [ -+ OrderedDict([("name", "echo test1")]), -+ "run", -+ {"order": 10001}, -+ ], -+ ), -+ ("__sls__", "test1"), -+ ("__env__", "base"), -+ ] -+ ), -+ ), -+ ( -+ "test2-id", -+ OrderedDict( -+ [ -+ ( -+ "cmd", -+ [ -+ OrderedDict([("name", "echo test2")]), -+ "run", -+ {"order": 10000}, -+ ], -+ ), -+ ("__sls__", "test2"), -+ ("__env__", "base"), -+ ] -+ ), -+ ), -+ ("__exclude__", [OrderedDict([("sls", "test2")])]), -+ ] -+ ) -+ highstate.state.call_high(high) -+ # assert that the extend declaration was not applied -+ assert high == OrderedDict( -+ [ -+ ( -+ "test1", -+ OrderedDict( -+ [ -+ ( -+ "cmd", -+ [ -+ OrderedDict([("name", "echo test1")]), -+ "run", -+ {"order": 10001}, -+ ], -+ ), -+ ("__sls__", "test1"), -+ ("__env__", "base"), -+ ] -+ ), -+ ) -+ ] -+ ) --- -2.37.3 - - diff --git a/ignore-non-utf8-characters-while-reading-files-with-.patch b/ignore-non-utf8-characters-while-reading-files-with-.patch deleted file mode 100644 index 4fb2242..0000000 --- a/ignore-non-utf8-characters-while-reading-files-with-.patch +++ /dev/null @@ -1,214 +0,0 @@ -From 3f1b1180ba34e9ab3a4453248c733f11aa193f1b Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Wed, 14 Sep 2022 14:57:29 +0300 -Subject: [PATCH] Ignore non utf8 characters while reading files with - core grains module (bsc#1202165) - -* Ignore UnicodeDecodeError on reading files with core grains - -* Add tests for non utf8 chars in cmdline - -* Blacken modified lines - -* Fix the tests - -* Add changelog entry - -* Change ignore to surrogateescape for kernelparameters - -* Turn static test files to dynamic ---- - changelog/62633.fixed | 1 + - salt/grains/core.py | 12 ++- - tests/pytests/unit/grains/test_core.py | 118 +++++++++++++++++++++++++ - 3 files changed, 128 insertions(+), 3 deletions(-) - create mode 100644 changelog/62633.fixed - -diff --git a/changelog/62633.fixed b/changelog/62633.fixed -new file mode 100644 -index 0000000000..1ab74f9122 ---- /dev/null -+++ b/changelog/62633.fixed -@@ -0,0 +1 @@ -+Prevent possible tracebacks in core grains module by ignoring non utf8 characters in /proc/1/environ, /proc/1/cmdline, /proc/cmdline -diff --git a/salt/grains/core.py b/salt/grains/core.py -index 047c33ffd3..76f3767ddf 100644 ---- a/salt/grains/core.py -+++ b/salt/grains/core.py -@@ -1089,7 +1089,9 @@ def _virtual(osdata): - if ("virtual_subtype" not in grains) or (grains["virtual_subtype"] != "LXC"): - if os.path.isfile("/proc/1/environ"): - try: -- with salt.utils.files.fopen("/proc/1/environ", "r") as fhr: -+ with salt.utils.files.fopen( -+ "/proc/1/environ", "r", errors="ignore" -+ ) as fhr: - fhr_contents = fhr.read() - if "container=lxc" in fhr_contents: - grains["virtual"] = "container" -@@ -1909,7 +1911,9 @@ def os_data(): - grains["init"] = "systemd" - except OSError: - try: -- with salt.utils.files.fopen("/proc/1/cmdline") as fhr: -+ with salt.utils.files.fopen( -+ "/proc/1/cmdline", "r", errors="ignore" -+ ) as fhr: - init_cmdline = fhr.read().replace("\x00", " ").split() - except OSError: - pass -@@ -3154,7 +3158,9 @@ def kernelparams(): - return {} - else: - try: -- with salt.utils.files.fopen("/proc/cmdline", "r") as fhr: -+ with salt.utils.files.fopen( -+ "/proc/cmdline", "r", errors="surrogateescape" -+ ) as fhr: - cmdline = fhr.read() - grains = {"kernelparams": []} - for data in [ -diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py -index 7c4ea1f17f..c06cdb2db0 100644 ---- a/tests/pytests/unit/grains/test_core.py -+++ b/tests/pytests/unit/grains/test_core.py -@@ -11,6 +11,7 @@ import os - import pathlib - import platform - import socket -+import tempfile - import textwrap - from collections import namedtuple - -@@ -2738,6 +2739,38 @@ def test_kernelparams_return_linux(cmdline, expectation): - assert core.kernelparams() == expectation - - -+@pytest.mark.skip_unless_on_linux -+def test_kernelparams_return_linux_non_utf8(): -+ _salt_utils_files_fopen = salt.utils.files.fopen -+ -+ expected = { -+ "kernelparams": [ -+ ("TEST_KEY1", "VAL1"), -+ ("TEST_KEY2", "VAL2"), -+ ("BOOTABLE_FLAG", "\udc80"), -+ ("TEST_KEY_NOVAL", None), -+ ("TEST_KEY3", "3"), -+ ] -+ } -+ -+ with tempfile.TemporaryDirectory() as tempdir: -+ -+ def _open_mock(file_name, *args, **kwargs): -+ return _salt_utils_files_fopen( -+ os.path.join(tempdir, "cmdline"), *args, **kwargs -+ ) -+ -+ with salt.utils.files.fopen( -+ os.path.join(tempdir, "cmdline"), -+ "wb", -+ ) as cmdline_fh, patch("salt.utils.files.fopen", _open_mock): -+ cmdline_fh.write( -+ b'TEST_KEY1=VAL1 TEST_KEY2=VAL2 BOOTABLE_FLAG="\x80" TEST_KEY_NOVAL TEST_KEY3=3\n' -+ ) -+ cmdline_fh.close() -+ assert core.kernelparams() == expected -+ -+ - def test_linux_gpus(): - """ - Test GPU detection on Linux systems -@@ -2940,3 +2973,88 @@ def test_virtual_set_virtual_ec2(): - - assert virtual_grains["virtual"] == "kvm" - assert "virtual_subtype" not in virtual_grains -+ -+ -+@pytest.mark.skip_on_windows -+def test_linux_proc_files_with_non_utf8_chars(): -+ _salt_utils_files_fopen = salt.utils.files.fopen -+ -+ empty_mock = MagicMock(return_value={}) -+ -+ with tempfile.TemporaryDirectory() as tempdir: -+ -+ def _mock_open(filename, *args, **kwargs): -+ return _salt_utils_files_fopen( -+ os.path.join(tempdir, "cmdline-1"), *args, **kwargs -+ ) -+ -+ with salt.utils.files.fopen( -+ os.path.join(tempdir, "cmdline-1"), -+ "wb", -+ ) as cmdline_fh, patch("os.path.isfile", return_value=False), patch( -+ "salt.utils.files.fopen", _mock_open -+ ), patch.dict( -+ core.__salt__, -+ { -+ "cmd.retcode": salt.modules.cmdmod.retcode, -+ "cmd.run": MagicMock(return_value=""), -+ }, -+ ), patch.object( -+ core, "_linux_bin_exists", return_value=False -+ ), patch.object( -+ core, "_parse_lsb_release", return_value=empty_mock -+ ), patch.object( -+ core, "_parse_os_release", return_value=empty_mock -+ ), patch.object( -+ core, "_hw_data", return_value=empty_mock -+ ), patch.object( -+ core, "_virtual", return_value=empty_mock -+ ), patch.object( -+ core, "_bsd_cpudata", return_value=empty_mock -+ ), patch.object( -+ os, "stat", side_effect=OSError() -+ ): -+ cmdline_fh.write( -+ b"/usr/lib/systemd/systemd\x00--switched-root\x00--system\x00--deserialize\x0028\x80\x00" -+ ) -+ cmdline_fh.close() -+ os_grains = core.os_data() -+ assert os_grains != {} -+ -+ -+@pytest.mark.skip_on_windows -+def test_virtual_linux_proc_files_with_non_utf8_chars(): -+ _salt_utils_files_fopen = salt.utils.files.fopen -+ -+ def _is_file_mock(filename): -+ if filename == "/proc/1/environ": -+ return True -+ return False -+ -+ with tempfile.TemporaryDirectory() as tempdir: -+ -+ def _mock_open(filename, *args, **kwargs): -+ return _salt_utils_files_fopen( -+ os.path.join(tempdir, "environ"), *args, **kwargs -+ ) -+ -+ with salt.utils.files.fopen( -+ os.path.join(tempdir, "environ"), -+ "wb", -+ ) as environ_fh, patch("os.path.isfile", _is_file_mock), patch( -+ "salt.utils.files.fopen", _mock_open -+ ), patch.object( -+ salt.utils.path, "which", MagicMock(return_value=None) -+ ), patch.dict( -+ core.__salt__, -+ { -+ "cmd.run_all": MagicMock( -+ return_value={"retcode": 1, "stderr": "", "stdout": ""} -+ ), -+ "cmd.run": MagicMock(return_value=""), -+ }, -+ ): -+ environ_fh.write(b"KEY1=VAL1 KEY2=VAL2\x80 KEY2=VAL2") -+ environ_fh.close() -+ virt_grains = core._virtual({"kernel": "Linux"}) -+ assert virt_grains == {"virtual": "physical"} --- -2.37.3 - - diff --git a/include-stdout-in-error-message-for-zypperpkg-559.patch b/include-stdout-in-error-message-for-zypperpkg-559.patch deleted file mode 100644 index 79615fe..0000000 --- a/include-stdout-in-error-message-for-zypperpkg-559.patch +++ /dev/null @@ -1,63 +0,0 @@ -From f9fe9ea009915478ea8f7896dff2c281e68b5d36 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Yeray=20Guti=C3=A9rrez=20Cedr=C3=A9s?= - -Date: Fri, 14 Oct 2022 08:41:40 +0100 -Subject: [PATCH] Include stdout in error message for zypperpkg (#559) - ---- - salt/modules/zypperpkg.py | 5 +++++ - tests/unit/modules/test_zypperpkg.py | 17 ++++++++++++++++- - 2 files changed, 21 insertions(+), 1 deletion(-) - -diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py -index c787d4009d..5d745c432d 100644 ---- a/salt/modules/zypperpkg.py -+++ b/salt/modules/zypperpkg.py -@@ -339,6 +339,11 @@ class _Zypper: - and self.__call_result["stderr"].strip() - or "" - ) -+ msg += ( -+ self.__call_result["stdout"] -+ and self.__call_result["stdout"].strip() -+ or "" -+ ) - if msg: - _error_msg.append(msg) - else: -diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py -index 37d555844c..bcd001cd85 100644 ---- a/tests/unit/modules/test_zypperpkg.py -+++ b/tests/unit/modules/test_zypperpkg.py -@@ -207,11 +207,26 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - ): - zypper.__zypper__.xml.call("crashme") - -+ output_to_user_stdout = "Output to user to stdout" -+ output_to_user_stderr = "Output to user to stderr" -+ sniffer = RunSniffer( -+ stdout=output_to_user_stdout, stderr=output_to_user_stderr, retcode=1 -+ ) -+ with patch.dict( -+ "salt.modules.zypperpkg.__salt__", {"cmd.run_all": sniffer} -+ ), patch.object(zypper.__zypper__, "_is_rpm_lock", return_value=False): - with self.assertRaisesRegex( -- CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$" -+ CommandExecutionError, -+ "^Zypper command failure: {}$".format( -+ output_to_user_stderr + output_to_user_stdout -+ ), - ): - zypper.__zypper__.call("crashme again") - -+ sniffer = RunSniffer(retcode=1) -+ with patch.dict( -+ "salt.modules.zypperpkg.__salt__", {"cmd.run_all": sniffer} -+ ), patch.object(zypper.__zypper__, "_is_rpm_lock", return_value=False): - zypper.__zypper__.noraise.call("stay quiet") - self.assertEqual(zypper.__zypper__.error_msg, "Check Zypper's logs.") - --- -2.37.3 - - diff --git a/make-pass-renderer-configurable-other-fixes-532.patch b/make-pass-renderer-configurable-other-fixes-532.patch deleted file mode 100644 index a99c412..0000000 --- a/make-pass-renderer-configurable-other-fixes-532.patch +++ /dev/null @@ -1,414 +0,0 @@ -From 030e2cb20af09673d5f38d68bcb257c6c839a2f3 Mon Sep 17 00:00:00 2001 -From: Daniel Mach -Date: Thu, 6 Oct 2022 11:58:23 +0200 -Subject: [PATCH] Make pass renderer configurable & other fixes (#532) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -* pass: Use a secure way of handling pass arguments - -The original code would fail on pass paths with spaces, -because they would be split into multiple arguments. - -* pass: Strip only trailing newline characters from the secret - -* pass: Do not modify $HOME env globally - -Just set $HOME for calling the pass binary -to avoid affecting anything outside the pass renderer. - -* pass: Use pass executable path from _get_pass_exec() - -* Make the pass renderer more configurable - -1. Allow us to make the pass renderer fail during pillar rendering - when a secret corresponding with a pass path cannot be fetched. - For this we add a master config variable pass_strict_fetch. - -2. Allow to have prefix for variables that should be processed - with the pass renderer. - For this we add a master config variable pass_variable_prefix. - -3. Allow us to configure pass' GNUPGHOME and PASSWORD_STORE_DIR - environmental variables. - For this we add master config variables pass_gnupghome and pass_dir. - -* Add tests for the pass renderer - -* pass: Handle FileNotFoundError when pass binary is not available - -Co-authored-by: Marcus Rückert ---- - changelog/62120.added | 4 + - changelog/62120.fixed | 4 + - salt/config/__init__.py | 12 ++ - salt/renderers/pass.py | 104 ++++++++++++-- - tests/pytests/unit/renderers/test_pass.py | 164 ++++++++++++++++++++++ - 5 files changed, 274 insertions(+), 14 deletions(-) - create mode 100644 changelog/62120.added - create mode 100644 changelog/62120.fixed - create mode 100644 tests/pytests/unit/renderers/test_pass.py - -diff --git a/changelog/62120.added b/changelog/62120.added -new file mode 100644 -index 0000000000..4303d124f0 ---- /dev/null -+++ b/changelog/62120.added -@@ -0,0 +1,4 @@ -+Config option pass_variable_prefix allows to distinguish variables that contain paths to pass secrets. -+Config option pass_strict_fetch allows to error out when a secret cannot be fetched from pass. -+Config option pass_dir allows setting the PASSWORD_STORE_DIR env for pass. -+Config option pass_gnupghome allows setting the $GNUPGHOME env for pass. -diff --git a/changelog/62120.fixed b/changelog/62120.fixed -new file mode 100644 -index 0000000000..22a9711383 ---- /dev/null -+++ b/changelog/62120.fixed -@@ -0,0 +1,4 @@ -+Pass executable path from _get_path_exec() is used when calling the program. -+The $HOME env is no longer modified globally. -+Only trailing newlines are stripped from the fetched secret. -+Pass process arguments are handled in a secure way. -diff --git a/salt/config/__init__.py b/salt/config/__init__.py -index 7cdee12c4d..0cc0deb874 100644 ---- a/salt/config/__init__.py -+++ b/salt/config/__init__.py -@@ -967,6 +967,14 @@ VALID_OPTS = immutabletypes.freeze( - # Use Adler32 hashing algorithm for server_id (default False until Sodium, "adler32" after) - # Possible values are: False, adler32, crc32 - "server_id_use_crc": (bool, str), -+ # pass renderer: Fetch secrets only for the template variables matching the prefix -+ "pass_variable_prefix": str, -+ # pass renderer: Whether to error out when unable to fetch a secret -+ "pass_strict_fetch": bool, -+ # pass renderer: Set GNUPGHOME env for Pass -+ "pass_gnupghome": str, -+ # pass renderer: Set PASSWORD_STORE_DIR env for Pass -+ "pass_dir": str, - } - ) - -@@ -1608,6 +1616,10 @@ DEFAULT_MASTER_OPTS = immutabletypes.freeze( - "fips_mode": False, - "detect_remote_minions": False, - "remote_minions_port": 22, -+ "pass_variable_prefix": "", -+ "pass_strict_fetch": False, -+ "pass_gnupghome": "", -+ "pass_dir": "", - } - ) - -diff --git a/salt/renderers/pass.py b/salt/renderers/pass.py -index 71b1021b96..ba0f152c23 100644 ---- a/salt/renderers/pass.py -+++ b/salt/renderers/pass.py -@@ -45,6 +45,34 @@ Install pass binary - - pass: - pkg.installed -+ -+Salt master configuration options -+ -+.. code-block:: yaml -+ -+ # If the prefix is *not* set (default behavior), all template variables are -+ # considered for fetching secrets from Pass. Those that cannot be resolved -+ # to a secret are passed through. -+ # -+ # If the prefix is set, only the template variables with matching prefix are -+ # considered for fetching the secrets, other variables are passed through. -+ # -+ # For ease of use it is recommended to set the following options as well: -+ # renderer: 'jinja|yaml|pass' -+ # pass_strict_fetch: true -+ # -+ pass_variable_prefix: 'pass:' -+ -+ # If set to 'true', error out when unable to fetch a secret for a template variable. -+ pass_strict_fetch: true -+ -+ # Set GNUPGHOME env for Pass. -+ # Defaults to: ~/.gnupg -+ pass_gnupghome: -+ -+ # Set PASSWORD_STORE_DIR env for Pass. -+ # Defaults to: ~/.password-store -+ pass_dir: - """ - - -@@ -54,7 +82,7 @@ from os.path import expanduser - from subprocess import PIPE, Popen - - import salt.utils.path --from salt.exceptions import SaltRenderError -+from salt.exceptions import SaltConfigurationError, SaltRenderError - - log = logging.getLogger(__name__) - -@@ -75,18 +103,71 @@ def _fetch_secret(pass_path): - Fetch secret from pass based on pass_path. If there is - any error, return back the original pass_path value - """ -- cmd = "pass show {}".format(pass_path.strip()) -- log.debug("Fetching secret: %s", cmd) -+ pass_exec = _get_pass_exec() -+ -+ # Make a backup in case we want to return the original value without stripped whitespaces -+ original_pass_path = pass_path -+ -+ # Remove the optional prefix from pass path -+ pass_prefix = __opts__["pass_variable_prefix"] -+ if pass_prefix: -+ # If we do not see our prefix we do not want to process this variable -+ # and we return the unmodified pass path -+ if not pass_path.startswith(pass_prefix): -+ return pass_path -+ -+ # strip the prefix from the start of the string -+ pass_path = pass_path[len(pass_prefix) :] -+ -+ # The pass_strict_fetch option must be used with pass_variable_prefix -+ pass_strict_fetch = __opts__["pass_strict_fetch"] -+ if pass_strict_fetch and not pass_prefix: -+ msg = "The 'pass_strict_fetch' option requires 'pass_variable_prefix' option enabled" -+ raise SaltConfigurationError(msg) -+ -+ # Remove whitespaces from the pass_path -+ pass_path = pass_path.strip() - -- proc = Popen(cmd.split(" "), stdout=PIPE, stderr=PIPE) -- pass_data, pass_error = proc.communicate() -+ cmd = [pass_exec, "show", pass_path] -+ log.debug("Fetching secret: %s", " ".join(cmd)) -+ -+ # Make sure environment variable HOME is set, since Pass looks for the -+ # password-store under ~/.password-store. -+ env = os.environ.copy() -+ env["HOME"] = expanduser("~") -+ -+ pass_dir = __opts__["pass_dir"] -+ if pass_dir: -+ env["PASSWORD_STORE_DIR"] = pass_dir -+ -+ pass_gnupghome = __opts__["pass_gnupghome"] -+ if pass_gnupghome: -+ env["GNUPGHOME"] = pass_gnupghome -+ -+ try: -+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) -+ pass_data, pass_error = proc.communicate() -+ pass_returncode = proc.returncode -+ except OSError as e: -+ pass_data, pass_error = "", str(e) -+ pass_returncode = 1 - - # The version of pass used during development sent output to - # stdout instead of stderr even though its returncode was non zero. -- if proc.returncode or not pass_data: -- log.warning("Could not fetch secret: %s %s", pass_data, pass_error) -- pass_data = pass_path -- return pass_data.strip() -+ if pass_returncode or not pass_data: -+ try: -+ pass_error = pass_error.decode("utf-8") -+ except (AttributeError, ValueError): -+ pass -+ msg = "Could not fetch secret '{}' from the password store: {}".format( -+ pass_path, pass_error -+ ) -+ if pass_strict_fetch: -+ raise SaltRenderError(msg) -+ else: -+ log.warning(msg) -+ return original_pass_path -+ return pass_data.rstrip("\r\n") - - - def _decrypt_object(obj): -@@ -108,9 +189,4 @@ def render(pass_info, saltenv="base", sls="", argline="", **kwargs): - """ - Fetch secret from pass based on pass_path - """ -- _get_pass_exec() -- -- # Make sure environment variable HOME is set, since Pass looks for the -- # password-store under ~/.password-store. -- os.environ["HOME"] = expanduser("~") - return _decrypt_object(pass_info) -diff --git a/tests/pytests/unit/renderers/test_pass.py b/tests/pytests/unit/renderers/test_pass.py -new file mode 100644 -index 0000000000..74e822c7ec ---- /dev/null -+++ b/tests/pytests/unit/renderers/test_pass.py -@@ -0,0 +1,164 @@ -+import importlib -+ -+import pytest -+ -+import salt.config -+import salt.exceptions -+from tests.support.mock import MagicMock, patch -+ -+# "pass" is a reserved keyword, we need to import it differently -+pass_ = importlib.import_module("salt.renderers.pass") -+ -+ -+@pytest.fixture -+def configure_loader_modules(): -+ return { -+ pass_: { -+ "__opts__": salt.config.DEFAULT_MASTER_OPTS.copy(), -+ "_get_pass_exec": MagicMock(return_value="/usr/bin/pass"), -+ } -+ } -+ -+ -+# The default behavior is that if fetching a secret from pass fails, -+# the value is passed through. Even the trailing newlines are preserved. -+def test_passthrough(): -+ pass_path = "secret\n" -+ expected = pass_path -+ result = pass_.render(pass_path) -+ -+ assert result == expected -+ -+ -+# Fetch a secret in the strict mode. -+def test_strict_fetch(): -+ config = { -+ "pass_variable_prefix": "pass:", -+ "pass_strict_fetch": True, -+ } -+ -+ popen_mock = MagicMock(spec=pass_.Popen) -+ popen_mock.return_value.communicate.return_value = ("password123456\n", "") -+ popen_mock.return_value.returncode = 0 -+ -+ mocks = { -+ "Popen": popen_mock, -+ } -+ -+ pass_path = "pass:secret" -+ expected = "password123456" -+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks): -+ result = pass_.render(pass_path) -+ -+ assert result == expected -+ -+ -+# Fail to fetch a secret in the strict mode. -+def test_strict_fetch_fail(): -+ config = { -+ "pass_variable_prefix": "pass:", -+ "pass_strict_fetch": True, -+ } -+ -+ popen_mock = MagicMock(spec=pass_.Popen) -+ popen_mock.return_value.communicate.return_value = ("", "Secret not found") -+ popen_mock.return_value.returncode = 1 -+ -+ mocks = { -+ "Popen": popen_mock, -+ } -+ -+ pass_path = "pass:secret" -+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks): -+ with pytest.raises(salt.exceptions.SaltRenderError): -+ pass_.render(pass_path) -+ -+ -+# Passthrough a value that doesn't have a pass prefix. -+def test_strict_fetch_passthrough(): -+ config = { -+ "pass_variable_prefix": "pass:", -+ "pass_strict_fetch": True, -+ } -+ -+ pass_path = "variable-without-pass-prefix\n" -+ expected = pass_path -+ with patch.dict(pass_.__opts__, config): -+ result = pass_.render(pass_path) -+ -+ assert result == expected -+ -+ -+# Fetch a secret in the strict mode. The pass path contains spaces. -+def test_strict_fetch_pass_path_with_spaces(): -+ config = { -+ "pass_variable_prefix": "pass:", -+ "pass_strict_fetch": True, -+ } -+ -+ popen_mock = MagicMock(spec=pass_.Popen) -+ popen_mock.return_value.communicate.return_value = ("password123456\n", "") -+ popen_mock.return_value.returncode = 0 -+ -+ mocks = { -+ "Popen": popen_mock, -+ } -+ -+ pass_path = "pass:se cr et" -+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks): -+ pass_.render(pass_path) -+ -+ call_args, call_kwargs = popen_mock.call_args_list[0] -+ assert call_args[0] == ["/usr/bin/pass", "show", "se cr et"] -+ -+ -+# Fetch a secret in the strict mode. The secret contains leading and trailing whitespaces. -+def test_strict_fetch_secret_with_whitespaces(): -+ config = { -+ "pass_variable_prefix": "pass:", -+ "pass_strict_fetch": True, -+ } -+ -+ popen_mock = MagicMock(spec=pass_.Popen) -+ popen_mock.return_value.communicate.return_value = (" \tpassword123456\t \r\n", "") -+ popen_mock.return_value.returncode = 0 -+ -+ mocks = { -+ "Popen": popen_mock, -+ } -+ -+ pass_path = "pass:secret" -+ expected = " \tpassword123456\t " # only the trailing newlines get striped -+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks): -+ result = pass_.render(pass_path) -+ -+ assert result == expected -+ -+ -+# Test setting env variables based on config values: -+# - pass_gnupghome -> GNUPGHOME -+# - pass_dir -> PASSWORD_STORE_DIR -+def test_env(): -+ config = { -+ "pass_variable_prefix": "pass:", -+ "pass_strict_fetch": True, -+ "pass_gnupghome": "/path/to/gnupghome", -+ "pass_dir": "/path/to/secretstore", -+ } -+ -+ popen_mock = MagicMock(spec=pass_.Popen) -+ popen_mock.return_value.communicate.return_value = ("password123456\n", "") -+ popen_mock.return_value.returncode = 0 -+ -+ mocks = { -+ "Popen": popen_mock, -+ } -+ -+ pass_path = "pass:secret" -+ expected = " \tpassword123456\t " # only the trailing newlines get striped -+ with patch.dict(pass_.__opts__, config), patch.dict(pass_.__dict__, mocks): -+ result = pass_.render(pass_path) -+ -+ call_args, call_kwargs = popen_mock.call_args_list[0] -+ assert call_kwargs["env"]["GNUPGHOME"] == config["pass_gnupghome"] -+ assert call_kwargs["env"]["PASSWORD_STORE_DIR"] == config["pass_dir"] --- -2.37.3 - - diff --git a/make-sure-saltcacheloader-use-correct-fileclient-519.patch b/make-sure-saltcacheloader-use-correct-fileclient-519.patch deleted file mode 100644 index 51b8686..0000000 --- a/make-sure-saltcacheloader-use-correct-fileclient-519.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 03ce925098fb96ad2f2f4b7d4c151ef63aede75f Mon Sep 17 00:00:00 2001 -From: Witek Bedyk -Date: Thu, 19 May 2022 12:52:12 +0200 -Subject: [PATCH] Make sure SaltCacheLoader use correct fileclient (#519) - -Backported from https://github.com/saltstack/salt/pull/61895 - -Signed-off-by: Witek Bedyk ---- - salt/state.py | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/salt/state.py b/salt/state.py -index db228228a7..316dcdec63 100644 ---- a/salt/state.py -+++ b/salt/state.py -@@ -4170,6 +4170,9 @@ class BaseHighState: - ) - else: - try: -+ # Make sure SaltCacheLoader use correct fileclient -+ if context is None: -+ context = {"fileclient": self.client} - state = compile_template( - fn_, - self.state.rend, --- -2.37.3 - - diff --git a/normalize-package-names-once-with-pkg.installed-remo.patch b/normalize-package-names-once-with-pkg.installed-remo.patch deleted file mode 100644 index 76fd684..0000000 --- a/normalize-package-names-once-with-pkg.installed-remo.patch +++ /dev/null @@ -1,296 +0,0 @@ -From f2dc43cf1db3fee41e328c68545ccac2576021ca Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Mon, 27 Jun 2022 18:01:21 +0300 -Subject: [PATCH] Normalize package names once with pkg.installed/removed - using yum (bsc#1195895) - -* Normalize the package names only once on install/remove - -* Add test for checking pkg.installed/removed with only normalisation - -* Fix split_arch conditions - -* Fix test_pkg ---- - salt/modules/yumpkg.py | 18 ++- - salt/states/pkg.py | 3 +- - tests/pytests/unit/states/test_pkg.py | 177 +++++++++++++++++++++++++- - 3 files changed, 192 insertions(+), 6 deletions(-) - -diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py -index 46f0b1f613..f52e084346 100644 ---- a/salt/modules/yumpkg.py -+++ b/salt/modules/yumpkg.py -@@ -1460,7 +1460,12 @@ def install( - - try: - pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( -- name, pkgs, sources, saltenv=saltenv, normalize=normalize, **kwargs -+ name, -+ pkgs, -+ sources, -+ saltenv=saltenv, -+ normalize=normalize and kwargs.get("split_arch", True), -+ **kwargs - ) - except MinionError as exc: - raise CommandExecutionError(exc) -@@ -1612,7 +1617,10 @@ def install( - except ValueError: - pass - else: -- if archpart in salt.utils.pkg.rpm.ARCHES: -+ if archpart in salt.utils.pkg.rpm.ARCHES and ( -+ archpart != __grains__["osarch"] -+ or kwargs.get("split_arch", True) -+ ): - arch = "." + archpart - pkgname = namepart - -@@ -2143,11 +2151,13 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 - arch = "" - pkgname = target - try: -- namepart, archpart = target.rsplit(".", 1) -+ namepart, archpart = pkgname.rsplit(".", 1) - except ValueError: - pass - else: -- if archpart in salt.utils.pkg.rpm.ARCHES: -+ if archpart in salt.utils.pkg.rpm.ARCHES and ( -+ archpart != __grains__["osarch"] or kwargs.get("split_arch", True) -+ ): - arch = "." + archpart - pkgname = namepart - # Since we don't always have the arch info, epoch information has to parsed out. But -diff --git a/salt/states/pkg.py b/salt/states/pkg.py -index ef4e062145..cda966a1e8 100644 ---- a/salt/states/pkg.py -+++ b/salt/states/pkg.py -@@ -1873,6 +1873,7 @@ def installed( - normalize=normalize, - update_holds=update_holds, - ignore_epoch=ignore_epoch, -+ split_arch=False, - **kwargs - ) - except CommandExecutionError as exc: -@@ -2940,7 +2941,7 @@ def _uninstall( - } - - changes = __salt__["pkg.{}".format(action)]( -- name, pkgs=pkgs, version=version, **kwargs -+ name, pkgs=pkgs, version=version, split_arch=False, **kwargs - ) - new = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) - failed = [] -diff --git a/tests/pytests/unit/states/test_pkg.py b/tests/pytests/unit/states/test_pkg.py -index cba8201bda..ecb841e8ec 100644 ---- a/tests/pytests/unit/states/test_pkg.py -+++ b/tests/pytests/unit/states/test_pkg.py -@@ -2,6 +2,8 @@ import logging - - import pytest - import salt.modules.beacons as beaconmod -+import salt.modules.pkg_resource as pkg_resource -+import salt.modules.yumpkg as yumpkg - import salt.states.beacon as beaconstate - import salt.states.pkg as pkg - import salt.utils.state as state_utils -@@ -17,7 +19,7 @@ def configure_loader_modules(): - pkg: { - "__env__": "base", - "__salt__": {}, -- "__grains__": {"os": "CentOS"}, -+ "__grains__": {"os": "CentOS", "os_family": "RedHat"}, - "__opts__": {"test": False, "cachedir": ""}, - "__instance_id__": "", - "__low__": {}, -@@ -25,6 +27,15 @@ def configure_loader_modules(): - }, - beaconstate: {"__salt__": {}, "__opts__": {}}, - beaconmod: {"__salt__": {}, "__opts__": {}}, -+ pkg_resource: { -+ "__salt__": {}, -+ "__grains__": {"os": "CentOS", "os_family": "RedHat"}, -+ }, -+ yumpkg: { -+ "__salt__": {}, -+ "__grains__": {"osarch": "x86_64", "osmajorrelease": 7}, -+ "__opts__": {}, -+ }, - } - - -@@ -726,3 +737,167 @@ def test_held_unheld(package_manager): - hold_mock.assert_not_called() - unhold_mock.assert_any_call(name="held-test", pkgs=["baz"]) - unhold_mock.assert_any_call(name="held-test", pkgs=["bar"]) -+ -+ -+def test_installed_with_single_normalize(): -+ """ -+ Test pkg.installed with preventing multiple package name normalisation -+ """ -+ -+ list_no_weird_installed = { -+ "pkga": "1.0.1", -+ "pkgb": "1.0.2", -+ "pkgc": "1.0.3", -+ } -+ list_no_weird_installed_ver_list = { -+ "pkga": ["1.0.1"], -+ "pkgb": ["1.0.2"], -+ "pkgc": ["1.0.3"], -+ } -+ list_with_weird_installed = { -+ "pkga": "1.0.1", -+ "pkgb": "1.0.2", -+ "pkgc": "1.0.3", -+ "weird-name-1.2.3-1234.5.6.test7tst.x86_64": "20220214-2.1", -+ } -+ list_with_weird_installed_ver_list = { -+ "pkga": ["1.0.1"], -+ "pkgb": ["1.0.2"], -+ "pkgc": ["1.0.3"], -+ "weird-name-1.2.3-1234.5.6.test7tst.x86_64": ["20220214-2.1"], -+ } -+ list_pkgs = MagicMock( -+ side_effect=[ -+ # For the package with version specified -+ list_no_weird_installed_ver_list, -+ {}, -+ list_no_weird_installed, -+ list_no_weird_installed_ver_list, -+ list_with_weird_installed, -+ list_with_weird_installed_ver_list, -+ # For the package with no version specified -+ list_no_weird_installed_ver_list, -+ {}, -+ list_no_weird_installed, -+ list_no_weird_installed_ver_list, -+ list_with_weird_installed, -+ list_with_weird_installed_ver_list, -+ ] -+ ) -+ -+ salt_dict = { -+ "pkg.install": yumpkg.install, -+ "pkg.list_pkgs": list_pkgs, -+ "pkg.normalize_name": yumpkg.normalize_name, -+ "pkg_resource.version_clean": pkg_resource.version_clean, -+ "pkg_resource.parse_targets": pkg_resource.parse_targets, -+ } -+ -+ with patch("salt.modules.yumpkg.list_pkgs", list_pkgs), patch( -+ "salt.modules.yumpkg.version_cmp", MagicMock(return_value=0) -+ ), patch( -+ "salt.modules.yumpkg._call_yum", MagicMock(return_value={"retcode": 0}) -+ ) as call_yum_mock, patch.dict( -+ pkg.__salt__, salt_dict -+ ), patch.dict( -+ pkg_resource.__salt__, salt_dict -+ ), patch.dict( -+ yumpkg.__salt__, salt_dict -+ ), patch.dict( -+ yumpkg.__grains__, {"os": "CentOS", "osarch": "x86_64", "osmajorrelease": 7} -+ ), patch.object( -+ yumpkg, "list_holds", MagicMock() -+ ): -+ -+ expected = { -+ "weird-name-1.2.3-1234.5.6.test7tst.x86_64": { -+ "old": "", -+ "new": "20220214-2.1", -+ } -+ } -+ ret = pkg.installed( -+ "test_install", -+ pkgs=[{"weird-name-1.2.3-1234.5.6.test7tst.x86_64.noarch": "20220214-2.1"}], -+ ) -+ call_yum_mock.assert_called_once() -+ assert ( -+ call_yum_mock.mock_calls[0].args[0][2] -+ == "weird-name-1.2.3-1234.5.6.test7tst.x86_64-20220214-2.1" -+ ) -+ assert ret["result"] -+ assert ret["changes"] == expected -+ -+ -+def test_removed_with_single_normalize(): -+ """ -+ Test pkg.removed with preventing multiple package name normalisation -+ """ -+ -+ list_no_weird_installed = { -+ "pkga": "1.0.1", -+ "pkgb": "1.0.2", -+ "pkgc": "1.0.3", -+ } -+ list_no_weird_installed_ver_list = { -+ "pkga": ["1.0.1"], -+ "pkgb": ["1.0.2"], -+ "pkgc": ["1.0.3"], -+ } -+ list_with_weird_installed = { -+ "pkga": "1.0.1", -+ "pkgb": "1.0.2", -+ "pkgc": "1.0.3", -+ "weird-name-1.2.3-1234.5.6.test7tst.x86_64": "20220214-2.1", -+ } -+ list_with_weird_installed_ver_list = { -+ "pkga": ["1.0.1"], -+ "pkgb": ["1.0.2"], -+ "pkgc": ["1.0.3"], -+ "weird-name-1.2.3-1234.5.6.test7tst.x86_64": ["20220214-2.1"], -+ } -+ list_pkgs = MagicMock( -+ side_effect=[ -+ list_with_weird_installed_ver_list, -+ list_with_weird_installed, -+ list_no_weird_installed, -+ list_no_weird_installed_ver_list, -+ ] -+ ) -+ -+ salt_dict = { -+ "pkg.remove": yumpkg.remove, -+ "pkg.list_pkgs": list_pkgs, -+ "pkg.normalize_name": yumpkg.normalize_name, -+ "pkg_resource.parse_targets": pkg_resource.parse_targets, -+ "pkg_resource.version_clean": pkg_resource.version_clean, -+ } -+ -+ with patch("salt.modules.yumpkg.list_pkgs", list_pkgs), patch( -+ "salt.modules.yumpkg.version_cmp", MagicMock(return_value=0) -+ ), patch( -+ "salt.modules.yumpkg._call_yum", MagicMock(return_value={"retcode": 0}) -+ ) as call_yum_mock, patch.dict( -+ pkg.__salt__, salt_dict -+ ), patch.dict( -+ pkg_resource.__salt__, salt_dict -+ ), patch.dict( -+ yumpkg.__salt__, salt_dict -+ ): -+ -+ expected = { -+ "weird-name-1.2.3-1234.5.6.test7tst.x86_64": { -+ "old": "20220214-2.1", -+ "new": "", -+ } -+ } -+ ret = pkg.removed( -+ "test_remove", -+ pkgs=[{"weird-name-1.2.3-1234.5.6.test7tst.x86_64.noarch": "20220214-2.1"}], -+ ) -+ call_yum_mock.assert_called_once() -+ assert ( -+ call_yum_mock.mock_calls[0].args[0][2] -+ == "weird-name-1.2.3-1234.5.6.test7tst.x86_64-20220214-2.1" -+ ) -+ assert ret["result"] -+ assert ret["changes"] == expected --- -2.37.3 - - diff --git a/retry-if-rpm-lock-is-temporarily-unavailable-547.patch b/retry-if-rpm-lock-is-temporarily-unavailable-547.patch deleted file mode 100644 index 46cb60b..0000000 --- a/retry-if-rpm-lock-is-temporarily-unavailable-547.patch +++ /dev/null @@ -1,297 +0,0 @@ -From 4a9ec335e7da2f0e3314580e43075bb69fe90c38 Mon Sep 17 00:00:00 2001 -From: Witek Bedyk -Date: Mon, 29 Aug 2022 14:16:00 +0200 -Subject: [PATCH] Retry if RPM lock is temporarily unavailable (#547) - -* Retry if RPM lock is temporarily unavailable - -Backported from saltstack/salt#62204 - -Signed-off-by: Witek Bedyk - -* Sync formating fixes from upstream - -Signed-off-by: Witek Bedyk ---- - changelog/62204.fixed | 1 + - salt/modules/zypperpkg.py | 117 +++++++++++++++++---------- - tests/unit/modules/test_zypperpkg.py | 45 ++++++++++- - 3 files changed, 115 insertions(+), 48 deletions(-) - create mode 100644 changelog/62204.fixed - -diff --git a/changelog/62204.fixed b/changelog/62204.fixed -new file mode 100644 -index 0000000000..59f1914593 ---- /dev/null -+++ b/changelog/62204.fixed -@@ -0,0 +1 @@ -+Fixed Zypper module failing on RPM lock file being temporarily unavailable. -diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py -index 2c36e2968a..c787d4009d 100644 ---- a/salt/modules/zypperpkg.py -+++ b/salt/modules/zypperpkg.py -@@ -14,6 +14,7 @@ Package support for openSUSE via the zypper package manager - - import configparser - import datetime -+import errno - import fnmatch - import logging - import os -@@ -39,6 +40,9 @@ from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationEr - # pylint: disable=import-error,redefined-builtin,no-name-in-module - from salt.utils.versions import LooseVersion - -+if salt.utils.files.is_fcntl_available(): -+ import fcntl -+ - log = logging.getLogger(__name__) - - HAS_ZYPP = False -@@ -106,6 +110,7 @@ class _Zypper: - XML_DIRECTIVES = ["-x", "--xmlout"] - # ZYPPER_LOCK is not affected by --root - ZYPPER_LOCK = "/var/run/zypp.pid" -+ RPM_LOCK = "/var/lib/rpm/.rpm.lock" - TAG_RELEASED = "zypper/released" - TAG_BLOCKED = "zypper/blocked" - -@@ -276,7 +281,7 @@ class _Zypper: - and self.exit_code not in self.WARNING_EXIT_CODES - ) - -- def _is_lock(self): -+ def _is_zypper_lock(self): - """ - Is this is a lock error code? - -@@ -284,6 +289,23 @@ class _Zypper: - """ - return self.exit_code == self.LOCK_EXIT_CODE - -+ def _is_rpm_lock(self): -+ """ -+ Is this an RPM lock error? -+ """ -+ if salt.utils.files.is_fcntl_available(): -+ if self.exit_code > 0 and os.path.exists(self.RPM_LOCK): -+ with salt.utils.files.fopen(self.RPM_LOCK, mode="w+") as rfh: -+ try: -+ fcntl.lockf(rfh, fcntl.LOCK_EX | fcntl.LOCK_NB) -+ except OSError as err: -+ if err.errno == errno.EAGAIN: -+ return True -+ else: -+ fcntl.lockf(rfh, fcntl.LOCK_UN) -+ -+ return False -+ - def _is_xml_mode(self): - """ - Is Zypper's output is in XML format? -@@ -306,7 +328,7 @@ class _Zypper: - raise CommandExecutionError("No output result from Zypper?") - - self.exit_code = self.__call_result["retcode"] -- if self._is_lock(): -+ if self._is_zypper_lock() or self._is_rpm_lock(): - return False - - if self._is_error(): -@@ -387,48 +409,11 @@ class _Zypper: - if self._check_result(): - break - -- if os.path.exists(self.ZYPPER_LOCK): -- try: -- with salt.utils.files.fopen(self.ZYPPER_LOCK) as rfh: -- data = __salt__["ps.proc_info"]( -- int(rfh.readline()), -- attrs=["pid", "name", "cmdline", "create_time"], -- ) -- data["cmdline"] = " ".join(data["cmdline"]) -- data["info"] = "Blocking process created at {}.".format( -- datetime.datetime.utcfromtimestamp( -- data["create_time"] -- ).isoformat() -- ) -- data["success"] = True -- except Exception as err: # pylint: disable=broad-except -- data = { -- "info": ( -- "Unable to retrieve information about blocking process: {}".format( -- err.message -- ) -- ), -- "success": False, -- } -- else: -- data = { -- "info": "Zypper is locked, but no Zypper lock has been found.", -- "success": False, -- } -- -- if not data["success"]: -- log.debug("Unable to collect data about blocking process.") -- else: -- log.debug("Collected data about blocking process.") -- -- __salt__["event.fire_master"](data, self.TAG_BLOCKED) -- log.debug( -- "Fired a Zypper blocked event to the master with the data: %s", data -- ) -- log.debug("Waiting 5 seconds for Zypper gets released...") -- time.sleep(5) -- if not was_blocked: -- was_blocked = True -+ if self._is_zypper_lock(): -+ self._handle_zypper_lock_file() -+ if self._is_rpm_lock(): -+ self._handle_rpm_lock_file() -+ was_blocked = True - - if was_blocked: - __salt__["event.fire_master"]( -@@ -451,6 +436,50 @@ class _Zypper: - or self.__call_result["stdout"] - ) - -+ def _handle_zypper_lock_file(self): -+ if os.path.exists(self.ZYPPER_LOCK): -+ try: -+ with salt.utils.files.fopen(self.ZYPPER_LOCK) as rfh: -+ data = __salt__["ps.proc_info"]( -+ int(rfh.readline()), -+ attrs=["pid", "name", "cmdline", "create_time"], -+ ) -+ data["cmdline"] = " ".join(data["cmdline"]) -+ data["info"] = "Blocking process created at {}.".format( -+ datetime.datetime.utcfromtimestamp( -+ data["create_time"] -+ ).isoformat() -+ ) -+ data["success"] = True -+ except Exception as err: # pylint: disable=broad-except -+ data = { -+ "info": ( -+ "Unable to retrieve information about " -+ "blocking process: {}".format(err) -+ ), -+ "success": False, -+ } -+ else: -+ data = { -+ "info": "Zypper is locked, but no Zypper lock has been found.", -+ "success": False, -+ } -+ if not data["success"]: -+ log.debug("Unable to collect data about blocking process.") -+ else: -+ log.debug("Collected data about blocking process.") -+ __salt__["event.fire_master"](data, self.TAG_BLOCKED) -+ log.debug("Fired a Zypper blocked event to the master with the data: %s", data) -+ log.debug("Waiting 5 seconds for Zypper gets released...") -+ time.sleep(5) -+ -+ def _handle_rpm_lock_file(self): -+ data = {"info": "RPM is temporarily locked.", "success": True} -+ __salt__["event.fire_master"](data, self.TAG_BLOCKED) -+ log.debug("Fired an RPM blocked event to the master with the data: %s", data) -+ log.debug("Waiting 5 seconds for RPM to get released...") -+ time.sleep(5) -+ - - __zypper__ = _Zypper() - -diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py -index 3f1560a385..37d555844c 100644 ---- a/tests/unit/modules/test_zypperpkg.py -+++ b/tests/unit/modules/test_zypperpkg.py -@@ -4,6 +4,7 @@ - - - import configparser -+import errno - import io - import os - from xml.dom import minidom -@@ -97,7 +98,7 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - } - with patch.dict( - zypper.__salt__, {"cmd.run_all": MagicMock(return_value=ref_out)} -- ): -+ ), patch.object(zypper.__zypper__, "_is_rpm_lock", return_value=False): - upgrades = zypper.list_upgrades(refresh=False) - self.assertEqual(len(upgrades), 3) - for pkg, version in { -@@ -198,7 +199,9 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - ' type="error">Booya!' - ) - sniffer = RunSniffer(stdout=stdout_xml_snippet, retcode=1) -- with patch.dict("salt.modules.zypperpkg.__salt__", {"cmd.run_all": sniffer}): -+ with patch.dict( -+ "salt.modules.zypperpkg.__salt__", {"cmd.run_all": sniffer} -+ ), patch.object(zypper.__zypper__, "_is_rpm_lock", return_value=False): - with self.assertRaisesRegex( - CommandExecutionError, "^Zypper command failure: Booya!$" - ): -@@ -232,7 +235,7 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - with patch.dict( - "salt.modules.zypperpkg.__salt__", - {"cmd.run_all": MagicMock(return_value=ref_out)}, -- ): -+ ), patch.object(zypper.__zypper__, "_is_rpm_lock", return_value=False): - with self.assertRaisesRegex( - CommandExecutionError, - "^Zypper command failure: Some handled zypper internal error{}Another" -@@ -245,7 +248,7 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): - with patch.dict( - "salt.modules.zypperpkg.__salt__", - {"cmd.run_all": MagicMock(return_value=ref_out)}, -- ): -+ ), patch.object(zypper.__zypper__, "_is_rpm_lock", return_value=False): - with self.assertRaisesRegex( - CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$" - ): -@@ -2064,3 +2067,37 @@ pattern() = package-c""" - python_shell=False, - env={"ZYPP_READONLY_HACK": "1"}, - ) -+ -+ def test_is_rpm_lock_no_error(self): -+ with patch.object(os.path, "exists", return_value=True): -+ self.assertFalse(zypper.__zypper__._is_rpm_lock()) -+ -+ def test_rpm_lock_does_not_exist(self): -+ if salt.utils.files.is_fcntl_available(): -+ zypper.__zypper__.exit_code = 1 -+ with patch.object( -+ os.path, "exists", return_value=False -+ ) as mock_path_exists: -+ self.assertFalse(zypper.__zypper__._is_rpm_lock()) -+ mock_path_exists.assert_called_with(zypper.__zypper__.RPM_LOCK) -+ zypper.__zypper__._reset() -+ -+ def test_rpm_lock_acquirable(self): -+ if salt.utils.files.is_fcntl_available(): -+ zypper.__zypper__.exit_code = 1 -+ with patch.object(os.path, "exists", return_value=True), patch( -+ "fcntl.lockf", side_effect=OSError(errno.EAGAIN, "") -+ ) as lockf_mock, patch("salt.utils.files.fopen", mock_open()): -+ self.assertTrue(zypper.__zypper__._is_rpm_lock()) -+ lockf_mock.assert_called() -+ zypper.__zypper__._reset() -+ -+ def test_rpm_lock_not_acquirable(self): -+ if salt.utils.files.is_fcntl_available(): -+ zypper.__zypper__.exit_code = 1 -+ with patch.object(os.path, "exists", return_value=True), patch( -+ "fcntl.lockf" -+ ) as lockf_mock, patch("salt.utils.files.fopen", mock_open()): -+ self.assertFalse(zypper.__zypper__._is_rpm_lock()) -+ self.assertEqual(lockf_mock.call_count, 2) -+ zypper.__zypper__._reset() --- -2.37.3 - - diff --git a/set-default-target-for-pip-from-venv_pip_target-envi.patch b/set-default-target-for-pip-from-venv_pip_target-envi.patch deleted file mode 100644 index c3a6934..0000000 --- a/set-default-target-for-pip-from-venv_pip_target-envi.patch +++ /dev/null @@ -1,87 +0,0 @@ -From d561491c48ee30472e0d4699ba389648ef0d863a Mon Sep 17 00:00:00 2001 -From: Victor Zhestkov -Date: Mon, 27 Jun 2022 18:02:31 +0300 -Subject: [PATCH] Set default target for pip from VENV_PIP_TARGET - environment variable - -* Use VENV_PIP_TARGET as a target for pkg.install - -if set and no target specified on the call - -* Add test for VENV_PIP_TARGET environment variable - -* Changelog entry ---- - changelog/62089.changed | 1 + - salt/modules/pip.py | 6 +++++ - tests/pytests/unit/modules/test_pip.py | 31 ++++++++++++++++++++++++++ - 3 files changed, 38 insertions(+) - create mode 100644 changelog/62089.changed - -diff --git a/changelog/62089.changed b/changelog/62089.changed -new file mode 100644 -index 0000000000..09feb2e922 ---- /dev/null -+++ b/changelog/62089.changed -@@ -0,0 +1 @@ -+Use VENV_PIP_TARGET environment variable as a default target for pip if present. -diff --git a/salt/modules/pip.py b/salt/modules/pip.py -index da26416662..9410024fd5 100644 ---- a/salt/modules/pip.py -+++ b/salt/modules/pip.py -@@ -858,6 +858,12 @@ def install( - if build: - cmd.extend(["--build", build]) - -+ # Use VENV_PIP_TARGET environment variable value as target -+ # if set and no target specified on the function call -+ target_env = os.environ.get("VENV_PIP_TARGET", None) -+ if target is None and target_env is not None: -+ target = target_env -+ - if target: - cmd.extend(["--target", target]) - -diff --git a/tests/pytests/unit/modules/test_pip.py b/tests/pytests/unit/modules/test_pip.py -index 405ec6c82e..ae9005d806 100644 ---- a/tests/pytests/unit/modules/test_pip.py -+++ b/tests/pytests/unit/modules/test_pip.py -@@ -1773,3 +1773,34 @@ def test_when_version_is_called_with_a_user_it_should_be_passed_to_undelying_run - cwd=None, - python_shell=False, - ) -+ -+ -+def test_install_target_from_VENV_PIP_TARGET_in_resulting_command(): -+ pkg = "pep8" -+ target = "/tmp/foo" -+ target_env = "/tmp/bar" -+ mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) -+ environment = os.environ.copy() -+ environment["VENV_PIP_TARGET"] = target_env -+ with patch.dict(pip.__salt__, {"cmd.run_all": mock}), patch.object( -+ os, "environ", environment -+ ): -+ pip.install(pkg) -+ expected = [sys.executable, "-m", "pip", "install", "--target", target_env, pkg] -+ mock.assert_called_with( -+ expected, -+ saltenv="base", -+ runas=None, -+ use_vt=False, -+ python_shell=False, -+ ) -+ mock.reset_mock() -+ pip.install(pkg, target=target) -+ expected = [sys.executable, "-m", "pip", "install", "--target", target, pkg] -+ mock.assert_called_with( -+ expected, -+ saltenv="base", -+ runas=None, -+ use_vt=False, -+ python_shell=False, -+ ) --- -2.37.3 - - diff --git a/state.apply-don-t-check-for-cached-pillar-errors.patch b/state.apply-don-t-check-for-cached-pillar-errors.patch deleted file mode 100644 index a724ea5..0000000 --- a/state.apply-don-t-check-for-cached-pillar-errors.patch +++ /dev/null @@ -1,362 +0,0 @@ -From cba6455bd0480bfb80c466a2b34a702a9afb5bd5 Mon Sep 17 00:00:00 2001 -From: Alexander Graul -Date: Tue, 25 Jan 2022 17:20:55 +0100 -Subject: [PATCH] state.apply: don't check for cached pillar errors - -state.apply request new pillar data from the server. This done to always -have the most up-to-date pillar to work with. Previously, checking for -pillar errors looked at both the new pillar and the in-memory pillar. -The latter might contain pillar rendering errors even if the former does -not. - -For this reason, only the new pillar should be checked, not both. ---- - changelog/52354.fixed | 1 + - changelog/57180.fixed | 1 + - changelog/59339.fixed | 1 + - salt/modules/state.py | 17 ++- - .../modules/state/test_state_pillar_errors.py | 131 ++++++++++++++++++ - .../pytests/unit/modules/state/test_state.py | 115 +++++---------- - 6 files changed, 177 insertions(+), 89 deletions(-) - create mode 100644 changelog/52354.fixed - create mode 100644 changelog/57180.fixed - create mode 100644 changelog/59339.fixed - create mode 100644 tests/pytests/integration/modules/state/test_state_pillar_errors.py - -diff --git a/changelog/52354.fixed b/changelog/52354.fixed -new file mode 100644 -index 0000000000..af885d77fa ---- /dev/null -+++ b/changelog/52354.fixed -@@ -0,0 +1 @@ -+Don't check for cached pillar errors on state.apply -diff --git a/changelog/57180.fixed b/changelog/57180.fixed -new file mode 100644 -index 0000000000..af885d77fa ---- /dev/null -+++ b/changelog/57180.fixed -@@ -0,0 +1 @@ -+Don't check for cached pillar errors on state.apply -diff --git a/changelog/59339.fixed b/changelog/59339.fixed -new file mode 100644 -index 0000000000..af885d77fa ---- /dev/null -+++ b/changelog/59339.fixed -@@ -0,0 +1 @@ -+Don't check for cached pillar errors on state.apply -diff --git a/salt/modules/state.py b/salt/modules/state.py -index f214291328..c0feabe842 100644 ---- a/salt/modules/state.py -+++ b/salt/modules/state.py -@@ -106,18 +106,17 @@ def _set_retcode(ret, highstate=None): - - def _get_pillar_errors(kwargs, pillar=None): - """ -- Checks all pillars (external and internal) for errors. -- Return an error message, if anywhere or None. -+ Check pillar for errors. -+ -+ If a pillar is passed, it will be checked. Otherwise, the in-memory pillar -+ will checked instead. Passing kwargs['force'] = True short cuts the check -+ and always returns None, indicating no errors. - - :param kwargs: dictionary of options -- :param pillar: external pillar -- :return: None or an error message -+ :param pillar: pillar -+ :return: None or a list of error messages - """ -- return ( -- None -- if kwargs.get("force") -- else (pillar or {}).get("_errors", __pillar__.get("_errors")) or None -- ) -+ return None if kwargs.get("force") else (pillar or __pillar__).get("_errors") - - - def _wait(jid): -diff --git a/tests/pytests/integration/modules/state/test_state_pillar_errors.py b/tests/pytests/integration/modules/state/test_state_pillar_errors.py -new file mode 100644 -index 0000000000..af65a05945 ---- /dev/null -+++ b/tests/pytests/integration/modules/state/test_state_pillar_errors.py -@@ -0,0 +1,131 @@ -+#!/usr/bin/python3 -+ -+import textwrap -+ -+import pytest -+from saltfactories.utils.functional import StateResult -+ -+pytestmark = [ -+ pytest.mark.slow_test, -+] -+ -+ -+@pytest.fixture(scope="module") -+def reset_pillar(salt_call_cli): -+ try: -+ # Run tests -+ yield -+ finally: -+ # Refresh pillar once all tests are done. -+ ret = salt_call_cli.run("saltutil.refresh_pillar", wait=True) -+ assert ret.exitcode == 0 -+ assert ret.json is True -+ -+ -+@pytest.fixture -+def testfile_path(tmp_path, base_env_state_tree_root_dir): -+ testfile = tmp_path / "testfile" -+ sls_contents = textwrap.dedent( -+ """ -+ {}: -+ file: -+ - managed -+ - source: salt://testfile -+ - makedirs: true -+ """.format(testfile) -+ ) -+ with pytest.helpers.temp_file( -+ "sls-id-test.sls", sls_contents, base_env_state_tree_root_dir -+ ): -+ yield testfile -+ -+ -+@pytest.mark.usefixtures("testfile_path", "reset_pillar") -+def test_state_apply_aborts_on_pillar_error( -+ salt_cli, -+ salt_minion, -+ base_env_pillar_tree_root_dir, -+): -+ """ -+ Test state.apply with error in pillar. -+ """ -+ pillar_top_file = textwrap.dedent( -+ """ -+ base: -+ '{}': -+ - basic -+ """ -+ ).format(salt_minion.id) -+ basic_pillar_file = textwrap.dedent( -+ """ -+ syntax_error -+ """ -+ ) -+ -+ with pytest.helpers.temp_file( -+ "top.sls", pillar_top_file, base_env_pillar_tree_root_dir -+ ), pytest.helpers.temp_file( -+ "basic.sls", basic_pillar_file, base_env_pillar_tree_root_dir -+ ): -+ expected_comment = [ -+ "Pillar failed to render with the following messages:", -+ "SLS 'basic' does not render to a dictionary", -+ ] -+ shell_result = salt_cli.run( -+ "state.apply", "sls-id-test", minion_tgt=salt_minion.id -+ ) -+ assert shell_result.exitcode == 1 -+ assert shell_result.json == expected_comment -+ -+ -+@pytest.mark.usefixtures("testfile_path", "reset_pillar") -+def test_state_apply_continues_after_pillar_error_is_fixed( -+ salt_cli, -+ salt_minion, -+ base_env_pillar_tree_root_dir, -+): -+ """ -+ Test state.apply with error in pillar. -+ """ -+ pillar_top_file = textwrap.dedent( -+ """ -+ base: -+ '{}': -+ - basic -+ """.format(salt_minion.id) -+ ) -+ basic_pillar_file_error = textwrap.dedent( -+ """ -+ syntax_error -+ """ -+ ) -+ basic_pillar_file = textwrap.dedent( -+ """ -+ syntax_error: Fixed! -+ """ -+ ) -+ -+ # save pillar render error in minion's in-memory pillar -+ with pytest.helpers.temp_file( -+ "top.sls", pillar_top_file, base_env_pillar_tree_root_dir -+ ), pytest.helpers.temp_file( -+ "basic.sls", basic_pillar_file_error, base_env_pillar_tree_root_dir -+ ): -+ shell_result = salt_cli.run( -+ "saltutil.refresh_pillar", minion_tgt=salt_minion.id -+ ) -+ assert shell_result.exitcode == 0 -+ -+ # run state.apply with fixed pillar render error -+ with pytest.helpers.temp_file( -+ "top.sls", pillar_top_file, base_env_pillar_tree_root_dir -+ ), pytest.helpers.temp_file( -+ "basic.sls", basic_pillar_file, base_env_pillar_tree_root_dir -+ ): -+ shell_result = salt_cli.run( -+ "state.apply", "sls-id-test", minion_tgt=salt_minion.id -+ ) -+ assert shell_result.exitcode == 0 -+ state_result = StateResult(shell_result.json) -+ assert state_result.result is True -+ assert state_result.changes == {"diff": "New file", "mode": "0644"} -diff --git a/tests/pytests/unit/modules/state/test_state.py b/tests/pytests/unit/modules/state/test_state.py -index 02fd2dd307..30cda303cc 100644 ---- a/tests/pytests/unit/modules/state/test_state.py -+++ b/tests/pytests/unit/modules/state/test_state.py -@@ -1,14 +1,16 @@ - """ - :codeauthor: Rahul Handay - """ -- - import datetime - import logging - import os -+from collections import namedtuple - - import pytest -+ - import salt.config - import salt.loader -+import salt.loader.context - import salt.modules.config as config - import salt.modules.state as state - import salt.state -@@ -1200,85 +1202,6 @@ def test_lock_saltenv(): - ) - - --def test_get_pillar_errors_CC(): -- """ -- Test _get_pillar_errors function. -- CC: External clean, Internal clean -- :return: -- """ -- for int_pillar, ext_pillar in [ -- ({"foo": "bar"}, {"fred": "baz"}), -- ({"foo": "bar"}, None), -- ({}, {"fred": "baz"}), -- ]: -- with patch("salt.modules.state.__pillar__", int_pillar): -- for opts, res in [ -- ({"force": True}, None), -- ({"force": False}, None), -- ({}, None), -- ]: -- assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) -- -- --def test_get_pillar_errors_EC(): -- """ -- Test _get_pillar_errors function. -- EC: External erroneous, Internal clean -- :return: -- """ -- errors = ["failure", "everywhere"] -- for int_pillar, ext_pillar in [ -- ({"foo": "bar"}, {"fred": "baz", "_errors": errors}), -- ({}, {"fred": "baz", "_errors": errors}), -- ]: -- with patch("salt.modules.state.__pillar__", int_pillar): -- for opts, res in [ -- ({"force": True}, None), -- ({"force": False}, errors), -- ({}, errors), -- ]: -- assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) -- -- --def test_get_pillar_errors_EE(): -- """ -- Test _get_pillar_errors function. -- CC: External erroneous, Internal erroneous -- :return: -- """ -- errors = ["failure", "everywhere"] -- for int_pillar, ext_pillar in [ -- ({"foo": "bar", "_errors": errors}, {"fred": "baz", "_errors": errors}) -- ]: -- with patch("salt.modules.state.__pillar__", int_pillar): -- for opts, res in [ -- ({"force": True}, None), -- ({"force": False}, errors), -- ({}, errors), -- ]: -- assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) -- -- --def test_get_pillar_errors_CE(): -- """ -- Test _get_pillar_errors function. -- CC: External clean, Internal erroneous -- :return: -- """ -- errors = ["failure", "everywhere"] -- for int_pillar, ext_pillar in [ -- ({"foo": "bar", "_errors": errors}, {"fred": "baz"}), -- ({"foo": "bar", "_errors": errors}, None), -- ]: -- with patch("salt.modules.state.__pillar__", int_pillar): -- for opts, res in [ -- ({"force": True}, None), -- ({"force": False}, errors), -- ({}, errors), -- ]: -- assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) -- -- - def test_event(): - """ - test state.event runner -@@ -1318,3 +1241,35 @@ def test_event(): - if _expected in x.args[0]: - found = True - assert found is True -+ -+ -+PillarPair = namedtuple("PillarPair", ["in_memory", "fresh"]) -+pillar_combinations = [ -+ (PillarPair({"foo": "bar"}, {"fred": "baz"}), None), -+ (PillarPair({"foo": "bar"}, {"fred": "baz", "_errors": ["Failure"]}), ["Failure"]), -+ (PillarPair({"foo": "bar"}, None), None), -+ (PillarPair({"foo": "bar", "_errors": ["Failure"]}, None), ["Failure"]), -+ (PillarPair({"foo": "bar", "_errors": ["Failure"]}, {"fred": "baz"}), None), -+] -+ -+ -+@pytest.mark.parametrize("pillar,expected_errors", pillar_combinations) -+def test_get_pillar_errors(pillar: PillarPair, expected_errors): -+ """ -+ test _get_pillar_errors function -+ -+ There are three cases to consider: -+ 1. kwargs['force'] is True -> None, no matter what's in pillar/__pillar__ -+ 2. pillar kwarg is available -> only check pillar, no matter what's in __pillar__ -+ 3. pillar kwarg is not available -> check __pillar__ -+ """ -+ ctx = salt.loader.context.LoaderContext() -+ named_ctx = ctx.named_context("__pillar__", pillar.in_memory) -+ with patch("salt.modules.state.__pillar__", named_ctx, create=True): -+ assert ( -+ state._get_pillar_errors(kwargs={"force": True}, pillar=pillar.fresh) -+ is None -+ ) -+ assert ( -+ state._get_pillar_errors(kwargs={}, pillar=pillar.fresh) == expected_errors -+ ) --- -2.37.3 - - diff --git a/state.orchestrate_single-does-not-pass-pillar-none-4.patch b/state.orchestrate_single-does-not-pass-pillar-none-4.patch deleted file mode 100644 index 511ee81..0000000 --- a/state.orchestrate_single-does-not-pass-pillar-none-4.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 634e82874b17c38bd4d27c0c07a53c9e39e49968 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= - -Date: Wed, 9 Feb 2022 09:01:08 +0000 -Subject: [PATCH] state.orchestrate_single does not pass pillar=None - (#488) - -Passing a pillar to state.single can results in state functions not -working when they don't accept a pillar keyword argument. One example -where this is the case is salt.wait_for_event. When no pillar is -provided, it does not need to be passed. - -Co-authored-by: Alexander Graul ---- - changelog/61092.fixed | 3 ++ - salt/runners/state.py | 10 ++++-- - tests/pytests/unit/runners/test_state.py | 41 ++++++++++++++++++++++++ - 3 files changed, 51 insertions(+), 3 deletions(-) - create mode 100644 changelog/61092.fixed - create mode 100644 tests/pytests/unit/runners/test_state.py - -diff --git a/changelog/61092.fixed b/changelog/61092.fixed -new file mode 100644 -index 0000000000..6ca66839c9 ---- /dev/null -+++ b/changelog/61092.fixed -@@ -0,0 +1,3 @@ -+state.orchestrate_single only passes a pillar if it is set to the state -+function. This allows it to be used with state functions that don't accept a -+pillar keyword argument. -diff --git a/salt/runners/state.py b/salt/runners/state.py -index f8fc1b0944..5642204ce9 100644 ---- a/salt/runners/state.py -+++ b/salt/runners/state.py -@@ -150,12 +150,16 @@ def orchestrate_single(fun, name, test=None, queue=False, pillar=None, **kwargs) - - salt-run state.orchestrate_single fun=salt.wheel name=key.list_all - """ -- if pillar is not None and not isinstance(pillar, dict): -- raise SaltInvocationError("Pillar data must be formatted as a dictionary") -+ if pillar is not None: -+ if isinstance(pillar, dict): -+ kwargs["pillar"] = pillar -+ else: -+ raise SaltInvocationError("Pillar data must be formatted as a dictionary") -+ - __opts__["file_client"] = "local" - minion = salt.minion.MasterMinion(__opts__) - running = minion.functions["state.single"]( -- fun, name, test=None, queue=False, pillar=pillar, **kwargs -+ fun, name, test=None, queue=False, **kwargs - ) - ret = {minion.opts["id"]: running} - __jid_event__.fire_event({"data": ret, "outputter": "highstate"}, "progress") -diff --git a/tests/pytests/unit/runners/test_state.py b/tests/pytests/unit/runners/test_state.py -new file mode 100644 -index 0000000000..df0a718a41 ---- /dev/null -+++ b/tests/pytests/unit/runners/test_state.py -@@ -0,0 +1,41 @@ -+#!/usr/bin/python3 -+ -+import pytest -+from salt.runners import state as state_runner -+from tests.support.mock import Mock, patch -+ -+ -+@pytest.fixture -+def configure_loader_modules(): -+ return {state_runner: {"__opts__": {}, "__jid_event__": Mock()}} -+ -+ -+def test_orchestrate_single_passes_pillar(): -+ """ -+ test state.orchestrate_single passes given pillar to state.single -+ """ -+ mock_master_minion = Mock() -+ mock_state_single = Mock() -+ mock_master_minion.functions = {"state.single": mock_state_single} -+ mock_master_minion.opts = {"id": "dummy"} -+ test_pillar = {"test_entry": "exists"} -+ with patch("salt.minion.MasterMinion", Mock(return_value=mock_master_minion)): -+ state_runner.orchestrate_single( -+ fun="pillar.get", name="test_entry", pillar=test_pillar -+ ) -+ assert mock_state_single.call_args.kwargs["pillar"] == test_pillar -+ -+ -+def test_orchestrate_single_does_not_pass_none_pillar(): -+ """ -+ test state.orchestrate_single does not pass pillar=None to state.single -+ """ -+ mock_master_minion = Mock() -+ mock_state_single = Mock() -+ mock_master_minion.functions = {"state.single": mock_state_single} -+ mock_master_minion.opts = {"id": "dummy"} -+ with patch("salt.minion.MasterMinion", Mock(return_value=mock_master_minion)): -+ state_runner.orchestrate_single( -+ fun="pillar.get", name="test_entry", pillar=None -+ ) -+ assert "pillar" not in mock_state_single.call_args.kwargs --- -2.37.3 - -